rendering topic images

This commit is contained in:
Roland Osborne 2022-05-02 00:49:11 -07:00
parent ed89df3235
commit 80c0b0c97d
10 changed files with 139 additions and 35 deletions

View File

@ -238,6 +238,7 @@ type Topic struct {
Created int64 `gorm:"autoCreateTime"` Created int64 `gorm:"autoCreateTime"`
Updated int64 `gorm:"autoUpdateTime"` Updated int64 `gorm:"autoUpdateTime"`
TagRevision int64 `gorm:"not null"` TagRevision int64 `gorm:"not null"`
Account Account
Channel *Channel Channel *Channel
Assets []Asset Assets []Asset
Tags []Tag Tags []Tag

View File

@ -1,7 +1,6 @@
package databag package databag
import ( import (
"time"
"os" "os"
"io" "io"
"hash/crc32" "hash/crc32"
@ -106,7 +105,6 @@ func transcodeAsset(asset *store.Asset) {
cmd.Stdout = &stdout cmd.Stdout = &stdout
var stderr bytes.Buffer var stderr bytes.Buffer
cmd.Stderr = &stderr cmd.Stderr = &stderr
time.Sleep(time.Second);
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
LogMsg(stdout.String()) LogMsg(stdout.String())
@ -138,7 +136,7 @@ time.Sleep(time.Second);
func UpdateAsset(asset *store.Asset, status string, crc uint32, size int64) (err error) { func UpdateAsset(asset *store.Asset, status string, crc uint32, size int64) (err error) {
act := asset.Account topic := store.Topic{};
err = store.DB.Transaction(func(tx *gorm.DB) error { err = store.DB.Transaction(func(tx *gorm.DB) error {
asset.Crc = crc asset.Crc = crc
asset.Size = size asset.Size = size
@ -146,19 +144,22 @@ func UpdateAsset(asset *store.Asset, status string, crc uint32, size int64) (err
if res := tx.Model(store.Asset{}).Where("id = ?", asset.ID).Updates(asset).Error; res != nil { if res := tx.Model(store.Asset{}).Where("id = ?", asset.ID).Updates(asset).Error; res != nil {
return res return res
} }
if res := tx.Model(&asset.Topic).Update("detail_revision", act.ChannelRevision + 1).Error; res != nil { if res := tx.Preload("Account").Preload("TopicSlot").Preload("Channel.ChannelSlot").First(&topic, asset.Topic.ID).Error; res != nil {
return res;
}
if res := tx.Model(&topic).Update("detail_revision", topic.Account.ChannelRevision + 1).Error; res != nil {
return res return res
} }
if res := tx.Model(&asset.Topic.TopicSlot).Update("revision", act.ChannelRevision + 1).Error; res != nil { if res := tx.Model(&topic.TopicSlot).Update("revision", topic.Account.ChannelRevision + 1).Error; res != nil {
return res return res
} }
if res := tx.Model(&asset.Channel).Update("topic_revision", act.ChannelRevision + 1).Error; res != nil { if res := tx.Model(&topic.Channel).Update("topic_revision", topic.Account.ChannelRevision + 1).Error; res != nil {
return res return res
} }
if res := tx.Model(&asset.Channel.ChannelSlot).Update("revision", act.ChannelRevision + 1).Error; res != nil { if res := tx.Model(&topic.Channel.ChannelSlot).Update("revision", topic.Account.ChannelRevision + 1).Error; res != nil {
return res return res
} }
if res := tx.Model(&act).Update("channel_revision", act.ChannelRevision + 1).Error; res != nil { if res := tx.Model(&topic.Account).Update("channel_revision", topic.Account.ChannelRevision + 1).Error; res != nil {
return res return res
} }
return nil return nil
@ -169,19 +170,19 @@ func UpdateAsset(asset *store.Asset, status string, crc uint32, size int64) (err
// determine affected contact list // determine affected contact list
cards := make(map[string]store.Card) cards := make(map[string]store.Card)
for _, card := range asset.Channel.Cards { for _, card := range topic.Channel.Cards {
cards[card.Guid] = card cards[card.Guid] = card
} }
for _, group := range asset.Channel.Groups { for _, group := range topic.Channel.Groups {
for _, card := range group.Cards { for _, card := range group.Cards {
cards[card.Guid] = card cards[card.Guid] = card
} }
} }
// notify // notify
SetStatus(&act) SetStatus(&topic.Account)
for _, card := range cards { for _, card := range cards {
SetContactChannelNotification(&act, &card) SetContactChannelNotification(&topic.Account, &card)
} }
return return

View File

@ -4,6 +4,7 @@ import ReactResizeDetector from 'react-resize-detector';
import { useTopicItem } from './useTopicItem.hook'; import { useTopicItem } from './useTopicItem.hook';
import { Avatar } from 'avatar/Avatar'; import { Avatar } from 'avatar/Avatar';
import { CommentOutlined } from '@ant-design/icons'; import { CommentOutlined } from '@ant-design/icons';
import { Carousel } from 'Carousel/Carousel';
export function TopicItem({ topic }) { export function TopicItem({ topic }) {
@ -14,6 +15,13 @@ export function TopicItem({ topic }) {
let d = new Date(); let d = new Date();
let offset = d.getTime() / 1000 - state.created; let offset = d.getTime() / 1000 - state.created;
const renderAsset = (asset) => {
if (asset.image) {
return <img style={{ height: '100%', objectFit: 'container' }} src={actions.getAssetUrl(asset.image.full)} alt="" />
}
return <></>
}
return ( return (
<TopicItemWrapper> <TopicItemWrapper>
<div class="avatar"> <div class="avatar">
@ -27,7 +35,8 @@ export function TopicItem({ topic }) {
<CommentOutlined /> <CommentOutlined />
</div> </div>
</div> </div>
<div class="message">{ state.message }</div> <Carousel items={state.assets} itemRenderer={renderAsset} />
<div class="message">{ state.message?.text }</div>
</div> </div>
</TopicItemWrapper> </TopicItemWrapper>
) )

View File

@ -13,6 +13,9 @@ export function useTopicItem(topic) {
imageUrl: null, imageUrl: null,
message: null, message: null,
created: null, created: null,
status: null,
transform: null,
assets: [],
}); });
const profile = useContext(ProfileContext); const profile = useContext(ProfileContext);
@ -24,10 +27,19 @@ export function useTopicItem(topic) {
} }
useEffect(() => { useEffect(() => {
const { status, transform, data } = topic.data.topicDetail;
let message; let message;
if( topic.data.topicDetail.status === 'confirmed') { let assets = [];
if (status === 'confirmed') {
try { try {
message = JSON.parse(topic.data.topicDetail.data).text; message = JSON.parse(data);
if (transform === 'complete') {
if (message.assets) {
assets = message.assets;
delete message.assets;
}
}
} }
catch(err) { catch(err) {
console.log(err); console.log(err);
@ -38,16 +50,19 @@ export function useTopicItem(topic) {
const { guid, created } = topic.data.topicDetail; const { guid, created } = topic.data.topicDetail;
if (profile.state.profile.guid == guid) { if (profile.state.profile.guid == guid) {
const { name, handle, imageUrl } = profile.actions.getProfile(); const { name, handle, imageUrl } = profile.actions.getProfile();
updateState({ name, handle, imageUrl, message, created }); updateState({ name, handle, imageUrl, status, message, transform, assets, created });
} }
else { else {
const { name, handle, imageUrl } = card.actions.getCardProfileByGuid(guid); const { name, handle, imageUrl } = card.actions.getCardProfileByGuid(guid);
updateState({ name, handle, imageUrl, message, created }); updateState({ name, handle, imageUrl, status, message, transform, assets, created });
} }
} }
}, [profile, card, conversation, topic]); }, [profile, card, conversation, topic]);
const actions = { const actions = {
getAssetUrl: (assetId) => {
return conversation.actions.getAssetUrl(topic.id, assetId);
}
}; };
return { state, actions }; return { state, actions };

View File

@ -2,30 +2,71 @@ import { checkResponse, fetchWithTimeout } from './fetchUtil';
export async function addChannelTopic(token, channelId, message, assets ) { export async function addChannelTopic(token, channelId, message, assets ) {
if (assets == null || assets.length == 0) {
let subject = { data: JSON.stringify(message, (key, value) => { let subject = { data: JSON.stringify(message, (key, value) => {
if (value !== null) return value if (value !== null) return value
}), datatype: 'superbasictopic' }; }), datatype: 'superbasictopic' };
if (assets == null || assets.length == 0) {
let topic = await fetchWithTimeout(`/content/channels/${channelId}/topics?agent=${token}&confirm=true`, let topic = await fetchWithTimeout(`/content/channels/${channelId}/topics?agent=${token}&confirm=true`,
{ method: 'POST', body: JSON.stringify(subject) }); { method: 'POST', body: JSON.stringify(subject) });
checkResponse(topic); checkResponse(topic);
} }
else { else {
let topic = await fetchWithTimeout(`/content/channels/${channelId}/topics?agent=${token}`, let topic = await fetchWithTimeout(`/content/channels/${channelId}/topics?agent=${token}`,
{ method: 'POST', body: JSON.stringify({}) }); { method: 'POST', body: JSON.stringify({}) });
checkResponse(topic); checkResponse(topic);
let slot = await topic.json(); let slot = await topic.json();
// add each asset // add each asset
message.assets = [];
for (let asset of assets) { for (let asset of assets) {
if (asset.image) {
const formData = new FormData(); const formData = new FormData();
formData.append('asset', asset.image); formData.append('asset', asset.image);
let transform = encodeURIComponent(JSON.stringify(["ithumb;photo"])); let transform = encodeURIComponent(JSON.stringify(["ithumb;photo", "icopy;photo"]));
let topicAsset = await fetch(`/content/channels/${channelId}/topics/${slot.id}/assets?transforms=${transform}&agent=${token}`, { method: 'POST', body: formData }); let topicAsset = await fetch(`/content/channels/${channelId}/topics/${slot.id}/assets?transforms=${transform}&agent=${token}`, { method: 'POST', body: formData });
checkResponse(topicAsset); checkResponse(topicAsset);
console.log(await topicAsset.json()); let assetEntry = await topicAsset.json();
message.assets.push({
image: {
thumb: assetEntry.find(item => item.transform === 'ithumb;photo').assetId,
full: assetEntry.find(item => item.transform === 'icopy;photo').assetId,
} }
});
}
else if (asset.video) {
const formData = new FormData();
formData.append('asset', asset.video);
let transform = encodeURIComponent(JSON.stringify(["vthumb;video", "vcopy;video"]));
let topicAsset = await fetch(`/content/channels/${channelId}/topics/${slot.id}/assets?transforms=${transform}&agent=${token}`, { method: 'POST', body: formData });
checkResponse(topicAsset);
let assetEntry = await topicAsset.json();
message.assets.push({
image: {
thumb: assetEntry.find(item => item.transform === 'vthumb;video').assetId,
full: assetEntry.find(item => item.transform === 'vcopy;video').assetId,
}
});
}
else if (asset.audio) {
const formData = new FormData();
formData.append('asset', asset.audio);
let transform = encodeURIComponent(JSON.stringify(["acopy;audio"]));
let topicAsset = await fetch(`/content/channels/${channelId}/topics/${slot.id}/assets?transforms=${transform}&agent=${token}`, { method: 'POST', body: formData });
checkResponse(topicAsset);
let assetEntry = await topicAsset.json();
message.assets.push({
image: {
full: assetEntry.find(item => item.transform === 'acopy;audio').assetId,
}
});
}
}
let subject = { data: JSON.stringify(message, (key, value) => {
if (value !== null) return value
}), datatype: 'superbasictopic' };
let unconfirmed = await fetchWithTimeout(`/content/channels/${channelId}/topics/${slot.id}/subject?agent=${token}`, let unconfirmed = await fetchWithTimeout(`/content/channels/${channelId}/topics/${slot.id}/subject?agent=${token}`,
{ method: 'PUT', body: JSON.stringify(subject) }); { method: 'PUT', body: JSON.stringify(subject) });

View File

@ -0,0 +1,4 @@
export function getChannelTopicAssetUrl(token, channelId, topicId, assetId) {
return `/content/channels/${channelId}/topics/${topicId}/assets/${assetId}?agent=${token}`
}

View File

@ -0,0 +1,4 @@
export function getContactChannelTopicAssetUrl(server, token, channelId, topicId, assetId) {
return `https://${server}/content/channels/${channelId}/topics/${topicId}/assets/${assetId}?contact=${token}`
}

View File

@ -15,6 +15,7 @@ import { getCardCloseMessage } from 'api/getCardCloseMessage';
import { setCardCloseMessage } from 'api/setCardCloseMessage'; import { setCardCloseMessage } from 'api/setCardCloseMessage';
import { getContactChannelTopics } from 'api/getContactChannelTopics'; import { getContactChannelTopics } from 'api/getContactChannelTopics';
import { getContactChannelTopic } from 'api/getContactChannelTopic'; import { getContactChannelTopic } from 'api/getContactChannelTopic';
import { getContactChannelTopicAssetUrl } from 'api/getContactChannelTopicAssetUrl';
import { addCard } from 'api/addCard'; import { addCard } from 'api/addCard';
import { removeCard } from 'api/removeCard'; import { removeCard } from 'api/removeCard';
@ -249,6 +250,12 @@ export function useCardContext() {
setCardCloseMessage: async (server, message) => { setCardCloseMessage: async (server, message) => {
return await setCardCloseMessage(server, message); return await setCardCloseMessage(server, message);
}, },
getContactChannelTopicAssetUrl: (cardId, channelId, topicId, assetId) => {
let card = cards.current.get(cardId);
let node = card.data.cardProfile.node;
let token = card.data.cardProfile.guid + "." + card.data.cardDetail.token;
return getContactChannelTopicAssetUrl(node, token, channelId, topicId, assetId);
}
} }
return { state, actions } return { state, actions }

View File

@ -5,6 +5,7 @@ import { addChannel } from 'api/addChannel';
import { addChannelTopic } from 'api/addChannelTopic'; import { addChannelTopic } from 'api/addChannelTopic';
import { getChannelTopics } from 'api/getChannelTopics'; import { getChannelTopics } from 'api/getChannelTopics';
import { getChannelTopic } from 'api/getChannelTopic'; import { getChannelTopic } from 'api/getChannelTopic';
import { getChannelTopicAssetUrl } from 'api/getChannelTopicAssetUrl';
export function useChannelContext() { export function useChannelContext() {
const [state, setState] = useState({ const [state, setState] = useState({
@ -91,6 +92,9 @@ export function useChannelContext() {
getChannelTopic: async (channelId, topicId) => { getChannelTopic: async (channelId, topicId) => {
return await getChannelTopic(access.current, channelId, topicId); return await getChannelTopic(access.current, channelId, topicId);
}, },
getChannelTopicAssetUrl: (channelId, topicId, assetId) => {
return getChannelTopicAssetUrl(access.current, channelId, topicId, assetId);
}
} }
return { state, actions } return { state, actions }

View File

@ -13,6 +13,7 @@ export function useConversationContext() {
const channel = useContext(ChannelContext); const channel = useContext(ChannelContext);
const topics = useRef(new Map()); const topics = useRef(new Map());
const revision = useRef(null); const revision = useRef(null);
const count = useRef(0);
const conversationId = useRef(null); const conversationId = useRef(null);
const updateState = (value) => { const updateState = (value) => {
@ -26,7 +27,6 @@ export function useConversationContext() {
let rev = card.actions.getChannelRevision(cardId, channelId); let rev = card.actions.getChannelRevision(cardId, channelId);
if (revision.current != rev) { if (revision.current != rev) {
let delta = await card.actions.getChannelTopics(cardId, channelId, revision.current); let delta = await card.actions.getChannelTopics(cardId, channelId, revision.current);
console.log(delta);
for (let topic of delta) { for (let topic of delta) {
if (topic.data == null) { if (topic.data == null) {
topics.current.delete(topic.id); topics.current.delete(topic.id);
@ -37,7 +37,7 @@ export function useConversationContext() {
cur = { id: topic.id, data: {} }; cur = { id: topic.id, data: {} };
} }
if (topic.data.detailRevision != cur.data.detailRevision) { if (topic.data.detailRevision != cur.data.detailRevision) {
if(topic.data.topicDetail != null) { if(topic.data.topicDetail) {
cur.data.topicDetail = topic.data.topicDetail; cur.data.topicDetail = topic.data.topicDetail;
cur.data.detailRevision = topic.data.detailRevision; cur.data.detailRevision = topic.data.detailRevision;
} }
@ -108,12 +108,21 @@ export function useConversationContext() {
return; return;
} }
if (count.current == 0) {
count.current += 1;
while(count.current > 0) {
try { try {
setTopics(); await setTopics();
} }
catch (err) { catch (err) {
console.log(err); console.log(err);
} }
count.current -= 1;
}
}
else {
count.current += 1;
}
}; };
useEffect(() => { useEffect(() => {
@ -129,6 +138,15 @@ export function useConversationContext() {
topics.current = new Map(); topics.current = new Map();
updateState({ init: false, topics: topics.current }); updateState({ init: false, topics: topics.current });
updateConversation(); updateConversation();
},
getAssetUrl: (topicId, assetId) => {
const { cardId, channelId } = conversationId.current;
if (conversationId.current.cardId) {
return card.actions.getContactChannelTopicAssetUrl(cardId, channelId, topicId, assetId);
}
else {
return channel.actions.getChannelTopicAssetUrl(channelId, topicId, assetId);
}
} }
} }