rendering basic channel list

This commit is contained in:
Roland Osborne 2022-04-09 01:36:03 -07:00
parent 3188385e75
commit 489239b10b
16 changed files with 440 additions and 33 deletions

View File

@ -3495,7 +3495,9 @@ components:
contacts:
$ref: '#/components/schemas/ChannelContacts'
members:
$ref: '#/components/schemas/ChannelMembers'
type: array
item:
type: string
ChannelContacts:
type: object

View File

@ -204,7 +204,7 @@ func getChannelModel(slot *store.ChannelSlot, showData bool, showList bool) *Cha
Created: slot.Channel.Created,
Updated: slot.Channel.Updated,
Contacts: contacts,
Members: &ChannelMembers{ members },
Members: members,
},
},
}

View File

@ -190,12 +190,7 @@ type ChannelDetail struct {
Contacts *ChannelContacts `json:"contacts,omitempty"`
Members *ChannelMembers `json:"members,omitempty"`
}
type ChannelMembers struct {
Members []string `json:"members,omitempty"`
Members []string `json:"members"`
}
type ChannelParams struct {

View File

@ -83,8 +83,8 @@ func TestChannelShare(t *testing.T) {
assert.NoError(t, ApiTestMsg(GetChannel, "GET", "/content/channels/{channelId}",
&params, nil, APP_TOKENCONTACT, set.B.A.Token, channel, nil))
assert.Equal(t, "channeldatatype", channel.Data.ChannelDetail.DataType)
assert.Equal(t, 1, len(channel.Data.ChannelDetail.Members.Members))
assert.Equal(t, set.B.Guid, channel.Data.ChannelDetail.Members.Members[0])
assert.Equal(t, 1, len(channel.Data.ChannelDetail.Members))
assert.Equal(t, set.B.Guid, channel.Data.ChannelDetail.Members[0])
assert.Nil(t, channel.Data.ChannelDetail.Contacts)
// get revision
@ -154,7 +154,7 @@ func TestChannelShare(t *testing.T) {
assert.NoError(t, ApiTestMsg(GetChannel, "GET", "/content/channels/{channelId}",
&params, nil, APP_TOKENCONTACT, set.C.A.Token, channel, nil))
assert.Equal(t, "channeldatatype", channel.Data.ChannelDetail.DataType)
assert.Equal(t, 2, len(channel.Data.ChannelDetail.Members.Members))
assert.Equal(t, 2, len(channel.Data.ChannelDetail.Members))
assert.Nil(t, channel.Data.ChannelDetail.Contacts)
// reset notification

View File

@ -0,0 +1,4 @@
export function getCardImageUrl(token, cardId, revision) {
return `/contact/cards/${cardId}/profile/image?agent=${token}&revision=${revision}`
}

View File

@ -223,6 +223,7 @@ export function useAppContext() {
profileRevision.current = null;
groupRevision.current = null;
cardRevision.current = null;
channels.current = new Map();
cards.current = new Map();
groups.current = new Map();
setState({});
@ -376,6 +377,7 @@ export function useAppContext() {
}, []);
if (!state) {
console.log("STATE IS NULL");
return {}
}
if (state.access === 'user') {

View File

@ -0,0 +1,25 @@
import React, { useEffect, useState } from 'react'
import { ChannelItemWrapper } from './ChannelItem.styled';
import { ChannelLogo } from './ChannelLogo/ChannelLogo';
import { ChannelLabel } from './ChannelLabel/ChannelLabel';
export function ChannelItem({ item }) {
// if 0 or 5+ render number in big border
// if 2 renber other in big border
// if 3 or 4 render others in small borders
// subject, hosting, username list, last msg, updated time, unread flag
const onSelect = () => {
console.log(item);
}
return (
<ChannelItemWrapper onClick={() => onSelect()}>
<ChannelLogo item={item} />
<ChannelLabel item={item} />
</ChannelItemWrapper>
)
}

View File

@ -0,0 +1,18 @@
import styled from 'styled-components';
import { List } from 'antd';
export const ChannelItemWrapper = styled(List.Item)`
padding-left: 16px;
padding-right: 16px;
padding-top: 4px;
padding-bottom: 4px;
height: 48px;
cursor: pointer;
display: flex;
flex-direction: row;
align-items: center;
&:hover {
background-color: #eeeeee;
}
`;

View File

@ -0,0 +1,72 @@
import React, { useState, useEffect } from 'react';
import { useChannelLabel } from './useChannelLabel.hook';
import { LabelWrapper } from './ChannelLabel.styled';
import { HomeOutlined, DatabaseOutlined } from '@ant-design/icons';
export function ChannelLabel({ item }) {
const [host, setHost] = useState(null);
const [members, setMembers] = useState([]);
const [subject, setSubject] = useState('');
const { state, actions } = useChannelLabel();
useEffect(() => {
let contacts = [];
if (item?.guid) {
setHost(actions.getCard(item.guid));
for (let member of item.channel.data.channelDetail.members) {
if (member != state.guid) {
contacts.push(actions.getCard(member));
}
}
setMembers(contacts);
}
else {
setHost(null);
for (let member of item.channel.data.channelDetail.members) {
contacts.push(actions.getCard(member));
}
setMembers(contacts);
}
if (item?.channel?.data?.channelDetail?.data) {
let data = JSON.parse(item.channel.data.channelDetail.data);
if (data.subject != '' && data.subject != null) {
setSubject(data.subject);
return
}
}
let names = ''
for (let contact of contacts) {
if (contact != null) {
if (names != '') {
names += ', ';
}
names += contact.data.cardProfile.handle;
}
}
if (names != '') {
names = '[' + names + ']';
}
setSubject(names)
}, [item, state]);
const Host = () => {
if (host) {
return (<div><DatabaseOutlined />&nbsp;{ host.data.cardProfile.handle }</div>)
}
return <HomeOutlined />
}
return (
<LabelWrapper>
<div class="subject">{subject}</div>
<div class="host"><Host /></div>
</LabelWrapper>
)
}

View File

@ -0,0 +1,22 @@
import styled from 'styled-components';
export const LabelWrapper = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
flex-grow: 1;
padding-right: 8px;
overflow: hidden;
color: #444444;
.subject {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: right;
}
.host {
text-align: right;
}
`;

View File

@ -0,0 +1,27 @@
import { useContext, useState, useEffect } from 'react';
import { AppContext } from '../../../../../../AppContext/AppContext';
import { getCardImageUrl } from '../../../../../../Api/getCardImageUrl';
export function useChannelLabel() {
const [state, setState] = useState({
guid: null
});
const app = useContext(AppContext);
const actions = {
getCard: app?.actions?.getCard,
};
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
}
useEffect(() => {
updateState({ guid: app?.state?.Data?.profile?.guid })
}, [app])
return { state, actions };
}

View File

@ -0,0 +1,136 @@
import React, { useEffect, useState, useContext } from 'react'
import { DisconnectOutlined, UserOutlined } from '@ant-design/icons';
import { LogoWrapper, Contact, Host, ChannelImage } from './ChannelLogo.styled';
import { useChannelLogo } from './useChannelLogo.hook';
export function ChannelLogo({ item }) {
const [home, setHome] = useState(false);
const [host, setHost] = useState(null);
const [members, setMembers] = useState([]);
const { state, actions } = useChannelLogo();
useEffect(() => {
if (item?.guid) {
setHome(false);
setHost(actions.getCard(item.guid));
let contacts = [];
for (let member of item.channel.data.channelDetail.members) {
if (member != state.guid) {
contacts.push(actions.getCard(member));
}
}
setMembers(contacts);
}
else {
setHome(true);
let contacts = [];
for (let member of item.channel.data.channelDetail.members) {
contacts.push(actions.getCard(member));
}
setMembers(contacts);
}
}, [item, state]);
const Logo = ({card}) => {
if (card?.data?.cardProfile?.imageSet) {
let imageUrl = actions.getCardImageUrl(card?.id, card?.revision);
return <ChannelImage src={ imageUrl } alt='' />
}
return <UserOutlined />
}
if (members.length == 0 && home) {
return (
<LogoWrapper>
<div class="container">
<div class="large contact"><DisconnectOutlined /></div>
</div>
</LogoWrapper>
)
}
else if (members.length == 0 && !home) {
return (
<LogoWrapper>
<div class="container">
<div class="large host"><Logo card={host} /></div>
</div>
</LogoWrapper>
)
}
else if (members.length == 1 && home) {
return (
<LogoWrapper>
<div class="container">
<div class="large contact"><Logo card={members[0]} /></div>
</div>
</LogoWrapper>
)
}
else if (members.length == 1 && !home) {
return (
<LogoWrapper>
<div class="grid">
<div class="medium host topleft"><Logo card={host} /></div>
<div class="medium contact bottomright"><Logo card={members[0]} /></div>
</div>
</LogoWrapper>
)
}
else if (members.length == 2 && home) {
return (
<LogoWrapper>
<div class="grid">
<div class="medium host topleft"><Logo card={members[0]} /></div>
<div class="medium contact bottomright"><Logo card={members[1]} /></div>
</div>
</LogoWrapper>
)
}
else if (members.length == 2 && !home) {
return (
<LogoWrapper>
<div class="grid">
<div class="small host topleft"><Logo card={host} /></div>
<div class="small contact topright"><Logo card={members[0]} /></div>
<div class="small contact bottom"><Logo card={members[1]} /></div>
</div>
</LogoWrapper>
)
}
else if (members.length == 3 && home) {
return (
<LogoWrapper>
<div class="grid">
<div class="small contact topleft"><Logo card={members[0]} /></div>
<div class="small contact topright"><Logo card={members[1]} /></div>
<div class="small contact bottom"><Logo card={members[2]} /></div>
</div>
</LogoWrapper>
)
}
else if (members.length > 2 && !home) {
return (
<LogoWrapper>
<div class="grid">
<div class="medium host topleft"><Logo card={host} /></div>
<div class="medium contact bottomright">{members.length}</div>
</div>
</LogoWrapper>
)
}
else if (members.length > 3 && home) {
return (
<LogoWrapper>
<div class="container">
<div class="large contact">{members.length}</div>
</div>
</LogoWrapper>
)
}
return <></>
}

View File

@ -0,0 +1,90 @@
import styled from 'styled-components';
export const LogoWrapper = styled.div`
height: 48px;
width: 48px;
display: flex;
flex-shrink: 0;
align-items: center;
justify-content: center;
.container {
display: flex;
align-items: center;
justify-content: center;
}
.grid {
position: relative;
margin-left: 2px;
margin-right: 2px;
width: 44px;
height: 40px;
}
.large {
border-radius: 18px;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
}
.medium {
border-radius: 16px;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
}
.small {
border-radius: 16px;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
}
.host {
overflow: hidden;
border: 2px solid #88cc88;
}
.contact {
overflow: hidden;
border: 1px solid #777777;
}
.topleft {
position: absolute;
top: 0px;
left: 0px;
}
.topright {
position: absolute;
top: 0px;
right: 0px;
}
.bottomright {
position: absolute;
bottom: 0px;
right: 0px;
}
.bottom {
position: absolute;
bottom: 0px;
left: 12px;
}
`;
export const ChannelImage = styled.img`
width: 100%;
height: 100%;
`;

View File

@ -0,0 +1,32 @@
import { useContext, useState, useEffect } from 'react';
import { AppContext } from '../../../../../../AppContext/AppContext';
import { getCardImageUrl } from '../../../../../../Api/getCardImageUrl';
export function useChannelLogo() {
const [state, setState] = useState({
guid: null
});
const app = useContext(AppContext);
const actions = {
getCardImageUrl: (cardId, revision) => {
if (app?.state?.token) {
return getCardImageUrl(app.state.token, cardId, revision)
}
return null;
},
getCard: app?.actions?.getCard,
};
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
}
useEffect(() => {
updateState({ guid: app?.state?.Data?.profile?.guid })
}, [app])
return { state, actions };
}

View File

@ -1,25 +1,20 @@
import React, { useEffect, useState } from 'react'
import { ChannelsWrapper, ChannelItem } from './Channels.styled';
import { ChannelsWrapper } from './Channels.styled';
import { List, Button, Select, Modal } from 'antd';
import { useChannels } from './useChannels.hook';
import { AddChannel } from './AddChannel/AddChannel';
import { ChannelItem } from './ChannelItem/ChannelItem';
export function Channels({ showAdd, setShowAdd }) {
const { state, actions } = useChannels();
console.log(state.channels);
const onStart = async () => {
if (await actions.addChannel()) {
setShowAdd(false);
}
}
const onSelect = (item) => {
console.log(item);
}
return (
<ChannelsWrapper>
<List
@ -28,8 +23,7 @@ console.log(state.channels);
dataSource={state.channels}
gutter="0"
renderItem={item => (
<ChannelItem onClick={() => onSelect(item)}>
</ChannelItem>
<ChannelItem item={item} />
)}
/>
<Modal title="Start Conversation" visible={showAdd} centered

View File

@ -5,20 +5,8 @@ export const ChannelsWrapper = styled.div`
position: relative;
height: calc(100vh - 127px);
width: 100%;
overflow: hidden;
overflow: auto;
text-align: center;
border-radius: 2px;
`;
export const ChannelItem = styled(List.Item)`
padding-left: 16px;
padding-right: 16px;
padding-top: 4px;
padding-bottom: 4px;
height: 64px;
cursor: pointer;
&:hover {
background-color: #eeeeee;
}
`;