providing server side config for key size and asset type support

This commit is contained in:
Roland Osborne 2022-09-01 00:22:43 -07:00
parent fd372187bc
commit 58a2984dbc
22 changed files with 217 additions and 89 deletions

View File

@ -3271,8 +3271,6 @@ components:
type: object type: object
required: required:
- domain - domain
- openAccess
- accountLimit
- accountStorage - accountStorage
properties: properties:
domain: domain:
@ -3280,11 +3278,14 @@ components:
accountStorage: accountStorage:
type: integer type: integer
format: int64 format: int64
openAccess: enableImage:
type: boolean type: boolean
accountLimit: enableAudio:
type: integer type: boolean
format: int64 enableVideo:
type: boolean
keyType:
type: string
AccountStatus: AccountStatus:
type: object type: object
@ -3627,6 +3628,12 @@ components:
updated: updated:
type: integer type: integer
format: int64 format: int64
enableImage:
type: boolean
enableAudio:
type: boolean
enableVideo:
type: video
contacts: contacts:
$ref: '#/components/schemas/ChannelContacts' $ref: '#/components/schemas/ChannelContacts'
members: members:

View File

@ -80,5 +80,9 @@ func AddChannel(w http.ResponseWriter, r *http.Request) {
for _, card := range cards { for _, card := range cards {
SetContactChannelNotification(account, card) SetContactChannelNotification(account, card)
} }
WriteResponse(w, getChannelModel(slot, true, true))
video := getBoolConfigValue(CNFEnableVideo, true);
audio := getBoolConfigValue(CNFEnableAudio, true);
image := getBoolConfigValue(CNFEnableImage, true);
WriteResponse(w, getChannelModel(slot, true, true, image, audio, video))
} }

View File

@ -89,5 +89,9 @@ func ClearChannelCard(w http.ResponseWriter, r *http.Request) {
for _, card := range cards { for _, card := range cards {
SetContactChannelNotification(account, &card) SetContactChannelNotification(account, &card)
} }
WriteResponse(w, getChannelModel(&channelSlot, true, true))
video := getBoolConfigValue(CNFEnableVideo, true);
audio := getBoolConfigValue(CNFEnableAudio, true);
image := getBoolConfigValue(CNFEnableImage, true);
WriteResponse(w, getChannelModel(&channelSlot, true, true, image, audio, video))
} }

View File

@ -83,5 +83,9 @@ func ClearChannelGroup(w http.ResponseWriter, r *http.Request) {
for _, card := range cards { for _, card := range cards {
SetContactChannelNotification(account, &card) SetContactChannelNotification(account, &card)
} }
WriteResponse(w, getChannelModel(&channelSlot, true, true))
video := getBoolConfigValue(CNFEnableVideo, true);
audio := getBoolConfigValue(CNFEnableAudio, true);
image := getBoolConfigValue(CNFEnableImage, true);
WriteResponse(w, getChannelModel(&channelSlot, true, true, image, audio, video))
} }

View File

@ -19,8 +19,8 @@ func GetAccountAvailable(w http.ResponseWriter, r *http.Request) {
func getAvailableAccounts() (available int64, err error) { func getAvailableAccounts() (available int64, err error) {
open := getBoolConfigValue(CNFOpenAccess, true) open := getBoolConfigValue(CNFOpenAccess, false)
limit := getNumConfigValue(CNFAccountLimit, 16) limit := getNumConfigValue(CNFAccountLimit, 0)
var count int64 var count int64
if err = store.DB.Model(&store.Account{}).Count(&count).Error; err != nil { if err = store.DB.Model(&store.Account{}).Count(&count).Error; err != nil {

View File

@ -49,15 +49,19 @@ func GetChannelDetail(w http.ResponseWriter, r *http.Request) {
return return
} }
video := getBoolConfigValue(CNFEnableVideo, true);
audio := getBoolConfigValue(CNFEnableAudio, true);
image := getBoolConfigValue(CNFEnableImage, true);
// return model data // return model data
if guid != "" { if guid != "" {
if isChannelShared(guid, slot.Channel) { if isChannelShared(guid, slot.Channel) {
WriteResponse(w, getChannelDetailModel(&slot, false)) WriteResponse(w, getChannelDetailModel(&slot, false, image, audio, video))
} else { } else {
ErrResponse(w, http.StatusNotFound, errors.New("channel not shared with requestor")) ErrResponse(w, http.StatusNotFound, errors.New("channel not shared with requestor"))
return return
} }
} else { } else {
WriteResponse(w, getChannelDetailModel(&slot, true)) WriteResponse(w, getChannelDetailModel(&slot, true, image, audio, video))
} }
} }

View File

@ -50,6 +50,10 @@ func GetChannels(w http.ResponseWriter, r *http.Request) {
} }
} }
video := getBoolConfigValue(CNFEnableVideo, true);
audio := getBoolConfigValue(CNFEnableAudio, true);
image := getBoolConfigValue(CNFEnableImage, true);
response := []*Channel{} response := []*Channel{}
tokenType := ParamTokenType(r) tokenType := ParamTokenType(r)
if tokenType == APPTokenAgent { if tokenType == APPTokenAgent {
@ -80,7 +84,7 @@ func GetChannels(w http.ResponseWriter, r *http.Request) {
if channelRevisionSet { if channelRevisionSet {
response = append(response, getChannelRevisionModel(&slot, true)) response = append(response, getChannelRevisionModel(&slot, true))
} else if slot.Channel != nil { } else if slot.Channel != nil {
response = append(response, getChannelModel(&slot, true, true)) response = append(response, getChannelModel(&slot, true, true, image, audio, video))
} }
} }
} }
@ -124,7 +128,7 @@ func GetChannels(w http.ResponseWriter, r *http.Request) {
if channelRevisionSet { if channelRevisionSet {
response = append(response, getChannelRevisionModel(&slot, shared)) response = append(response, getChannelRevisionModel(&slot, shared))
} else if shared { } else if shared {
response = append(response, getChannelModel(&slot, true, false)) response = append(response, getChannelModel(&slot, true, false, image, audio, video))
} }
} }
} }

View File

@ -16,9 +16,11 @@ func GetNodeConfig(w http.ResponseWriter, r *http.Request) {
// get node config fields // get node config fields
var config NodeConfig var config NodeConfig
config.Domain = getStrConfigValue(CNFDomain, "") config.Domain = getStrConfigValue(CNFDomain, "")
config.AccountLimit = getNumConfigValue(CNFAccountLimit, 16)
config.OpenAccess = getBoolConfigValue(CNFOpenAccess, true)
config.AccountStorage = getNumConfigValue(CNFStorage, 0) config.AccountStorage = getNumConfigValue(CNFStorage, 0)
config.EnableImage = getBoolConfigValue(CNFEnableImage, true)
config.EnableAudio = getBoolConfigValue(CNFEnableAudio, true)
config.EnableVideo = getBoolConfigValue(CNFEnableVideo, true)
config.KeyType = getStrConfigValue(CNFKeyType, APPRSA4096)
WriteResponse(w, config) WriteResponse(w, config)
} }

View File

@ -90,5 +90,9 @@ func SetChannelCard(w http.ResponseWriter, r *http.Request) {
for _, card := range cards { for _, card := range cards {
SetContactChannelNotification(account, &card) SetContactChannelNotification(account, &card)
} }
WriteResponse(w, getChannelModel(&channelSlot, true, true))
video := getBoolConfigValue(CNFEnableVideo, true);
audio := getBoolConfigValue(CNFEnableAudio, true);
image := getBoolConfigValue(CNFEnableImage, true);
WriteResponse(w, getChannelModel(&channelSlot, true, true, image, audio, video))
} }

View File

@ -83,5 +83,9 @@ func SetChannelGroup(w http.ResponseWriter, r *http.Request) {
for _, card := range cards { for _, card := range cards {
SetContactChannelNotification(account, &card) SetContactChannelNotification(account, &card)
} }
WriteResponse(w, getChannelModel(&channelSlot, true, true))
video := getBoolConfigValue(CNFEnableVideo, true);
audio := getBoolConfigValue(CNFEnableAudio, true);
image := getBoolConfigValue(CNFEnableImage, true);
WriteResponse(w, getChannelModel(&channelSlot, true, true, image, audio, video))
} }

View File

@ -82,5 +82,9 @@ func SetChannelSubject(w http.ResponseWriter, r *http.Request) {
for _, card := range cards { for _, card := range cards {
SetContactChannelNotification(account, &card) SetContactChannelNotification(account, &card)
} }
WriteResponse(w, getChannelModel(&slot, true, true))
video := getBoolConfigValue(CNFEnableVideo, true);
audio := getBoolConfigValue(CNFEnableAudio, true);
image := getBoolConfigValue(CNFEnableImage, true);
WriteResponse(w, getChannelModel(&slot, true, true, image, audio, video))
} }

View File

@ -34,22 +34,6 @@ func SetNodeConfig(w http.ResponseWriter, r *http.Request) {
return res return res
} }
// upsert account limit config
if res := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "config_id"}},
DoUpdates: clause.AssignmentColumns([]string{"num_value"}),
}).Create(&store.Config{ConfigID: CNFAccountLimit, NumValue: config.AccountLimit}).Error; res != nil {
return res
}
// upsert account open access
if res := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "config_id"}},
DoUpdates: clause.AssignmentColumns([]string{"bool_value"}),
}).Create(&store.Config{ConfigID: CNFAccountLimit, BoolValue: config.OpenAccess}).Error; res != nil {
return res
}
// upsert account storage config // upsert account storage config
if res := tx.Clauses(clause.OnConflict{ if res := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "config_id"}}, Columns: []clause.Column{{Name: "config_id"}},
@ -58,6 +42,38 @@ func SetNodeConfig(w http.ResponseWriter, r *http.Request) {
return res return res
} }
// upsert enable image processing
if res := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "config_id"}},
DoUpdates: clause.AssignmentColumns([]string{"bool_value"}),
}).Create(&store.Config{ConfigID: CNFEnableImage, BoolValue: config.EnableImage}).Error; res != nil {
return res
}
// upsert enable audio processing
if res := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "config_id"}},
DoUpdates: clause.AssignmentColumns([]string{"bool_value"}),
}).Create(&store.Config{ConfigID: CNFEnableAudio, BoolValue: config.EnableAudio}).Error; res != nil {
return res
}
// upsert enable video processing
if res := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "config_id"}},
DoUpdates: clause.AssignmentColumns([]string{"bool_value"}),
}).Create(&store.Config{ConfigID: CNFEnableVideo, BoolValue: config.EnableVideo}).Error; res != nil {
return res
}
// upsert key type
if res := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "config_id"}},
DoUpdates: clause.AssignmentColumns([]string{"str_value"}),
}).Create(&store.Config{ConfigID: CNFKeyType, StrValue: config.KeyType}).Error; res != nil {
return res
}
return nil return nil
}) })
if err != nil { if err != nil {

View File

@ -30,6 +30,18 @@ const CNFAssetPath = "asset_path"
//CNFScriptPath specifies the path where transform scripts are found //CNFScriptPath specifies the path where transform scripts are found
const CNFScriptPath = "script_path" const CNFScriptPath = "script_path"
//CNFEnableImage specifies whether node can process image assets
const CNFEnableImage = "enable_image"
//CNFEnableAudio specifies whether node can process audio assets
const CNFEnableAudio = "enable_audio"
//CNFEnableVideo specifies whether node can process video assets
const CNFEnableVideo = "enable_video"
//CNFKeyType specifies the type of key to use for identity
const CNFKeyType = "key_type"
func getStrConfigValue(configID string, empty string) string { func getStrConfigValue(configID string, empty string) string {
var config store.Config var config store.Config
err := store.DB.Where("config_id = ?", configID).First(&config).Error err := store.DB.Where("config_id = ?", configID).First(&config).Error

View File

@ -8,20 +8,14 @@ import (
"errors" "errors"
) )
var keySize int = APPKeySize
//SetKeySize sets the key size to use for new accounts
func SetKeySize(size int) {
keySize = size
}
//GenerateRsaKeyPair creates a public/private key for a new account //GenerateRsaKeyPair creates a public/private key for a new account
func GenerateRsaKeyPair() (*rsa.PrivateKey, *rsa.PublicKey, string, error) { func GenerateRsaKeyPair() (*rsa.PrivateKey, *rsa.PublicKey, string, error) {
if keySize == 2048 { keyType := getStrConfigValue(CNFKeyType, "RSA4096");
privkey, _ := rsa.GenerateKey(rand.Reader, keySize) if keyType == "RSA2048" {
privkey, _ := rsa.GenerateKey(rand.Reader, 2048)
return privkey, &privkey.PublicKey, "RSA2048", nil return privkey, &privkey.PublicKey, "RSA2048", nil
} else if keySize == 4096 { } else if keyType == "RSA4096" {
privkey, _ := rsa.GenerateKey(rand.Reader, keySize) privkey, _ := rsa.GenerateKey(rand.Reader, 4096)
return privkey, &privkey.PublicKey, "RSA2048", nil return privkey, &privkey.PublicKey, "RSA2048", nil
} else { } else {
return nil, nil, "", errors.New("invalid key setting") return nil, nil, "", errors.New("invalid key setting")

View File

@ -166,7 +166,7 @@ func getChannelRevisionModel(slot *store.ChannelSlot, showData bool) *Channel {
} }
} }
func getChannelDetailModel(slot *store.ChannelSlot, showList bool) *ChannelDetail { func getChannelDetailModel(slot *store.ChannelSlot, showList bool, image bool, audio bool, video bool) *ChannelDetail {
if slot.Channel == nil { if slot.Channel == nil {
return nil return nil
@ -195,6 +195,9 @@ func getChannelDetailModel(slot *store.ChannelSlot, showList bool) *ChannelDetai
Data: slot.Channel.Data, Data: slot.Channel.Data,
Created: slot.Channel.Created, Created: slot.Channel.Created,
Updated: slot.Channel.Updated, Updated: slot.Channel.Updated,
EnableImage: image,
EnableAudio: audio,
EnableVideo: video,
Contacts: contacts, Contacts: contacts,
Members: members, Members: members,
} }
@ -221,7 +224,7 @@ func getChannelSummaryModel(slot *store.ChannelSlot) *ChannelSummary {
} }
} }
func getChannelModel(slot *store.ChannelSlot, showData bool, showList bool) *Channel { func getChannelModel(slot *store.ChannelSlot, showData bool, showList bool, image bool, audio bool, video bool) *Channel {
if !showData || slot.Channel == nil { if !showData || slot.Channel == nil {
return &Channel{ return &Channel{
@ -236,7 +239,7 @@ func getChannelModel(slot *store.ChannelSlot, showData bool, showList bool) *Cha
Data: &ChannelData{ Data: &ChannelData{
DetailRevision: slot.Channel.DetailRevision, DetailRevision: slot.Channel.DetailRevision,
TopicRevision: slot.Channel.TopicRevision, TopicRevision: slot.Channel.TopicRevision,
ChannelDetail: getChannelDetailModel(slot, showList), ChannelDetail: getChannelDetailModel(slot, showList, image, audio, video),
ChannelSummary: getChannelSummaryModel(slot), ChannelSummary: getChannelSummaryModel(slot),
}, },
} }

View File

@ -178,6 +178,12 @@ type ChannelDetail struct {
Updated int64 `json:"updated"` Updated int64 `json:"updated"`
EnableImage bool `json:"enableImage"`
EnableAudio bool `json:"enableAudio"`
EnableVideo bool `json:"enableVideo"`
Contacts *ChannelContacts `json:"contacts,omitempty"` Contacts *ChannelContacts `json:"contacts,omitempty"`
Members []string `json:"members"` Members []string `json:"members"`
@ -321,9 +327,13 @@ type LoginAccess struct {
type NodeConfig struct { type NodeConfig struct {
Domain string `json:"domain"` Domain string `json:"domain"`
OpenAccess bool `json:"openAccess"` EnableImage bool `json:"enableImage"`
AccountLimit int64 `json:"accountLimit"` EnableAudio bool `json:"enableAudio"`
EnableVideo bool `json:"enableVideo"`
KeyType string `json:"keyType"`
AccountStorage int64 `json:"accountStorage"` AccountStorage int64 `json:"accountStorage"`
} }

View File

@ -1,5 +1,5 @@
import { DashboardWrapper, SettingsButton, AddButton, SettingsLayout, CreateLayout } from './Dashboard.styled'; import { DashboardWrapper, SettingsButton, AddButton, SettingsLayout, CreateLayout } from './Dashboard.styled';
import { Tooltip, Button, Modal, Input, InputNumber, Space, List } from 'antd'; import { Tooltip, Checkbox, Select, Button, Modal, Input, InputNumber, Space, List } from 'antd';
import { SettingOutlined, CopyOutlined, UserAddOutlined, LogoutOutlined, ReloadOutlined } from '@ant-design/icons'; import { SettingOutlined, CopyOutlined, UserAddOutlined, LogoutOutlined, ReloadOutlined } from '@ant-design/icons';
import { useDashboard } from './useDashboard.hook'; import { useDashboard } from './useDashboard.hook';
import { AccountItem } from './accountItem/AccountItem'; import { AccountItem } from './accountItem/AccountItem';
@ -63,15 +63,35 @@ export function Dashboard({ token, config, logout }) {
<Modal title="Settings" visible={state.showSettings} centered <Modal title="Settings" visible={state.showSettings} centered
okText="Save" onOk={() => actions.setSettings()} onCancel={() => actions.setShowSettings(false)}> okText="Save" onOk={() => actions.setSettings()} onCancel={() => actions.setShowSettings(false)}>
<SettingsLayout direction="vertical"> <SettingsLayout direction="vertical">
<div class="host"> <div class="field">
<div>Federated Host:&nbsp;</div> <div>Federated Host:&nbsp;</div>
<Input placeholder="domain:port/app" onChange={(e) => actions.setHost(e.target.value)} <Input placeholder="domain:port/app" onChange={(e) => actions.setHost(e.target.value)}
value={state.host} /> value={state.domain} />
</div> </div>
<div class="storage"> <div class="field">
<div>Storage Limit (GB) / Account:&nbsp;</div> <div>Storage Limit (GB) / Account:&nbsp;</div>
<InputNumber defaultValue={8} onChange={(e) => actions.setStorage(e)} <InputNumber defaultValue={0} onChange={(e) => actions.setStorage(e)}
placeholder="0 for unrestricted" value={state.storage} /> placeholder="0 for unrestricted" value={state.accountStorage} />
</div>
<div class="field">
<div>Account Key Type:&nbsp;</div>
<Select labelInValue defaultValue={{ value: 'RSA4096', label: 'RSA 4096' }}
value={state.keyType} onChange={(o) => actions.setKeyType(o.value)}>
<Select.Option value="RSA2048">RSA 2048</Select.Option>
<Select.Option value="RSA4096">RSA 4096</Select.Option>
</Select>
</div>
<div class="field">
<Checkbox onChange={(e) => actions.setEnableImage(e.target.checked)}
defaultChecked={true} checked={state.enableImage}>Enable Image Queue</Checkbox>
</div>
<div class="field">
<Checkbox onChange={(e) => actions.setEnableAudio(e.target.checked)}
defaultChecked={true} checked={state.enableAudio}>Enable Audio Queue</Checkbox>
</div>
<div class="field">
<Checkbox onChange={(e) => actions.setEnableVideo(e.target.checked)}
defaultChecked={true} checked={state.enableVideo}>Enable Video Queue</Checkbox>
</div> </div>
</SettingsLayout> </SettingsLayout>
</Modal> </Modal>

View File

@ -71,14 +71,7 @@ export const SettingsButton = styled(Button)`
export const SettingsLayout = styled(Space)` export const SettingsLayout = styled(Space)`
width: 100%; width: 100%;
.host { .field {
white-space: nowrap;
display: flex;
flex-direction: row;
align-items: center;
}
.storage {
white-space: nowrap; white-space: nowrap;
display: flex; display: flex;
flex-direction: row; flex-direction: row;

View File

@ -7,8 +7,12 @@ import { addAccountCreate } from 'api/addAccountCreate';
export function useDashboard(token, config) { export function useDashboard(token, config) {
const [state, setState] = useState({ const [state, setState] = useState({
host: "", domain: "",
storage: null, accountStorage: null,
keyType: null,
enableImage: null,
enableAudio: null,
enableVideo: null,
showSettings: false, showSettings: false,
busy: false, busy: false,
loading: false, loading: false,
@ -42,11 +46,23 @@ export function useDashboard(token, config) {
await removeAccount(token, accountId); await removeAccount(token, accountId);
actions.getAccounts(); actions.getAccounts();
}, },
setHost: (value) => { setHost: (domain) => {
updateState({ host: value }); updateState({ domain });
}, },
setStorage: (value) => { setStorage: (accountStorage) => {
updateState({ storage: value }); updateState({ accountStorage });
},
setKeyType: (keyType) => {
updateState({ keyType });
},
setEnableImage: (enableImage) => {
updateState({ enableImage });
},
setEnableAudio: (enableAudio) => {
updateState({ enableAudio });
},
setEnableVideo: (enableVideo) => {
updateState({ enableVideo });
}, },
setShowSettings: (value) => { setShowSettings: (value) => {
updateState({ showSettings: value }); updateState({ showSettings: value });
@ -55,8 +71,10 @@ export function useDashboard(token, config) {
if (!state.busy) { if (!state.busy) {
updateState({ busy: true }); updateState({ busy: true });
try { try {
const { domain, keyType, accountStorage, enableImage, enableAudio, enableVideo } = state;
await setNodeConfig(token, await setNodeConfig(token,
{ ...state.config, domain: state.host, accountStorage: state.storage * 1073741824 }); { domain, accountStorage: accountStorage * 1073741824,
keyType, enableImage, enableAudio, enableVideo });
updateState({ showSettings: false }); updateState({ showSettings: false });
} }
catch(err) { catch(err) {
@ -92,13 +110,11 @@ export function useDashboard(token, config) {
}; };
useEffect(() => { useEffect(() => {
let storage = config.accountStorage / 1073741824; const { accountStorage, domain, keyType, enableImage, enableAudio, enableVideo } = config;
if (storage > 1) { updateState({ domain, accountStorage: Math.ceil(accountStorage / 1073741824), keyType,
storage = Math.ceil(storage); enableImage, enableAudio, enableVideo });
}
updateState({ host: config.domain, storage: storage });
actions.getAccounts(); actions.getAccounts();
}, []); }, [config]);
return { state, actions }; return { state, actions };
} }

View File

@ -18,6 +18,9 @@ export function useConversationContext() {
members: new Set(), members: new Set(),
topics: new Map(), topics: new Map(),
revision: null, revision: null,
enableImage: null,
enabelAudio: null,
enableVideo: null,
}); });
const EVENT_OPEN = 1; const EVENT_OPEN = 1;
@ -198,12 +201,16 @@ export function useConversationContext() {
let subject = getSubject(chan); let subject = getSubject(chan);
let contacts = getContacts(chan); let contacts = getContacts(chan);
let members = getMembers(chan); let members = getMembers(chan);
const { enableImage, enableAudio, enableVideo } = chan.data.channelDetail;
updateState({ updateState({
init: true, init: true,
error: false, error: false,
subject, subject,
contacts, contacts,
members, members,
enableImage,
enableAudio,
enableVideo,
topics: topics.current, topics: topics.current,
revision: channelView.current.revision, revision: channelView.current.revision,
}); });

View File

@ -104,15 +104,21 @@ export function AddTopic({ cardId, channelId }) {
value={state.messageText} autocapitalize="none" /> value={state.messageText} autocapitalize="none" />
</div> </div>
<div class="buttons"> <div class="buttons">
{ state.enableImage && (
<div class="button space" onClick={() => attachImage.current.click()}> <div class="button space" onClick={() => attachImage.current.click()}>
<PictureOutlined /> <PictureOutlined />
</div> </div>
)}
{ state.enableVideo && (
<div class="button space" onClick={() => attachVideo.current.click()}> <div class="button space" onClick={() => attachVideo.current.click()}>
<VideoCameraOutlined /> <VideoCameraOutlined />
</div> </div>
)}
{ state.enableAudio && (
<div class="button space" onClick={() => attachAudio.current.click()}> <div class="button space" onClick={() => attachAudio.current.click()}>
<SoundOutlined /> <SoundOutlined />
</div> </div>
)}
<div class="bar space" /> <div class="bar space" />
<div class="button space"> <div class="button space">
<Dropdown overlay={picker} overlayStyle={{ minWidth: 0 }} trigger={['click']} placement="top"> <Dropdown overlay={picker} overlayStyle={{ minWidth: 0 }} trigger={['click']} placement="top">

View File

@ -1,10 +1,14 @@
import { useContext, useState } from 'react'; import { useContext, useState, useEffect } from 'react';
import { CardContext } from 'context/CardContext'; import { CardContext } from 'context/CardContext';
import { ChannelContext } from 'context/ChannelContext'; import { ChannelContext } from 'context/ChannelContext';
import { ConversationContext } from 'context/ConversationContext';
export function useAddTopic(cardId, channelId) { export function useAddTopic(cardId, channelId) {
const [state, setState] = useState({ const [state, setState] = useState({
enableImage: null,
enableAudio: null,
enableVideo: null,
assets: [], assets: [],
messageText: null, messageText: null,
textColor: '#444444', textColor: '#444444',
@ -16,6 +20,7 @@ export function useAddTopic(cardId, channelId) {
const card = useContext(CardContext); const card = useContext(CardContext);
const channel = useContext(ChannelContext); const channel = useContext(ChannelContext);
const conversation = useContext(ConversationContext);
const updateState = (value) => { const updateState = (value) => {
setState((s) => ({ ...s, ...value })); setState((s) => ({ ...s, ...value }));
@ -43,6 +48,11 @@ export function useAddTopic(cardId, channelId) {
}); });
} }
useEffect(() => {
const { enableImage, enableAudio, enableVideo } = conversation.state;
updateState({ enableImage, enableAudio, enableVideo });
}, [conversation]);
const actions = { const actions = {
addImage: (image) => { addImage: (image) => {
let url = URL.createObjectURL(image); let url = URL.createObjectURL(image);