addTopics to self hosted channels

This commit is contained in:
Roland Osborne 2022-04-14 14:13:08 -07:00
parent 343aa21e3e
commit 6e2801d705
16 changed files with 125 additions and 39 deletions

View File

@ -2113,7 +2113,7 @@ paths:
schema:
$ref: '#/components/schemas/ChannelParams'
/content/channels/{channelId}:
/content/channels/{channelId}/detail:
get:
tags:
- content
@ -2143,6 +2143,8 @@ paths:
description: account disabled
'500':
description: internal server error
/content/channels/{channelId}:
delete:
tags:
- content
@ -2869,7 +2871,7 @@ paths:
content:
application/json:
schema:
type: boolean
type: string
/content/channels/{channelId}/topics/{topicId}/tags:

View File

@ -16,16 +16,16 @@ func GetChannel(w http.ResponseWriter, r *http.Request) {
var guid string
var act *store.Account
tokenType := r.Header.Get("TokenType")
tokenType := ParamTokenType(r)
if tokenType == APP_TOKENAGENT {
account, code, err := BearerAppToken(r, false);
account, code, err := ParamAgentToken(r, false);
if err != nil {
ErrResponse(w, code, err)
return
}
act = account
} else if tokenType == APP_TOKENCONTACT {
card, code, err := BearerContactToken(r, true)
card, code, err := ParamContactToken(r, true)
if err != nil {
ErrResponse(w, code, err)
return

View File

@ -62,16 +62,16 @@ func getChannelSlot(r *http.Request, member bool) (slot store.ChannelSlot, guid
// validate contact access
var account *store.Account
tokenType := r.Header.Get("TokenType")
tokenType := ParamTokenType(r);
if tokenType == APP_TOKENAGENT {
account, code, err = BearerAppToken(r, false);
account, code, err = ParamAgentToken(r, false);
if err != nil {
return
}
guid = account.Guid
} else if tokenType == APP_TOKENCONTACT {
var card *store.Card
card, code, err = BearerContactToken(r, true)
card, code, err = ParamContactToken(r, true)
if err != nil {
return
}

View File

@ -29,7 +29,7 @@ func SetChannelTopicSubject(w http.ResponseWriter, r *http.Request) {
// load topic
var topicSlot store.TopicSlot
if err = store.DB.Where("channel_id = ? AND topic_slot_id = ?", channelSlot.Channel.ID, topicId).First(&topicSlot).Error; err != nil {
if err = store.DB.Preload("Topic").Where("channel_id = ? AND topic_slot_id = ?", channelSlot.Channel.ID, topicId).First(&topicSlot).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
ErrResponse(w, http.StatusNotFound, err)
} else {

View File

@ -541,7 +541,7 @@ var routes = Routes{
Route{
"GetChannel",
strings.ToUpper("Get"),
"/content/channels/{channelId}",
"/content/channels/{channelId}/detail",
GetChannel,
},

View File

@ -196,18 +196,28 @@ func ApiTestUpload(
return
}
if tokenType == APP_TOKENAGENT {
if !strings.Contains(name, "?") {
name += "?"
} else {
name += "&"
}
name += "agent=" + token
} else if tokenType == APP_TOKENCONTACT {
if !strings.Contains(name, "?") {
name += "?"
} else {
name += "&"
}
name += "contact=" + token
}
w := httptest.NewRecorder()
r := httptest.NewRequest(requestType, name, &data)
if params != nil {
r = mux.SetURLVars(r, *params)
}
if tokenType != "" {
r.Header.Add("TokenType", tokenType)
}
if token != "" {
SetBearerAuth(r, token)
}
r.Header.Set("Content-Type", writer.FormDataContentType())
endpoint(w, r)

View File

@ -0,0 +1,24 @@
import { checkResponse, fetchWithTimeout } from './fetchUtil';
export async function addChannelTopic(token, channelId, message, assets ) {
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
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);
let confirmed = await fetchWithTimeout(`/content/channels/${channelId}/topics/${slot.id}/confirmed?agent=${token}`,
{ method: 'PUT', body: JSON.stringify('confirmed') });
checkResponse(confirmed);
return;
}

View File

@ -0,0 +1,8 @@
import { checkResponse, fetchWithTimeout } from './fetchUtil';
export async function getChannel(token, channelId) {
let channel = await fetchWithTimeout(`/content/channels/${channelId}/detail?agent=${token}`, { method: 'GET' });
checkResponse(channel)
return await channel.json()
}

View File

@ -3,7 +3,7 @@ import { checkResponse, fetchWithTimeout } from './fetchUtil';
export async function getChannels(token, revision) {
let param = "?agent=" + token
if (revision != null) {
param += '&revision=' + revision
param += '&channelRevision=' + revision
}
let channels = await fetchWithTimeout('/content/channels' + param, { method: 'GET' });
checkResponse(channels)

View File

@ -26,7 +26,8 @@ function App() {
<Route path="/user" element={ <User /> }>
<Route path="profile" element={<Profile />} />
<Route path="contact/:guid" element={<Contact />} />
<Route path="conversation/:id" element={<Conversation />} />
<Route path="conversation/:contact/:channel" element={<Conversation />} />
<Route path="conversation/:channel" element={<Conversation />} />
</Route>
</Routes>
</Router>

View File

@ -1,6 +1,7 @@
import { useEffect, useState, useRef } from 'react';
import { getContactProfile, setCardProfile, getCards, getCardImageUrl, getCardProfile, getCardDetail, getListingImageUrl, getListing, setProfileImage, setProfileData, getProfileImageUrl, getAccountStatus, setAccountSearchable, getProfile, getGroups, getAvailable, getUsername, setLogin, createAccount } from './fetchUtil';
import { getChannels } from '../Api/getChannels';
import { getChannel } from '../Api/getChannel';
import { getContactChannels } from '../Api/getContactChannels';
async function updateAccount(token, updateData) {
@ -30,7 +31,22 @@ async function updateChannels(token, revision, channelMap, mergeChannels) {
let channels = await getChannels(token, revision);
for (let channel of channels) {
if (channel.data) {
channelMap.set(channel.id, channel);
let cur = channelMap.get(channel.id);
if (cur == null) {
cur = { id: channel.id, data: { } }
}
if (cur.data.detailRevision != channel.data.detailRevision) {
if (channel.data.channelDetail != null) {
cur.data.channelDetail = channel.data.channelDetail;
cur.data.detailRevision = channel.data.detailRevision;
}
else {
let slot = await getChannel(token, channel.id);
cur.data.channelDetail = slot.data.channelDetail;
cur.data.detailRevision = slot.data.detailRevision;
}
}
channelMap.set(channel.id, cur);
}
else {
channelMap.delete(channel.id);

View File

@ -1,6 +1,6 @@
import React from 'react';
import { Button, Dropdown, Input, Tooltip, Menu } from 'antd';
import { AddTopicWrapper } from './AddTopic.styled';
import { AddTopicWrapper, BusySpin } from './AddTopic.styled';
import { AddCarousel } from './AddCarousel/AddCarousel';
import { useAddTopic } from './useAddTopic.hook';
import { BgColorsOutlined, FontColorsOutlined, FontSizeOutlined, PaperClipOutlined, SendOutlined } from '@ant-design/icons';
@ -9,11 +9,6 @@ export function AddTopic() {
const { state, actions } = useAddTopic();
const onAttach = () => {
console.log("ADD IMAGE");
actions.addImage(null);
}
const menu = (
<Menu>
<Menu.Item key="0">
@ -28,6 +23,10 @@ export function AddTopic() {
</Menu>
);
const onSend = () => {
actions.addTopic();
}
return (
<AddTopicWrapper>
<div class="container noselect">
@ -52,7 +51,8 @@ export function AddTopic() {
<Button icon={<BgColorsOutlined />} size="large" />
</div>
<div class="send">
<Button icon={<SendOutlined />} size="large" />
<Button icon={<SendOutlined />} onClick={onSend} size="large" />
<BusySpin spinning={state.busy} />
</div>
</div>
</div>

View File

@ -1,3 +1,4 @@
import { Spin } from 'antd';
import styled from 'styled-components';
export const AddTopicWrapper = styled.div`
@ -35,7 +36,14 @@ export const AddTopicWrapper = styled.div`
display: flex;
flex-grow: 1;
justify-content: flex-end;
align-items: center;
padding-top: 4px;
}
`;
export const BusySpin = styled(Spin)`
position: absolute;
margin-right: 12px;
x-index: 10;
`;

View File

@ -1,5 +1,7 @@
import { useContext, useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useParams } from 'react-router-dom';
import { addChannelTopic } from '../../../Api/addChannelTopic';
import { AppContext } from '../../../AppContext/AppContext';
export function useAddTopic() {
@ -7,11 +9,14 @@ export function useAddTopic() {
assets: [],
messageText: null,
messageColor: null,
messageWeight: null,
messageSize: null,
backgroundColor: null,
busy: false,
});
const { contact, channel } = useParams();
const app = useContext(AppContext);
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
}
@ -23,8 +28,6 @@ export function useAddTopic() {
});
}
const navigate = useNavigate();
const actions = {
addImage: (image) => { addAsset(image) },
addVideo: (video) => { addAsset(video) },
@ -46,7 +49,22 @@ export function useAddTopic() {
setBackgroundColor: (value) => {
updateState({ backgroundColor: value });
},
addTopic: () => {},
addTopic: async () => {
if (!state.busy) {
updateState({ busy: true });
try {
if (!contact) {
let message = { text: state.messageText, textColor: state.messageColor,
textSize: state.messageSize, backgroundColor: state.backgroundColor };
await addChannelTopic(app.state.token, channel, message, []);
}
}
catch(err) {
window.alert(err);
}
updateState({ busy: false });
}
},
};
return { state, actions };

View File

@ -8,7 +8,7 @@ export function useConversation() {
});
const data = useLocation();
const { id } = useParams();
const { contact, channel } = useParams();
const navigate = useNavigate();
const app = useContext(AppContext);
@ -22,11 +22,5 @@ export function useConversation() {
},
};
useEffect(() => {
if (app?.state?.access === 'user') {
console.log("CONVERSATION:", id);
}
}, [app, id])
return { state, actions };
}

View File

@ -14,7 +14,12 @@ export function useChannelItem() {
const actions = {
select: (item) => {
navigate(`/user/conversation/${item.channel.id}`);
if (item.guid) {
navigate(`/user/conversation/${item.guid}/${item.channel.id}`);
}
else {
navigate(`/user/conversation/${item.channel.id}`);
}
},
};