mirror of
https://github.com/balzack/databag.git
synced 2025-02-14 12:39:17 +00:00
support deleting topics
This commit is contained in:
parent
04c965847b
commit
08517c4abd
@ -41,8 +41,8 @@ func RemoveChannelTopic(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// check permission
|
||||
if topicSlot.Topic.Guid != guid {
|
||||
ErrResponse(w, http.StatusUnauthorized, errors.New("not creator of topic"))
|
||||
if act.Guid != guid && topicSlot.Topic.Guid != guid {
|
||||
ErrResponse(w, http.StatusUnauthorized, errors.New("not creator of topic or host"))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { useContext, useState, useEffect } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { addChannelTopic } from 'api/addChannelTopic';
|
||||
import { addContactChannelTopic } from 'api/addContactChannelTopic';
|
||||
import { CardContext } from 'context/CardContext';
|
||||
import { ChannelContext } from 'context/ChannelContext';
|
||||
|
||||
|
@ -28,7 +28,7 @@ export function Conversation() {
|
||||
}, [state]);
|
||||
|
||||
const topicRenderer = (topic) => {
|
||||
return (<TopicItem topic={topic} />)
|
||||
return (<TopicItem host={state.cardId == null} topic={topic} />)
|
||||
}
|
||||
|
||||
const onSaveSubject = () => {
|
||||
|
@ -5,10 +5,11 @@ import { VideoAsset } from './VideoAsset/VideoAsset';
|
||||
import { AudioAsset } from './AudioAsset/AudioAsset';
|
||||
import { ImageAsset } from './ImageAsset/ImageAsset';
|
||||
import { Avatar } from 'avatar/Avatar';
|
||||
import { CommentOutlined } from '@ant-design/icons';
|
||||
import { Button } from 'antd';
|
||||
import { DeleteOutlined, EditOutlined } from '@ant-design/icons';
|
||||
import { Carousel } from 'Carousel/Carousel';
|
||||
|
||||
export function TopicItem({ topic }) {
|
||||
export function TopicItem({ host, topic }) {
|
||||
|
||||
const { state, actions } = useTopicItem(topic);
|
||||
|
||||
@ -37,6 +38,39 @@ export function TopicItem({ topic }) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
const onEdit = () => {
|
||||
console.log("EDIT TOPIC");
|
||||
}
|
||||
|
||||
const onDelete = () => {
|
||||
console.log("DELETE TOPIC");
|
||||
}
|
||||
|
||||
const Options = () => {
|
||||
if (state.owner) {
|
||||
return (
|
||||
<div class="buttons">
|
||||
<div class="button" onClick={() => onEdit()}>
|
||||
<EditOutlined />
|
||||
</div>
|
||||
<div class="button" onClick={() => actions.removeTopic()}>
|
||||
<DeleteOutlined />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (host) {
|
||||
return (
|
||||
<div class="buttons">
|
||||
<div class="button" onClick={() => actions.removeTopic()}>
|
||||
<DeleteOutlined />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<TopicItemWrapper>
|
||||
<div class="avatar">
|
||||
@ -49,6 +83,9 @@ export function TopicItem({ topic }) {
|
||||
</div>
|
||||
<Carousel ready={state.ready} items={state.assets} itemRenderer={renderAsset} />
|
||||
<div class="message">{ state.message?.text }</div>
|
||||
<div class="options">
|
||||
<Options />
|
||||
</div>
|
||||
</div>
|
||||
</TopicItemWrapper>
|
||||
)
|
||||
|
@ -15,6 +15,34 @@ export const TopicItemWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-left: 8px;
|
||||
flex-grow: 1;
|
||||
|
||||
&:hover .options {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.options {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
visibility: hidden;
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
border-radius: 4px;
|
||||
background-color: #eeeeee;
|
||||
border: 1px solid #555555;
|
||||
margin-top: 2px;
|
||||
|
||||
.button {
|
||||
font-size: 14px;
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
|
@ -14,6 +14,7 @@ export function useTopicItem(topic) {
|
||||
message: null,
|
||||
created: null,
|
||||
ready: false,
|
||||
owner: false,
|
||||
assets: [],
|
||||
});
|
||||
|
||||
@ -26,6 +27,10 @@ export function useTopicItem(topic) {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
let owner = false;
|
||||
if (profile.state.profile.guid == topic?.data?.topicDetail.guid) {
|
||||
owner = true;
|
||||
}
|
||||
|
||||
if (!topic?.data) {
|
||||
console.log("invalid topic:", topic);
|
||||
@ -56,11 +61,11 @@ 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, status, message, transform, assets, ready, created });
|
||||
updateState({ name, handle, imageUrl, status, message, transform, assets, ready, created, owner });
|
||||
}
|
||||
else {
|
||||
const { name, handle, imageUrl } = card.actions.getCardProfileByGuid(guid);
|
||||
updateState({ name, handle, imageUrl, status, message, transform, assets, ready, created });
|
||||
updateState({ name, handle, imageUrl, status, message, transform, assets, ready, created, owner });
|
||||
}
|
||||
}
|
||||
}, [profile, card, conversation, topic]);
|
||||
@ -68,6 +73,9 @@ export function useTopicItem(topic) {
|
||||
const actions = {
|
||||
getAssetUrl: (assetId) => {
|
||||
return conversation.actions.getAssetUrl(topic?.id, assetId);
|
||||
},
|
||||
removeTopic: async () => {
|
||||
return conversation.actions.removeTopic(topic.id);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -22,6 +22,7 @@ export function VirtualList({ id, items, itemRenderer }) {
|
||||
let containers = useRef([]);
|
||||
let listRef = useRef();
|
||||
let key = useRef(null);
|
||||
let itemView = useRef([]);
|
||||
|
||||
const addSlot = (id, slot) => {
|
||||
setSlots((m) => { m.set(id, slot); return new Map(m); })
|
||||
@ -59,6 +60,7 @@ export function VirtualList({ id, items, itemRenderer }) {
|
||||
containers.current = [];
|
||||
clearSlots();
|
||||
}
|
||||
itemView.current = items;
|
||||
setItems();
|
||||
}, [items, id]);
|
||||
|
||||
@ -87,7 +89,7 @@ export function VirtualList({ id, items, itemRenderer }) {
|
||||
|
||||
const limitScroll = () => {
|
||||
let view = getPlacement();
|
||||
if (view && containers.current[containers.current.length - 1].index == items.length - 1) {
|
||||
if (view && containers.current[containers.current.length - 1].index == itemView.current.length - 1) {
|
||||
if (view?.overscan?.bottom <= 0) {
|
||||
if (view.position.height < viewHeight.current) {
|
||||
if (scrollTop.current != view.position.top) {
|
||||
@ -116,14 +118,14 @@ export function VirtualList({ id, items, itemRenderer }) {
|
||||
let view = getPlacement();
|
||||
if (view) {
|
||||
if (view.overscan.top < OVERSCAN) {
|
||||
if (containers.current[0].index > 0 && containers.current[0].index < items.length) {
|
||||
if (containers.current[0].index > 0 && containers.current[0].index < itemView.current.length) {
|
||||
let below = containers.current[0];
|
||||
let container = {
|
||||
top: below.top - (DEFAULT_ITEM_HEIGHT + 2 * GUTTER),
|
||||
height: DEFAULT_ITEM_HEIGHT,
|
||||
index: containers.current[0].index - 1,
|
||||
id: items[containers.current[0].index - 1].id,
|
||||
revision: items[containers.current[0].index - 1].revision,
|
||||
id: itemView.current[containers.current[0].index - 1].id,
|
||||
revision: itemView.current[containers.current[0].index - 1].revision,
|
||||
}
|
||||
containers.current.unshift(container);
|
||||
addSlot(container.id, getSlot(container))
|
||||
@ -131,14 +133,14 @@ export function VirtualList({ id, items, itemRenderer }) {
|
||||
}
|
||||
}
|
||||
if (view.overscan.bottom < OVERSCAN) {
|
||||
if (containers.current[containers.current.length - 1].index + 1 < items.length) {
|
||||
if (containers.current[containers.current.length - 1].index + 1 < itemView.current.length) {
|
||||
let above = containers.current[containers.current.length - 1];
|
||||
let container = {
|
||||
top: above.top + above.height + 2 * GUTTER,
|
||||
height: DEFAULT_ITEM_HEIGHT,
|
||||
index: containers.current[containers.current.length - 1].index + 1,
|
||||
id: items[containers.current[containers.current.length - 1].index + 1].id,
|
||||
revision: items[containers.current[containers.current.length - 1].index + 1].revision,
|
||||
id: itemView.current[containers.current[containers.current.length - 1].index + 1].id,
|
||||
revision: itemView.current[containers.current[containers.current.length - 1].index + 1].revision,
|
||||
}
|
||||
containers.current.push(container);
|
||||
addSlot(container.id, getSlot(container))
|
||||
@ -239,21 +241,21 @@ export function VirtualList({ id, items, itemRenderer }) {
|
||||
|
||||
for (let i = 0; i < containers.current.length; i++) {
|
||||
let container = containers.current[i];
|
||||
if (items.length <= container.index || items[container.index].id != container.id) {
|
||||
for (let j = i; j < containers.current.length; j++) {
|
||||
if (itemView.current.length <= container.index || itemView.current[container.index].id != container.id) {
|
||||
while (containers.current.length > i) {
|
||||
let popped = containers.current.pop();
|
||||
removeSlot(popped.id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (items[container.index].revision != container.revision) {
|
||||
else if (itemView.current[container.index].revision != container.revision) {
|
||||
updateSlot(container.id, getSlot(containers.current[i]));
|
||||
containers.revision = items[container.index].revision;
|
||||
containers.revision = itemView.current[container.index].revision;
|
||||
}
|
||||
}
|
||||
|
||||
// place first slot
|
||||
if (items.length > 0 && canvasHeight > 0) {
|
||||
if (itemView.current.length > 0 && canvasHeight > 0) {
|
||||
let view = getPlacement();
|
||||
if (!view) {
|
||||
let pos = canvasHeight / 2;
|
||||
@ -263,9 +265,9 @@ export function VirtualList({ id, items, itemRenderer }) {
|
||||
let container = {
|
||||
top: pos - DEFAULT_ITEM_HEIGHT,
|
||||
height: DEFAULT_ITEM_HEIGHT,
|
||||
index: items.length - 1,
|
||||
id: items[items.length - 1].id,
|
||||
revision: items[items.length - 1].revision,
|
||||
index: itemView.current.length - 1,
|
||||
id: itemView.current[itemView.current.length - 1].id,
|
||||
revision: itemView.current[itemView.current.length - 1].revision,
|
||||
}
|
||||
|
||||
containers.current.push(container);
|
||||
@ -289,7 +291,7 @@ export function VirtualList({ id, items, itemRenderer }) {
|
||||
if (typeof height !== 'undefined') {
|
||||
onItemHeight(container, height);
|
||||
}
|
||||
return itemRenderer(items[container.index]);
|
||||
return itemRenderer(itemView.current[container.index]);
|
||||
}}
|
||||
</ReactResizeDetector>
|
||||
</VirtualItem>
|
||||
|
8
net/web/src/api/removeChannelTopic.js
Normal file
8
net/web/src/api/removeChannelTopic.js
Normal file
@ -0,0 +1,8 @@
|
||||
import { checkResponse, fetchWithTimeout } from './fetchUtil';
|
||||
|
||||
export async function removeChannelTopic(token, channelId, topicId) {
|
||||
|
||||
let channel = await fetchWithTimeout(`/content/channels/${channelId}/topics/${topicId}?agent=${token}`,
|
||||
{ method: 'DELETE' });
|
||||
checkResponse(channel);
|
||||
}
|
8
net/web/src/api/removeContactChannelTopic.js
Normal file
8
net/web/src/api/removeContactChannelTopic.js
Normal file
@ -0,0 +1,8 @@
|
||||
import { checkResponse, fetchWithTimeout } from './fetchUtil';
|
||||
|
||||
export async function removeContactChannelTopic(server, token, channelId, topicId) {
|
||||
|
||||
let channel = await fetchWithTimeout(`https://${server}//content/channels/${channelId}/topics/${topicId}?contact=${token}`,
|
||||
{ method: 'DELETE' });
|
||||
checkResponse(channel);
|
||||
}
|
@ -9,6 +9,7 @@ import { getCardImageUrl } from 'api/getCardImageUrl';
|
||||
import { getCardProfile } from 'api/getCardProfile';
|
||||
import { getCardDetail } from 'api/getCardDetail';
|
||||
import { removeContactChannel } from 'api/removeContactChannel';
|
||||
import { removeContactChannelTopic } from 'api/removeContactChannelTopic';
|
||||
import { addContactChannelTopic } from 'api/addContactChannelTopic';
|
||||
import { setCardConnecting, setCardConnected, setCardConfirmed } from 'api/setCardStatus';
|
||||
import { getCardOpenMessage } from 'api/getCardOpenMessage';
|
||||
@ -220,6 +221,12 @@ export function useCardContext() {
|
||||
let node = cardProfile.node;
|
||||
await removeContactChannel(node, token, channelId);
|
||||
},
|
||||
removeChannelTopic: async (cardId, channelId, topicId) => {
|
||||
let { cardProfile, cardDetail } = cards.current.get(cardId).data;
|
||||
let token = cardProfile.guid + '.' + cardDetail.token;
|
||||
let node = cardProfile.node;
|
||||
await removeContactChannelTopic(node, token, channelId, topicId);
|
||||
},
|
||||
addChannelTopic: async (cardId, channelId, message, assets) => {
|
||||
let { cardProfile, cardDetail } = cards.current.get(cardId).data;
|
||||
let token = cardProfile.guid + '.' + cardDetail.token;
|
||||
|
@ -4,6 +4,7 @@ import { getChannelDetail } from 'api/getChannelDetail';
|
||||
import { getChannelSummary } from 'api/getChannelSummary';
|
||||
import { addChannel } from 'api/addChannel';
|
||||
import { removeChannel } from 'api/removeChannel';
|
||||
import { removeChannelTopic } from 'api/removeChannelTopic';
|
||||
import { addChannelTopic } from 'api/addChannelTopic';
|
||||
import { getChannelTopics } from 'api/getChannelTopics';
|
||||
import { getChannelTopic } from 'api/getChannelTopic';
|
||||
@ -109,6 +110,9 @@ export function useChannelContext() {
|
||||
removeChannel: async (channelId) => {
|
||||
return await removeChannel(access.current, channelId);
|
||||
},
|
||||
removeChannelTopic: async (channelId, topicId) => {
|
||||
return await removeChannelTopic(access.current, channelId, topicId);
|
||||
},
|
||||
addChannelTopic: async (channelId, message, assets) => {
|
||||
await addChannelTopic(access.current, channelId, message, assets);
|
||||
},
|
||||
|
@ -247,6 +247,15 @@ export function useConversationContext() {
|
||||
return await channel.actions.removeChannel(channelId);
|
||||
}
|
||||
},
|
||||
removeTopic: async (topicId) => {
|
||||
const { cardId, channelId } = conversationId.current;
|
||||
if (cardId) {
|
||||
return await card.actions.removeChannelTopic(cardId, channelId, topicId);
|
||||
}
|
||||
else {
|
||||
return await channel.actions.removeChannelTopic(channelId, topicId);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return { state, actions }
|
||||
|
Loading…
Reference in New Issue
Block a user