refactor add modal for webapp

This commit is contained in:
Roland Osborne 2023-01-18 15:35:27 -08:00
parent 064ec721af
commit e405f46330
6 changed files with 171 additions and 133 deletions

View File

@ -1,5 +1,5 @@
import { Modal, Input, List, Button, Switch } from 'antd'; import { Modal, Input, List, Button } from 'antd';
import { ChannelsWrapper, AddFooter } from './Channels.styled'; import { ChannelsWrapper } from './Channels.styled';
import { CommentOutlined, SearchOutlined } from '@ant-design/icons'; import { CommentOutlined, SearchOutlined } from '@ant-design/icons';
import { useChannels } from './useChannels.hook'; import { useChannels } from './useChannels.hook';
import { ChannelItem } from './channelItem/ChannelItem'; import { ChannelItem } from './channelItem/ChannelItem';
@ -9,34 +9,14 @@ export function Channels({ open, active }) {
const { state, actions } = useChannels(); const { state, actions } = useChannels();
const addChannel = async () => { const added = (id) => {
try {
const id = await actions.addChannel();
actions.clearShowAdd(); actions.clearShowAdd();
open(id); open(id);
}
catch(err) {
Modal.error({
title: 'Failed to Create Topic',
content: 'Please try again.',
});
}
}; };
const addFooter = ( const cancelled = () => {
<AddFooter> actions.clearShowAdd();
<div class="seal"> }
{ state.sealable && (
<>
<Switch checked={state.seal} onChange={actions.setSeal} size="small" />
<span class="sealText">Sealed Channel</span>
</>
)}
</div>
<Button key="back" onClick={actions.clearShowAdd}>Cancel</Button>
<Button key="save" type="primary" loading={state.busy} onClick={addChannel}>Save</Button>
</AddFooter>
);
return ( return (
<ChannelsWrapper> <ChannelsWrapper>
@ -69,9 +49,8 @@ export function Channels({ open, active }) {
<Button type="primary" icon={<CommentOutlined />} onClick={actions.setShowAdd}>New Topic</Button> <Button type="primary" icon={<CommentOutlined />} onClick={actions.setShowAdd}>New Topic</Button>
</div> </div>
)} )}
<Modal title="New Topic" centered visible={state.showAdd} footer={addFooter} <Modal title="New Topic" centered visible={state.showAdd} footer={null}>
onCancel={actions.clearShowAdd}> <AddChannel added={added} cancelled={cancelled} />
<AddChannel state={state} actions={actions} />
</Modal> </Modal>
</ChannelsWrapper> </ChannelsWrapper>
); );

View File

@ -87,22 +87,3 @@ export const ChannelsWrapper = styled.div`
} }
`; `;
export const AddFooter = styled.div`
width: 100%;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
.seal {
display: flex;
flex-grow: 1;
flex-direction: row;
align-items: center;
.sealText {
padding-left: 8px;
}
}
`

View File

@ -1,11 +1,30 @@
import { Input } from 'antd'; import { Input, Space, Modal, Switch, Button } from 'antd';
import { AddChannelWrapper } from './AddChannel.styled'; import { AddChannelWrapper, AddFooter } from './AddChannel.styled';
import { CardSelect } from '../../cardSelect/CardSelect'; import { CardSelect } from '../../cardSelect/CardSelect';
import { useAddChannel } from './useAddChannel.hook';
export function AddChannel({ state, actions }) { export function AddChannel({ added, cancelled }) {
const [ modal, modalContext ] = Modal.useModal();
const { state, actions } = useAddChannel();
const addChannel = async () => {
try {
const id = await actions.addChannel();
added(id);
}
catch(err) {
console.log(err);
modal.error({
title: 'Failed to Create Topic',
content: 'Please try again.',
});
}
}
return ( return (
<AddChannelWrapper> <AddChannelWrapper>
{ modalContext }
<Input placeholder="Subject (optional)" spellCheck="false" autocapitalize="word" <Input placeholder="Subject (optional)" spellCheck="false" autocapitalize="word"
value={state.subject} onChange={(e) => actions.setSubject(e.target.value)} /> value={state.subject} onChange={(e) => actions.setSubject(e.target.value)} />
<div class="members"> <div class="members">
@ -23,6 +42,20 @@ export function AddChannel({ state, actions }) {
unknown={0} unknown={0}
/> />
</div> </div>
<AddFooter>
<div class="seal">
{ state.sealable && (
<>
<Switch checked={state.seal} onChange={actions.setSeal} size="small" />
<span class="sealText">Sealed Channel</span>
</>
)}
</div>
<Space>
<Button key="back" onClick={cancelled}>Cancel</Button>
<Button key="save" type="primary" loading={state.busy} onClick={addChannel}>Save</Button>
</Space>
</AddFooter>
</AddChannelWrapper> </AddChannelWrapper>
); );
} }

View File

@ -23,4 +23,23 @@ export const AddChannelWrapper = styled.div`
} }
`; `;
export const AddFooter = styled.div`
width: 100%;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding-top: 8px;
.seal {
display: flex;
flex-grow: 1;
flex-direction: row;
align-items: center;
.sealText {
padding-left: 8px;
}
}
`

View File

@ -0,0 +1,105 @@
import { useContext, useState, useEffect } from 'react';
import { ChannelContext } from 'context/ChannelContext';
import { CardContext } from 'context/CardContext';
import { AccountContext } from 'context/AccountContext';
export function useAddChannel() {
const [state, setState] = useState({
sealable: false,
busy: false,
showAdd: false,
subject: null,
members: new Set(),
seal: false,
});
const card = useContext(CardContext);
const channel = useContext(ChannelContext);
const account = useContext(AccountContext);
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
}
useEffect(() => {
const { seal, sealKey } = account.state;
if (seal?.publicKey && sealKey?.public && sealKey?.private && seal.publicKey === sealKey.public) {
updateState({ seal: false, sealable: true });
}
else {
updateState({ seal: false, sealable: false });
}
}, [account.state]);
const actions = {
addChannel: async () => {
let conversation;
if (!state.busy) {
try {
updateState({ busy: true });
const cards = Array.from(state.members.values());
if (state.seal) {
const keys = [ account.state.sealKey.public ];
cards.forEach(id => {
keys.push(card.state.cards.get(id).data.cardProfile.seal);
});
throw new Error("TODO");
}
else {
conversation = await channel.actions.addChannel('superbasic', state.subject, cards);
}
updateState({ busy: false });
}
catch(err) {
console.log(err);
updateState({ busy: false });
throw new Error("failed to create new channel");
}
}
else {
throw new Error("operation in progress");
}
return conversation.id;
},
setSeal: (seal) => {
if (seal) {
const cards = Array.from(state.members.values());
const members = new Set(state.members);
cards.forEach(id => {
if (!(card.state.cards.get(id)?.data?.cardProfile?.seal)) {
members.delete(id);
}
});
updateState({ seal: true, members });
}
else {
updateState({ seal: false });
}
},
onMember: (string) => {
const members = new Set(state.members);
if (members.has(string)) {
members.delete(string);
}
else {
members.add(string);
}
updateState({ members });
},
setSubject: (subject) => {
updateState({ subject });
},
cardFilter: (card) => {
if (state.seal) {
return card?.data?.cardDetail?.status === 'connected' && card?.data?.cardProfile?.seal;
}
return card?.data?.cardDetail?.status === 'connected';
},
};
return { state, actions };
}

View File

@ -15,13 +15,7 @@ export function useChannels() {
const [state, setState] = useState({ const [state, setState] = useState({
display: null, display: null,
channels: [], channels: [],
sealable: false,
busy: false,
showAdd: false, showAdd: false,
subject: null,
members: new Set(),
seal: false,
}); });
const profile = useContext(ProfileContext); const profile = useContext(ProfileContext);
@ -172,16 +166,6 @@ export function useChannels() {
item.topicRevision = channelValue.data.topicRevision; item.topicRevision = channelValue.data.topicRevision;
}; };
useEffect(() => {
const { seal, sealKey } = account.state;
if (seal?.publicKey && sealKey?.public && sealKey?.private && seal.publicKey === sealKey.public) {
updateState({ seal: false, sealable: true });
}
else {
updateState({ seal: false, sealable: false });
}
}, [account.state]);
useEffect(() => { useEffect(() => {
const login = store.state['login:timestamp']; const login = store.state['login:timestamp'];
const conversations = new Map(); const conversations = new Map();
@ -278,78 +262,15 @@ export function useChannels() {
}, [viewport]); }, [viewport]);
const actions = { const actions = {
addChannel: async () => {
let added;
if (!state.busy) {
try {
updateState({ busy: true });
const cards = Array.from(state.members.values());
if (state.seal) {
const keys = [ account.state.sealKey.public ];
cards.forEach(id => {
keys.push(card.state.cards.get(id).data.cardProfile.seal);
});
added = await channel.actions.addSealedChannel(cards, state.subject, keys);
}
else {
added = await channel.actions.addBasicChannel(cards, state.subject);
}
updateState({ busy: false });
}
catch(err) {
console.log(err);
updateState({ busy: false });
throw new Error("failed to create new channel");
}
}
else {
throw new Error("operation in progress");
}
return added.id;
},
setSeal: (seal) => {
if (seal) {
const cards = Array.from(state.members.values());
const members = new Set(state.members);
cards.forEach(id => {
if (!(card.state.cards.get(id)?.data?.cardProfile?.seal)) {
members.delete(id);
}
});
updateState({ seal: true, members });
}
else {
updateState({ seal: false });
}
},
onFilter: (value) => { onFilter: (value) => {
setFilter(value?.toUpperCase()); setFilter(value?.toUpperCase());
}, },
setShowAdd: () => { setShowAdd: () => {
updateState({ showAdd: true, seal: false, members: new Set(), subject: null }); updateState({ showAdd: true });
}, },
clearShowAdd: () => { clearShowAdd: () => {
updateState({ showAdd: false }); updateState({ showAdd: false });
}, },
onMember: (string) => {
const members = new Set(state.members);
if (members.has(string)) {
members.delete(string);
}
else {
members.add(string);
}
updateState({ members });
},
setSubject: (subject) => {
updateState({ subject });
},
cardFilter: (card) => {
if (state.seal) {
return card?.data?.cardDetail?.status === 'connected' && card?.data?.cardProfile?.seal;
}
return card?.data?.cardDetail?.status === 'connected';
},
}; };
return { state, actions }; return { state, actions };