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"`
Updated int64 `gorm:"autoUpdateTime"`
TagRevision int64 `gorm:"not null"`
Account Account
Channel *Channel
Assets []Asset
Tags []Tag

View File

@ -1,7 +1,6 @@
package databag
import (
"time"
"os"
"io"
"hash/crc32"
@ -106,7 +105,6 @@ func transcodeAsset(asset *store.Asset) {
cmd.Stdout = &stdout
var stderr bytes.Buffer
cmd.Stderr = &stderr
time.Sleep(time.Second);
if err := cmd.Run(); err != nil {
LogMsg(stdout.String())
@ -138,7 +136,7 @@ time.Sleep(time.Second);
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 {
asset.Crc = crc
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 {
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
}
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
}
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
}
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
}
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 nil
@ -169,19 +170,19 @@ func UpdateAsset(asset *store.Asset, status string, crc uint32, size int64) (err
// determine affected contact list
cards := make(map[string]store.Card)
for _, card := range asset.Channel.Cards {
for _, card := range topic.Channel.Cards {
cards[card.Guid] = card
}
for _, group := range asset.Channel.Groups {
for _, group := range topic.Channel.Groups {
for _, card := range group.Cards {
cards[card.Guid] = card
}
}
// notify
SetStatus(&act)
SetStatus(&topic.Account)
for _, card := range cards {
SetContactChannelNotification(&act, &card)
SetContactChannelNotification(&topic.Account, &card)
}
return

View File

@ -4,6 +4,7 @@ import ReactResizeDetector from 'react-resize-detector';
import { useTopicItem } from './useTopicItem.hook';
import { Avatar } from 'avatar/Avatar';
import { CommentOutlined } from '@ant-design/icons';
import { Carousel } from 'Carousel/Carousel';
export function TopicItem({ topic }) {
@ -13,7 +14,14 @@ export function TopicItem({ topic }) {
let nameClass = state.name ? 'set' : 'unset';
let d = new Date();
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 (
<TopicItemWrapper>
<div class="avatar">
@ -27,7 +35,8 @@ export function TopicItem({ topic }) {
<CommentOutlined />
</div>
</div>
<div class="message">{ state.message }</div>
<Carousel items={state.assets} itemRenderer={renderAsset} />
<div class="message">{ state.message?.text }</div>
</div>
</TopicItemWrapper>
)

View File

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

View File

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

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 { getContactChannelTopics } from 'api/getContactChannelTopics';
import { getContactChannelTopic } from 'api/getContactChannelTopic';
import { getContactChannelTopicAssetUrl } from 'api/getContactChannelTopicAssetUrl';
import { addCard } from 'api/addCard';
import { removeCard } from 'api/removeCard';
@ -249,6 +250,12 @@ export function useCardContext() {
setCardCloseMessage: async (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 }

View File

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

View File

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