mirror of
https://github.com/balzack/databag.git
synced 2025-02-15 04:59:16 +00:00
support editing channel details
This commit is contained in:
parent
1317c6f6d7
commit
2db3178510
@ -9,6 +9,9 @@ export function Logo({ url, width, height, radius, img }) {
|
|||||||
{ img === 'team' && (
|
{ img === 'team' && (
|
||||||
<img src={team} alt="direct logo" width={width} height={height} />
|
<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 === 'appstore' && (
|
||||||
<img src={appstore} alt="group logo" width={width} height={height} />
|
<img src={appstore} alt="group logo" width={width} height={height} />
|
||||||
)}
|
)}
|
||||||
|
@ -3,6 +3,7 @@ import { List } from 'antd';
|
|||||||
import { CardSelectWrapper } from './CardSelect.styled';
|
import { CardSelectWrapper } from './CardSelect.styled';
|
||||||
import { SelectItem } from './selectItem/SelectItem';
|
import { SelectItem } from './selectItem/SelectItem';
|
||||||
import { useCardSelect } from './useCardSelect.hook';
|
import { useCardSelect } from './useCardSelect.hook';
|
||||||
|
import { Logo } from 'logo/Logo';
|
||||||
|
|
||||||
export function CardSelect({ filter, unknown, select, selected, markup }) {
|
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} />
|
<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>
|
</CardSelectWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -5,4 +5,17 @@ export const CardSelectWrapper = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
|
.unknown {
|
||||||
|
height: 48px;
|
||||||
|
width: 100%;
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.message {
|
||||||
|
padding-left: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -13,11 +13,9 @@ export function Channels({ open }) {
|
|||||||
try {
|
try {
|
||||||
const id = await actions.addChannel();
|
const id = await actions.addChannel();
|
||||||
actions.clearShowAdd();
|
actions.clearShowAdd();
|
||||||
console.log(id);
|
|
||||||
open(id);
|
open(id);
|
||||||
}
|
}
|
||||||
catch(err) {
|
catch(err) {
|
||||||
console.log(err);
|
|
||||||
Modal.error({
|
Modal.error({
|
||||||
title: 'Failed to Create Channel',
|
title: 'Failed to Create Channel',
|
||||||
content: 'Please try again.',
|
content: 'Please try again.',
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { useState } from 'react';
|
import { Input } from 'antd';
|
||||||
import { Input, Select } from 'antd';
|
|
||||||
import { AddChannelWrapper } from './AddChannel.styled';
|
import { AddChannelWrapper } from './AddChannel.styled';
|
||||||
import { CardSelect } from '../../cardSelect/CardSelect';
|
import { CardSelect } from '../../cardSelect/CardSelect';
|
||||||
|
|
||||||
|
@ -1,16 +1,94 @@
|
|||||||
import { Space } from 'antd';
|
import { Space, Button, Modal } from 'antd';
|
||||||
import { DetailsWrapper } from './Details.styled';
|
import { DetailsWrapper, ModalFooter } from './Details.styled';
|
||||||
import { DoubleRightOutlined } from '@ant-design/icons';
|
import { DoubleRightOutlined } from '@ant-design/icons';
|
||||||
import { useDetails } from './useDetails.hook';
|
import { useDetails } from './useDetails.hook';
|
||||||
import { Logo } from 'logo/Logo';
|
import { Logo } from 'logo/Logo';
|
||||||
import { EditOutlined } from '@ant-design/icons';
|
import { EditOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
|
||||||
import { CardSelect } from '../cardSelect/CardSelect';
|
import { CardSelect } from '../cardSelect/CardSelect';
|
||||||
|
import { EditSubject } from './editSubject/EditSubject';
|
||||||
|
import { EditMembers } from './editMembers/EditMembers';
|
||||||
|
|
||||||
export function Details({ cardId, channelId, closeDetails, closeConversation, openContact }) {
|
export function Details({ cardId, channelId, closeDetails, closeConversation, openContact }) {
|
||||||
|
|
||||||
const { state, actions } = useDetails(cardId, channelId);
|
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 (
|
return (
|
||||||
<DetailsWrapper>
|
<DetailsWrapper>
|
||||||
@ -20,14 +98,14 @@ console.log(state.contacts);
|
|||||||
<DoubleRightOutlined />
|
<DoubleRightOutlined />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content" onClick={closeDetails}>
|
<div class="content">
|
||||||
<div class="description">
|
<div class="description">
|
||||||
<div class="logo">
|
<div class="logo">
|
||||||
<Logo width={72} height={72} radius={4} img={state.img} />
|
<Logo width={72} height={72} radius={4} img={state.img} />
|
||||||
</div>
|
</div>
|
||||||
<div class="stats">
|
<div class="stats">
|
||||||
{ state.host && (
|
{ state.host && (
|
||||||
<div class="subject edit">
|
<div class="subject edit" onClick={actions.setEditSubject}>
|
||||||
<Space>
|
<Space>
|
||||||
<div>{ state.subject }</div>
|
<div>{ state.subject }</div>
|
||||||
<EditOutlined />
|
<EditOutlined />
|
||||||
@ -47,27 +125,33 @@ console.log(state.contacts);
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{ state.host && (
|
{ state.host && (
|
||||||
<div class="button">Delete Channel</div>
|
<div class="button" onClick={deleteChannel}>Delete Channel</div>
|
||||||
)}
|
)}
|
||||||
{ state.host && (
|
{ state.host && (
|
||||||
<div class="button">Edit Membership</div>
|
<div class="button" onClick={actions.setEditMembers}>Edit Membership</div>
|
||||||
)}
|
)}
|
||||||
{ !state.host && (
|
{ !state.host && (
|
||||||
<div class="button">Leave Channel</div>
|
<div class="button" onClick={leaveChannel}>Leave Channel</div>
|
||||||
)}
|
)}
|
||||||
<div class="label">Members</div>
|
<div class="label">Members</div>
|
||||||
<div class="members">
|
<div class="members">
|
||||||
<CardSelect filter={(item) => {
|
<CardSelect filter={(item) => {
|
||||||
console.log("CHECK: ", item.id, state.contacts);
|
|
||||||
if(state.contacts.includes(item.id)) {
|
if(state.contacts.includes(item.id)) {
|
||||||
console.log("YES");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}} unknown={0}
|
}} unknown={state.unknown}
|
||||||
markup={cardId} />
|
markup={cardId} />
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</DetailsWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
`
|
||||||
|
18
net/web/src/session/details/editMembers/EditMembers.jsx
Normal file
18
net/web/src/session/details/editMembers/EditMembers.jsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -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};
|
||||||
|
}
|
||||||
|
|
||||||
|
`
|
13
net/web/src/session/details/editSubject/EditSubject.jsx
Normal file
13
net/web/src/session/details/editSubject/EditSubject.jsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
`
|
@ -9,10 +9,15 @@ export function useDetails(cardId, channelId) {
|
|||||||
img: null,
|
img: null,
|
||||||
subject: null,
|
subject: null,
|
||||||
server: null,
|
server: null,
|
||||||
startedDate: null,
|
started: null,
|
||||||
startedTime: null,
|
|
||||||
host: null,
|
host: null,
|
||||||
contacts: [],
|
contacts: [],
|
||||||
|
members: new Set(),
|
||||||
|
editSubject: false,
|
||||||
|
editMembers: false,
|
||||||
|
busy: false,
|
||||||
|
subjectUpdate: null,
|
||||||
|
unknown: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
const card = useContext(CardContext);
|
const card = useContext(CardContext);
|
||||||
@ -23,7 +28,7 @@ export function useDetails(cardId, channelId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let img, subject, host, started;
|
let img, subject, host, started, contacts
|
||||||
let chan;
|
let chan;
|
||||||
if (cardId) {
|
if (cardId) {
|
||||||
const cardChan = card.state.cards.get(cardId);
|
const cardChan = card.state.cards.get(cardId);
|
||||||
@ -67,11 +72,72 @@ export function useDetails(cardId, channelId) {
|
|||||||
host = true;
|
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]);
|
}, [cardId, channelId, card, channel]);
|
||||||
|
|
||||||
const actions = {
|
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 };
|
return { state, actions };
|
||||||
|
Loading…
Reference in New Issue
Block a user