mirror of
https://github.com/balzack/databag.git
synced 2025-02-12 03:29:16 +00:00
addTopics to self hosted channels
This commit is contained in:
parent
343aa21e3e
commit
6e2801d705
@ -2113,7 +2113,7 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/ChannelParams'
|
$ref: '#/components/schemas/ChannelParams'
|
||||||
|
|
||||||
/content/channels/{channelId}:
|
/content/channels/{channelId}/detail:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
- content
|
- content
|
||||||
@ -2143,6 +2143,8 @@ paths:
|
|||||||
description: account disabled
|
description: account disabled
|
||||||
'500':
|
'500':
|
||||||
description: internal server error
|
description: internal server error
|
||||||
|
|
||||||
|
/content/channels/{channelId}:
|
||||||
delete:
|
delete:
|
||||||
tags:
|
tags:
|
||||||
- content
|
- content
|
||||||
@ -2869,7 +2871,7 @@ paths:
|
|||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
type: boolean
|
type: string
|
||||||
|
|
||||||
|
|
||||||
/content/channels/{channelId}/topics/{topicId}/tags:
|
/content/channels/{channelId}/topics/{topicId}/tags:
|
||||||
|
@ -16,16 +16,16 @@ func GetChannel(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
var guid string
|
var guid string
|
||||||
var act *store.Account
|
var act *store.Account
|
||||||
tokenType := r.Header.Get("TokenType")
|
tokenType := ParamTokenType(r)
|
||||||
if tokenType == APP_TOKENAGENT {
|
if tokenType == APP_TOKENAGENT {
|
||||||
account, code, err := BearerAppToken(r, false);
|
account, code, err := ParamAgentToken(r, false);
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrResponse(w, code, err)
|
ErrResponse(w, code, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
act = account
|
act = account
|
||||||
} else if tokenType == APP_TOKENCONTACT {
|
} else if tokenType == APP_TOKENCONTACT {
|
||||||
card, code, err := BearerContactToken(r, true)
|
card, code, err := ParamContactToken(r, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrResponse(w, code, err)
|
ErrResponse(w, code, err)
|
||||||
return
|
return
|
||||||
|
@ -62,16 +62,16 @@ func getChannelSlot(r *http.Request, member bool) (slot store.ChannelSlot, guid
|
|||||||
|
|
||||||
// validate contact access
|
// validate contact access
|
||||||
var account *store.Account
|
var account *store.Account
|
||||||
tokenType := r.Header.Get("TokenType")
|
tokenType := ParamTokenType(r);
|
||||||
if tokenType == APP_TOKENAGENT {
|
if tokenType == APP_TOKENAGENT {
|
||||||
account, code, err = BearerAppToken(r, false);
|
account, code, err = ParamAgentToken(r, false);
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guid = account.Guid
|
guid = account.Guid
|
||||||
} else if tokenType == APP_TOKENCONTACT {
|
} else if tokenType == APP_TOKENCONTACT {
|
||||||
var card *store.Card
|
var card *store.Card
|
||||||
card, code, err = BearerContactToken(r, true)
|
card, code, err = ParamContactToken(r, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ func SetChannelTopicSubject(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// load topic
|
// load topic
|
||||||
var topicSlot store.TopicSlot
|
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) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
ErrResponse(w, http.StatusNotFound, err)
|
ErrResponse(w, http.StatusNotFound, err)
|
||||||
} else {
|
} else {
|
||||||
|
@ -541,7 +541,7 @@ var routes = Routes{
|
|||||||
Route{
|
Route{
|
||||||
"GetChannel",
|
"GetChannel",
|
||||||
strings.ToUpper("Get"),
|
strings.ToUpper("Get"),
|
||||||
"/content/channels/{channelId}",
|
"/content/channels/{channelId}/detail",
|
||||||
GetChannel,
|
GetChannel,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -196,18 +196,28 @@ func ApiTestUpload(
|
|||||||
return
|
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()
|
w := httptest.NewRecorder()
|
||||||
r := httptest.NewRequest(requestType, name, &data)
|
r := httptest.NewRequest(requestType, name, &data)
|
||||||
|
|
||||||
if params != nil {
|
if params != nil {
|
||||||
r = mux.SetURLVars(r, *params)
|
r = mux.SetURLVars(r, *params)
|
||||||
}
|
}
|
||||||
if tokenType != "" {
|
|
||||||
r.Header.Add("TokenType", tokenType)
|
|
||||||
}
|
|
||||||
if token != "" {
|
|
||||||
SetBearerAuth(r, token)
|
|
||||||
}
|
|
||||||
r.Header.Set("Content-Type", writer.FormDataContentType())
|
r.Header.Set("Content-Type", writer.FormDataContentType())
|
||||||
endpoint(w, r)
|
endpoint(w, r)
|
||||||
|
|
||||||
|
24
net/web/src/Api/addChannelTopic.js
Normal file
24
net/web/src/Api/addChannelTopic.js
Normal 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;
|
||||||
|
}
|
||||||
|
|
8
net/web/src/Api/getChannel.js
Normal file
8
net/web/src/Api/getChannel.js
Normal 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()
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,7 @@ import { checkResponse, fetchWithTimeout } from './fetchUtil';
|
|||||||
export async function getChannels(token, revision) {
|
export async function getChannels(token, revision) {
|
||||||
let param = "?agent=" + token
|
let param = "?agent=" + token
|
||||||
if (revision != null) {
|
if (revision != null) {
|
||||||
param += '&revision=' + revision
|
param += '&channelRevision=' + revision
|
||||||
}
|
}
|
||||||
let channels = await fetchWithTimeout('/content/channels' + param, { method: 'GET' });
|
let channels = await fetchWithTimeout('/content/channels' + param, { method: 'GET' });
|
||||||
checkResponse(channels)
|
checkResponse(channels)
|
||||||
|
@ -26,7 +26,8 @@ function App() {
|
|||||||
<Route path="/user" element={ <User /> }>
|
<Route path="/user" element={ <User /> }>
|
||||||
<Route path="profile" element={<Profile />} />
|
<Route path="profile" element={<Profile />} />
|
||||||
<Route path="contact/:guid" element={<Contact />} />
|
<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>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
</Router>
|
</Router>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { useEffect, useState, useRef } from 'react';
|
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 { 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 { getChannels } from '../Api/getChannels';
|
||||||
|
import { getChannel } from '../Api/getChannel';
|
||||||
import { getContactChannels } from '../Api/getContactChannels';
|
import { getContactChannels } from '../Api/getContactChannels';
|
||||||
|
|
||||||
async function updateAccount(token, updateData) {
|
async function updateAccount(token, updateData) {
|
||||||
@ -30,7 +31,22 @@ async function updateChannels(token, revision, channelMap, mergeChannels) {
|
|||||||
let channels = await getChannels(token, revision);
|
let channels = await getChannels(token, revision);
|
||||||
for (let channel of channels) {
|
for (let channel of channels) {
|
||||||
if (channel.data) {
|
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 {
|
else {
|
||||||
channelMap.delete(channel.id);
|
channelMap.delete(channel.id);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Button, Dropdown, Input, Tooltip, Menu } from 'antd';
|
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 { AddCarousel } from './AddCarousel/AddCarousel';
|
||||||
import { useAddTopic } from './useAddTopic.hook';
|
import { useAddTopic } from './useAddTopic.hook';
|
||||||
import { BgColorsOutlined, FontColorsOutlined, FontSizeOutlined, PaperClipOutlined, SendOutlined } from '@ant-design/icons';
|
import { BgColorsOutlined, FontColorsOutlined, FontSizeOutlined, PaperClipOutlined, SendOutlined } from '@ant-design/icons';
|
||||||
@ -9,11 +9,6 @@ export function AddTopic() {
|
|||||||
|
|
||||||
const { state, actions } = useAddTopic();
|
const { state, actions } = useAddTopic();
|
||||||
|
|
||||||
const onAttach = () => {
|
|
||||||
console.log("ADD IMAGE");
|
|
||||||
actions.addImage(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
const menu = (
|
const menu = (
|
||||||
<Menu>
|
<Menu>
|
||||||
<Menu.Item key="0">
|
<Menu.Item key="0">
|
||||||
@ -28,6 +23,10 @@ export function AddTopic() {
|
|||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onSend = () => {
|
||||||
|
actions.addTopic();
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AddTopicWrapper>
|
<AddTopicWrapper>
|
||||||
<div class="container noselect">
|
<div class="container noselect">
|
||||||
@ -52,7 +51,8 @@ export function AddTopic() {
|
|||||||
<Button icon={<BgColorsOutlined />} size="large" />
|
<Button icon={<BgColorsOutlined />} size="large" />
|
||||||
</div>
|
</div>
|
||||||
<div class="send">
|
<div class="send">
|
||||||
<Button icon={<SendOutlined />} size="large" />
|
<Button icon={<SendOutlined />} onClick={onSend} size="large" />
|
||||||
|
<BusySpin spinning={state.busy} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { Spin } from 'antd';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
export const AddTopicWrapper = styled.div`
|
export const AddTopicWrapper = styled.div`
|
||||||
@ -35,7 +36,14 @@ export const AddTopicWrapper = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
padding-top: 4px;
|
padding-top: 4px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const BusySpin = styled(Spin)`
|
||||||
|
position: absolute;
|
||||||
|
margin-right: 12px;
|
||||||
|
x-index: 10;
|
||||||
|
`;
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { useContext, useState, useEffect } from 'react';
|
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() {
|
export function useAddTopic() {
|
||||||
|
|
||||||
@ -7,11 +9,14 @@ export function useAddTopic() {
|
|||||||
assets: [],
|
assets: [],
|
||||||
messageText: null,
|
messageText: null,
|
||||||
messageColor: null,
|
messageColor: null,
|
||||||
messageWeight: null,
|
|
||||||
messageSize: null,
|
messageSize: null,
|
||||||
backgroundColor: null,
|
backgroundColor: null,
|
||||||
|
busy: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { contact, channel } = useParams();
|
||||||
|
const app = useContext(AppContext);
|
||||||
|
|
||||||
const updateState = (value) => {
|
const updateState = (value) => {
|
||||||
setState((s) => ({ ...s, ...value }));
|
setState((s) => ({ ...s, ...value }));
|
||||||
}
|
}
|
||||||
@ -23,8 +28,6 @@ export function useAddTopic() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
addImage: (image) => { addAsset(image) },
|
addImage: (image) => { addAsset(image) },
|
||||||
addVideo: (video) => { addAsset(video) },
|
addVideo: (video) => { addAsset(video) },
|
||||||
@ -46,7 +49,22 @@ export function useAddTopic() {
|
|||||||
setBackgroundColor: (value) => {
|
setBackgroundColor: (value) => {
|
||||||
updateState({ backgroundColor: 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 };
|
return { state, actions };
|
||||||
|
@ -8,7 +8,7 @@ export function useConversation() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const data = useLocation();
|
const data = useLocation();
|
||||||
const { id } = useParams();
|
const { contact, channel } = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const app = useContext(AppContext);
|
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 };
|
return { state, actions };
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,12 @@ export function useChannelItem() {
|
|||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
select: (item) => {
|
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}`);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user