support editing channel details

This commit is contained in:
Roland Osborne 2022-08-20 23:10:22 -07:00
parent 1317c6f6d7
commit 2db3178510
12 changed files with 258 additions and 21 deletions

View File

@ -9,6 +9,9 @@ export function Logo({ url, width, height, radius, img }) {
{ img === 'team' && (
<img src={team} alt="direct logo" width={width} height={height} />
)}
{ img === 'avatar' && (
<img src={avatar} alt="anonymous logo" width={width} height={height} />
)}
{ img === 'appstore' && (
<img src={appstore} alt="group logo" width={width} height={height} />
)}

View File

@ -3,6 +3,7 @@ import { List } from 'antd';
import { CardSelectWrapper } from './CardSelect.styled';
import { SelectItem } from './selectItem/SelectItem';
import { useCardSelect } from './useCardSelect.hook';
import { Logo } from 'logo/Logo';
export function CardSelect({ filter, unknown, select, selected, markup }) {
@ -15,6 +16,14 @@ export function CardSelect({ filter, unknown, select, selected, markup }) {
<SelectItem item={item} select={select} selected={selected} markup={markup} />
)}
/>
{ unknown > 0 && (
<div class="unknown">
<Logo img="avatar" width={32} height={32} radius={8} />
<div class="message">
<span>+ { unknown } unknown</span>
</div>
</div>
)}
</CardSelectWrapper>
);
}

View File

@ -5,4 +5,17 @@ export const CardSelectWrapper = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
.unknown {
height: 48px;
width: 100%;
padding-left: 8px;
padding-right: 8px;
display: flex;
align-items: center;
.message {
padding-left: 16px;
}
}
`;

View File

@ -13,11 +13,9 @@ export function Channels({ open }) {
try {
const id = await actions.addChannel();
actions.clearShowAdd();
console.log(id);
open(id);
}
catch(err) {
console.log(err);
Modal.error({
title: 'Failed to Create Channel',
content: 'Please try again.',

View File

@ -1,5 +1,4 @@
import { useState } from 'react';
import { Input, Select } from 'antd';
import { Input } from 'antd';
import { AddChannelWrapper } from './AddChannel.styled';
import { CardSelect } from '../../cardSelect/CardSelect';

View File

@ -1,16 +1,94 @@
import { Space } from 'antd';
import { DetailsWrapper } from './Details.styled';
import { Space, Button, Modal } from 'antd';
import { DetailsWrapper, ModalFooter } from './Details.styled';
import { DoubleRightOutlined } from '@ant-design/icons';
import { useDetails } from './useDetails.hook';
import { Logo } from 'logo/Logo';
import { EditOutlined } from '@ant-design/icons';
import { EditOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
import { CardSelect } from '../cardSelect/CardSelect';
import { EditSubject } from './editSubject/EditSubject';
import { EditMembers } from './editMembers/EditMembers';
export function Details({ cardId, channelId, closeDetails, closeConversation, openContact }) {
const { state, actions } = useDetails(cardId, channelId);
console.log(state.contacts);
const deleteChannel = async () => {
Modal.confirm({
title: 'Are you sure you want to delete the channel?',
icon: <ExclamationCircleOutlined />,
okText: "Yes, Delete",
cancelText: "No, Cancel",
onOk() {
applyDeleteChannel();
},
onCancel() {},
});
}
const applyDeleteChannel = async () => {
try {
await actions.deleteChannel();
closeConversation();
}
catch(err) {
Modal.error({
title: 'Failed to Delete Channel',
content: 'Please try again.',
});
}
}
const leaveChannel = async () => {
Modal.confirm({
title: 'Are you sure you want to leave the channel?',
icon: <ExclamationCircleOutlined />,
okText: "Yes, Leave",
cancelText: "No, Cancel",
onOk() {
applyLeaveChannel();
},
onCancel() {},
});
}
const applyLeaveChannel = async () => {
try {
await actions.leaveChannel();
closeConversation();
}
catch(err) {
Modal.error({
title: 'Failed to Leave Channel',
content: 'Please try again.',
});
}
}
const saveSubject = async () => {
try {
const id = await actions.setSubject();
actions.clearEditSubject();
}
catch(err) {
Modal.error({
title: 'Failed to Update Subject',
content: 'Please try again.',
});
}
};
const editSubjectFooter = (
<ModalFooter>
<Button key="back" onClick={actions.clearEditSubject}>Cancel</Button>
<Button key="save" type="primary" onClick={saveSubject} loading={state.busy}>Save</Button>
</ModalFooter>
);
const editMembersFooter = (
<ModalFooter>
<Button key="back" onClick={actions.clearEditMembers}>Done</Button>
</ModalFooter>
);
return (
<DetailsWrapper>
@ -20,14 +98,14 @@ console.log(state.contacts);
<DoubleRightOutlined />
</div>
</div>
<div class="content" onClick={closeDetails}>
<div class="content">
<div class="description">
<div class="logo">
<Logo width={72} height={72} radius={4} img={state.img} />
</div>
<div class="stats">
{ state.host && (
<div class="subject edit">
<div class="subject edit" onClick={actions.setEditSubject}>
<Space>
<div>{ state.subject }</div>
<EditOutlined />
@ -47,27 +125,33 @@ console.log(state.contacts);
</div>
</div>
{ state.host && (
<div class="button">Delete Channel</div>
<div class="button" onClick={deleteChannel}>Delete Channel</div>
)}
{ state.host && (
<div class="button">Edit Membership</div>
<div class="button" onClick={actions.setEditMembers}>Edit Membership</div>
)}
{ !state.host && (
<div class="button">Leave Channel</div>
<div class="button" onClick={leaveChannel}>Leave Channel</div>
)}
<div class="label">Members</div>
<div class="members">
<CardSelect filter={(item) => {
console.log("CHECK: ", item.id, state.contacts);
if(state.contacts.includes(item.id)) {
console.log("YES");
return true;
}
return false;
}} unknown={0}
}} unknown={state.unknown}
markup={cardId} />
</div>
</div>
<Modal title="Edit Subject" centered visible={state.editSubject} footer={editSubjectFooter}
onCancel={actions.clearEditSubject}>
<EditSubject state={state} actions={actions} />
</Modal>
<Modal title="Edit Members" centered visible={state.editMembers} footer={editMembersFooter}
onCancel={actions.clearEditMembers}>
<EditMembers state={state} actions={actions} />
</Modal>
</DetailsWrapper>
);
}

View File

@ -112,3 +112,11 @@ export const DetailsWrapper = styled.div`
}
}
`
export const ModalFooter = styled.div`
width: 100%;
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
`

View File

@ -0,0 +1,18 @@
import { EditMembersWrapper } from './EditMembers.styled';
import { CardSelect } from '../../cardSelect/CardSelect';
export function EditMembers({ state, actions }) {
return (
<EditMembersWrapper>
<div class="list">
<CardSelect
select={actions.onMember}
selected={state.members}
filter={(card) => card?.data?.cardDetail?.status === 'connected'}
/>
</div>
</EditMembersWrapper>
);
}

View File

@ -0,0 +1,18 @@
import styled from 'styled-components';
import Colors from 'constants/Colors';
export const EditMembersWrapper = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.list {
width: 100%;
min-height: 100px;
max-height: 200px;
overflow: scroll;
border: 1px solid ${Colors.divider};
}
`

View File

@ -0,0 +1,13 @@
import { Input } from 'antd';
import { EditSubjectWrapper } from './EditSubject.styled';
export function EditSubject({ state, actions }) {
return (
<EditSubjectWrapper>
<Input placeholder="Subject (optional)" spellCheck="false" autocapitalize="word"
defaultValue={state.subject} onChange={(e) => actions.setSubjectUpdate(e.target.value)} />
</EditSubjectWrapper>
);
}

View File

@ -0,0 +1,8 @@
import styled from 'styled-components';
export const EditSubjectWrapper = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
`

View File

@ -9,10 +9,15 @@ export function useDetails(cardId, channelId) {
img: null,
subject: null,
server: null,
startedDate: null,
startedTime: null,
started: null,
host: null,
contacts: [],
members: new Set(),
editSubject: false,
editMembers: false,
busy: false,
subjectUpdate: null,
unknown: 0,
});
const card = useContext(CardContext);
@ -23,7 +28,7 @@ export function useDetails(cardId, channelId) {
}
useEffect(() => {
let img, subject, host, started;
let img, subject, host, started, contacts
let chan;
if (cardId) {
const cardChan = card.state.cards.get(cardId);
@ -67,11 +72,72 @@ export function useDetails(cardId, channelId) {
host = true;
}
}
updateState({ img, subject, host, started,
contacts: chan.contacts.map((contact) => contact?.id) });
if (chan?.contacts ) {
contacts = chan.contacts.map((contact) => contact?.id);
}
else {
contacts = [];
}
let members = new Set(contacts);
let unknown = 0;
contacts.forEach(id => {
if (id == null) {
unknown++;
}
});
updateState({ img, subject, host, started, contacts, members, unknown });
}, [cardId, channelId, card, channel]);
const actions = {
setEditSubject: () => {
updateState({ editSubject: true });
},
clearEditSubject: () => {
updateState({ editSubject: false });
},
setSubjectUpdate: (subjectUpdate) => {
updateState({ subjectUpdate });
},
setSubject: async () => {
if (!state.busy) {
try {
updateState({ busy: true });
channel.actions.setChannelSubject(channelId, state.subjectUpdate);
updateState({ busy: false });
}
catch(err) {
console.log(err);
updateState({ busy: false });
throw new Error("set channel subject failed");
}
}
else {
throw new Error('operation in progress');
}
},
setEditMembers: () => {
updateState({ editMembers: true });
},
clearEditMembers: () => {
updateState({ editMembers: false });
},
onMember: async (card) => {
if (state.members.has(card)) {
channel.actions.clearChannelCard(channelId, card);
}
else {
channel.actions.setChannelCard(channelId, card);
}
},
deleteChannel: async () => {
await channel.actions.removeChannel(channelId);
},
leaveChannel: async () => {
await card.actions.removeChannel(cardId, channelId);
}
};
return { state, actions };