mirror of
https://github.com/balzack/databag.git
synced 2025-02-15 13:09:17 +00:00
rendering contact profile
This commit is contained in:
parent
8401a2295a
commit
bdf4df5844
@ -27,6 +27,7 @@ export function useViewportContext() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
setTimeout(handleResize, 1000); //cludge for my mobile browser
|
||||||
handleResize();
|
handleResize();
|
||||||
window.addEventListener('resize', handleResize);
|
window.addEventListener('resize', handleResize);
|
||||||
window.addEventListener('orientationchange', handleResize);
|
window.addEventListener('orientationchange', handleResize);
|
||||||
|
@ -3,7 +3,6 @@ import { Drawer } from 'antd';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { SessionWrapper } from './Session.styled';
|
import { SessionWrapper } from './Session.styled';
|
||||||
import { AppContext } from 'context/AppContext';
|
import { AppContext } from 'context/AppContext';
|
||||||
import { ViewportContext } from 'context/ViewportContext';
|
|
||||||
import { useSession } from './useSession.hook';
|
import { useSession } from './useSession.hook';
|
||||||
import { Conversation } from './conversation/Conversation';
|
import { Conversation } from './conversation/Conversation';
|
||||||
import { Details } from './details/Details';
|
import { Details } from './details/Details';
|
||||||
@ -21,7 +20,6 @@ export function Session() {
|
|||||||
|
|
||||||
const { state, actions } = useSession();
|
const { state, actions } = useSession();
|
||||||
const app = useContext(AppContext);
|
const app = useContext(AppContext);
|
||||||
const viewport = useContext(ViewportContext);
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -60,7 +58,7 @@ console.log(state);
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SessionWrapper>
|
<SessionWrapper>
|
||||||
{ (viewport.state.display === 'xlarge') && (
|
{ (state.display === 'xlarge') && (
|
||||||
<div class="desktop-layout noselect">
|
<div class="desktop-layout noselect">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<Identity openAccount={openAccount} openCards={openCards} cardUpdated={state.cardUpdated} />
|
<Identity openAccount={openAccount} openCards={openCards} cardUpdated={state.cardUpdated} />
|
||||||
@ -95,8 +93,9 @@ console.log(state);
|
|||||||
{ state.cards && (
|
{ state.cards && (
|
||||||
<div class="reframe">
|
<div class="reframe">
|
||||||
<Cards closeCards={closeCards} openContact={actions.openContact} openListing={actions.openListing} />
|
<Cards closeCards={closeCards} openContact={actions.openContact} openListing={actions.openListing} />
|
||||||
<Drawer title="Basic Drawer" placement="bottom" closable={false} visible={state.listing}
|
<Drawer bodyStyle={{ padding: 0 }} placement="bottom" closable={false} visible={state.listing}
|
||||||
onClose={actions.closeListing} getContainer={false} height={'80%'} style={{ position: 'absolute', overflow: 'hidden' }}>
|
onClose={actions.closeListing} getContainer={false} height={'80%'}
|
||||||
|
style={{ position: 'absolute', overflow: 'hidden' }}>
|
||||||
<Listing openContact={actions.openContact} />
|
<Listing openContact={actions.openContact} />
|
||||||
</Drawer>
|
</Drawer>
|
||||||
</div>
|
</div>
|
||||||
@ -109,7 +108,7 @@ console.log(state);
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{ (viewport.state.display === 'large' || viewport.state.display === 'medium') && (
|
{ (state.display === 'large' || state.display === 'medium') && (
|
||||||
<div class="tablet-layout noselect">
|
<div class="tablet-layout noselect">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<Identity openAccount={actions.openProfile} openCards={actions.openCards} cardUpdated={state.cardUpdated} />
|
<Identity openAccount={actions.openProfile} openCards={actions.openCards} cardUpdated={state.cardUpdated} />
|
||||||
@ -129,17 +128,18 @@ console.log(state);
|
|||||||
<Details cardId={state.cardId} conversationId={state.conversationId} />
|
<Details cardId={state.cardId} conversationId={state.conversationId} />
|
||||||
)}
|
)}
|
||||||
</Drawer>
|
</Drawer>
|
||||||
<Drawer bodyStyle={{ padding: 0 }} width={'33%'} closable={false} onClose={actions.closeCards} visible={state.cards} zIndex={20} push={state.contact}>
|
<Drawer bodyStyle={{ padding: 0 }} width={'33%'} closable={false} onClose={closeCards} visible={state.cards} zIndex={20} push={state.contact}>
|
||||||
{ state.cards && (
|
{ state.cards && (
|
||||||
<Cards closeCards={closeCards} openContact={actions.openContact} openListing={actions.openListing} />
|
<Cards closeCards={closeCards} openContact={actions.openContact} openListing={actions.openListing} />
|
||||||
)}
|
)}
|
||||||
<Drawer title="Basic Drawer" placement="bottom" closable={false} visible={state.listing}
|
<Drawer bodyStyle={{ padding: 0 }} placement="bottom" closable={false} visible={state.listing}
|
||||||
onClose={actions.closeListing} getContainer={false} height={'80%'} style={{ overflow: 'hidden', position: 'absolute' }}>
|
onClose={actions.closeListing} getContainer={false} height={'80%'}
|
||||||
|
style={{ overflow: 'hidden', position: 'absolute' }}>
|
||||||
<Listing openContact={actions.openContact} />
|
<Listing openContact={actions.openContact} />
|
||||||
</Drawer>
|
</Drawer>
|
||||||
<Drawer bodyStyle={{ padding: 0 }} width={'33%'} closable={false} onClose={actions.closeContact} visible={state.contact} zIndex={30}>
|
<Drawer bodyStyle={{ padding: 0 }} width={'33%'} closable={false} onClose={actions.closeContact} visible={state.contact} zIndex={30}>
|
||||||
{ state.contact && (
|
{ state.contact && (
|
||||||
<Contact close={actions.closeContact} guid={state.contactGuid} node={state.contactNode} />
|
<Contact close={actions.closeContact} guid={state.contactGuid} listing={state.contactListing} />
|
||||||
)}
|
)}
|
||||||
</Drawer>
|
</Drawer>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
@ -151,7 +151,7 @@ console.log(state);
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{ (viewport.state.display === 'small') && (
|
{ (state.display === 'small') && (
|
||||||
<div class="mobile-layout noselect">
|
<div class="mobile-layout noselect">
|
||||||
<div class="top">
|
<div class="top">
|
||||||
<div class="reframe">
|
<div class="reframe">
|
||||||
@ -179,7 +179,7 @@ console.log(state);
|
|||||||
)}
|
)}
|
||||||
{ state.contact && (
|
{ state.contact && (
|
||||||
<div class="reframe">
|
<div class="reframe">
|
||||||
<Contact close={actions.closeContact} guid={state.contactGuid} node={state.contactNode} />
|
<Contact close={actions.closeContact} guid={state.contactGuid} listing={state.contactListing} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{ (state.profile || state.account) && (
|
{ (state.profile || state.account) && (
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Drawer, Input, List } from 'antd';
|
import { Drawer, Input, List } from 'antd';
|
||||||
import { CardsWrapper } from './Cards.styled';
|
import { CardsWrapper } from './Cards.styled';
|
||||||
import { SortAscendingOutlined, DoubleRightOutlined, UserOutlined, SearchOutlined } from '@ant-design/icons';
|
import { SortAscendingOutlined, UpOutlined, DoubleRightOutlined, UserOutlined, SearchOutlined } from '@ant-design/icons';
|
||||||
import { useCards } from './useCards.hook';
|
import { useCards } from './useCards.hook';
|
||||||
import { CardItem } from './cardItem/CardItem';
|
import { CardItem } from './cardItem/CardItem';
|
||||||
|
|
||||||
@ -50,8 +50,8 @@ export function Cards({ closeCards, openContact, openListing }) {
|
|||||||
{ state.display !== 'small' && (
|
{ state.display !== 'small' && (
|
||||||
<div class="bar">
|
<div class="bar">
|
||||||
<div class="add" onClick={openListing}>
|
<div class="add" onClick={openListing}>
|
||||||
<UserOutlined />
|
<UpOutlined />
|
||||||
<div class="label">New Contact</div>
|
<div class="label">Find New Contact</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -86,8 +86,7 @@ export const CardsWrapper = styled.div`
|
|||||||
.add {
|
.add {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
background-color: ${Colors.primary};
|
color: ${Colors.primary};
|
||||||
color: ${Colors.white};
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding-left: 16px;
|
padding-left: 16px;
|
||||||
|
@ -21,7 +21,7 @@ export function CardItem({ item, open }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CardItemWrapper onClick={() => open(profile.guid, profile.node)}>
|
<CardItemWrapper onClick={() => open(profile.guid)}>
|
||||||
<Logo url={state.logo} width={32} height={32} radius={8} />
|
<Logo url={state.logo} width={32} height={32} radius={8} />
|
||||||
<div class="details">
|
<div class="details">
|
||||||
<div class="name">{ profile?.name }</div>
|
<div class="name">{ profile?.name }</div>
|
||||||
|
@ -1,6 +1,63 @@
|
|||||||
import { ContactWrapper } from './Contact.styled';
|
import { ContactWrapper } from './Contact.styled';
|
||||||
|
import { useContact } from './useContact.hook';
|
||||||
|
import { Logo } from 'logo/Logo';
|
||||||
|
import { RightOutlined, BookOutlined, EnvironmentOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
export function Contact({ close, guid, node }) {
|
export function Contact({ close, guid, listing }) {
|
||||||
return <ContactWrapper />;
|
|
||||||
|
const { state, actions } = useContact(guid, listing);
|
||||||
|
|
||||||
|
const Image = (
|
||||||
|
<div class="logo">
|
||||||
|
<Logo url={state.logo} width={'100%'} radius={8} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Details = (
|
||||||
|
<div class="details">
|
||||||
|
<div class="name">
|
||||||
|
<div class="data">{ state.name }</div>
|
||||||
|
</div>
|
||||||
|
<div class="location">
|
||||||
|
<EnvironmentOutlined />
|
||||||
|
<div class="data">{ state.location }</div>
|
||||||
|
</div>
|
||||||
|
<div class="description">
|
||||||
|
<BookOutlined />
|
||||||
|
<div class="data">{ state.description }</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ContactWrapper>
|
||||||
|
{ state.init && state.display === 'xlarge' && (
|
||||||
|
<>
|
||||||
|
<div class="header">
|
||||||
|
<div class="handle">{ state.handle }</div>
|
||||||
|
<div class="close" onClick={close}>
|
||||||
|
<RightOutlined />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
{ Image }
|
||||||
|
{ Details }
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{ state.init && state.display !== 'xlarge' && (
|
||||||
|
<div class="view">
|
||||||
|
<div class="title">{ state.handle }</div>
|
||||||
|
<div class="section">Contact Profile</div>
|
||||||
|
<div class="controls">
|
||||||
|
{ Image }
|
||||||
|
{ Details }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</ContactWrapper>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,5 +7,115 @@ export const ContactWrapper = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background-color: ${Colors.profileForm};
|
background-color: ${Colors.profileForm};
|
||||||
|
|
||||||
|
|
||||||
|
.header {
|
||||||
|
margin-left: 16px;
|
||||||
|
margin-right: 16px;
|
||||||
|
height: 48px;
|
||||||
|
border-bottom: 1px solid ${Colors.profileDivider};
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
.handle {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
flex-grow: 1;
|
||||||
|
padding-left: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
font-size: 16px;
|
||||||
|
color: ${Colors.primary};
|
||||||
|
cursor: pointer;
|
||||||
|
padding-right: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
min-height: 0;
|
||||||
|
width: 100%;
|
||||||
|
overflow: scroll;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 32px;
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
position: relative;
|
||||||
|
width: 20vw;
|
||||||
|
margin-right: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.view {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: scroll;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
position: relative;
|
||||||
|
width: 80%;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
width: 100%;
|
||||||
|
color: ${Colors.grey};
|
||||||
|
padding-top: 16px;
|
||||||
|
font-size: 12px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.details {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.name {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.data {
|
||||||
|
padding-right: 8px;
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.location {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
|
||||||
|
.data {
|
||||||
|
padding-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
|
||||||
|
.data {
|
||||||
|
padding-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
70
net/web/src/session/contact/useContact.hook.js
Normal file
70
net/web/src/session/contact/useContact.hook.js
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import { useContext, useState, useEffect } from 'react';
|
||||||
|
import { CardContext } from 'context/CardContext';
|
||||||
|
import { ProfileContext } from 'context/ProfileContext';
|
||||||
|
import { ViewportContext } from 'context/ViewportContext';
|
||||||
|
import { getListingImageUrl } from 'api/getListingImageUrl';
|
||||||
|
|
||||||
|
export function useContact(guid, listing) {
|
||||||
|
|
||||||
|
const [state, setState] = useState({
|
||||||
|
logo: null,
|
||||||
|
name: null,
|
||||||
|
location: null,
|
||||||
|
description: null,
|
||||||
|
handle: null,
|
||||||
|
removed: false,
|
||||||
|
init: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const card = useContext(CardContext);
|
||||||
|
const profile = useContext(ProfileContext);
|
||||||
|
const viewport = useContext(ViewportContext);
|
||||||
|
|
||||||
|
const updateState = (value) => {
|
||||||
|
setState((s) => ({ ...s, ...value }));
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let logo, name, location, description, handle;
|
||||||
|
let contact = card.actions.getCardByGuid(guid);
|
||||||
|
if (contact) {
|
||||||
|
let cardProfile = contact?.data?.cardProfile;
|
||||||
|
if (cardProfile.node != profile.state.profile.node) {
|
||||||
|
handle = cardProfile.handle + '@' + cardProfile.node;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
handle = cardProfile.handle;
|
||||||
|
}
|
||||||
|
logo = card.actions.getImageUrl(contact.id);
|
||||||
|
name = cardProfile.name;
|
||||||
|
location = cardProfile.location;
|
||||||
|
description = cardProfile.description;
|
||||||
|
}
|
||||||
|
else if (listing) {
|
||||||
|
if (listing.node != profile.state.profile.node) {
|
||||||
|
handle = listing.handle + '@' + listing.node;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
handle = listing.handle;
|
||||||
|
}
|
||||||
|
logo = listing.imageSet ? getListingImageUrl(listing.node, listing.guid) : null;
|
||||||
|
name = listing.name;
|
||||||
|
location = listing.location;
|
||||||
|
description = listing.description;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
updateState({ removed: true });
|
||||||
|
}
|
||||||
|
updateState({ init: true, logo, name, location, description, handle });
|
||||||
|
}, [card, guid, listing]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateState({ display: viewport.state.display });
|
||||||
|
}, [viewport]);
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
};
|
||||||
|
|
||||||
|
return { state, actions };
|
||||||
|
}
|
||||||
|
|
@ -35,6 +35,7 @@ export const IdentityWrapper = styled.div`
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
line-height: 16px;
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
@ -1,4 +1,45 @@
|
|||||||
|
import { Modal, Button, Drawer, Input, List } from 'antd';
|
||||||
|
import { ListingWrapper } from './Listing.styled';
|
||||||
|
import { DatabaseOutlined, SearchOutlined } from '@ant-design/icons';
|
||||||
|
import { useListing } from './useListing.hook';
|
||||||
|
import { ListingItem } from './listingItem/ListingItem';
|
||||||
|
|
||||||
export function Listing({ openContact }) {
|
export function Listing({ openContact }) {
|
||||||
return <div onClick={() => openContact('asdf', 'qwer')}>LISTING</div>
|
|
||||||
|
const { state, actions } = useListing();
|
||||||
|
|
||||||
|
const getListing = async () => {
|
||||||
|
try {
|
||||||
|
await actions.getListing();
|
||||||
|
}
|
||||||
|
catch(err) {
|
||||||
|
console.log(err);
|
||||||
|
Modal.error({
|
||||||
|
title: 'Communication Error',
|
||||||
|
content: 'Please confirm your server name.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ListingWrapper>
|
||||||
|
<div class="search">
|
||||||
|
<div class="node">
|
||||||
|
<Input bordered={false} allowClear={true} placeholder="Server"
|
||||||
|
prefix={<DatabaseOutlined />} value={state.node} spellCheck="false"
|
||||||
|
disabled={state.disabled} onChange={(e) => actions.onNode(e.target.value)} />
|
||||||
|
</div>
|
||||||
|
<div class="inline">
|
||||||
|
<Button type="text" icon={<SearchOutlined />} loading={state.busy} onClick={getListing}></Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="view">
|
||||||
|
<List local={{ emptyText: '' }} itemLayout="horizontal" dataSource={state.contacts} gutter="0"
|
||||||
|
renderItem={item => (
|
||||||
|
<ListingItem item={item} node={state.node} open={openContact} />
|
||||||
|
)} />
|
||||||
|
</div>
|
||||||
|
</ListingWrapper>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
45
net/web/src/session/listing/Listing.styled.js
Normal file
45
net/web/src/session/listing/Listing.styled.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
import Colors from 'constants/Colors';
|
||||||
|
|
||||||
|
export const ListingWrapper = styled.div`
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: ${Colors.card};
|
||||||
|
|
||||||
|
.view {
|
||||||
|
min-height: 0;
|
||||||
|
overflow: scroll;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
border-bottom: 1px solid ${Colors.divider};
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
height: 48px;
|
||||||
|
padding-left: 16px;
|
||||||
|
padding-right: 16px;
|
||||||
|
padding-top: 8px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
|
||||||
|
.node {
|
||||||
|
border: 1px solid ${Colors.divider};
|
||||||
|
background-color: ${Colors.white};
|
||||||
|
border-radius: 8px;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline {
|
||||||
|
padding-left: 8px;
|
||||||
|
display: flex;
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
18
net/web/src/session/listing/listingItem/ListingItem.jsx
Normal file
18
net/web/src/session/listing/listingItem/ListingItem.jsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { ListingItemWrapper } from './ListingItem.styled';
|
||||||
|
import { useListingItem } from './useListingItem.hook';
|
||||||
|
import { Logo } from 'logo/Logo';
|
||||||
|
|
||||||
|
export function ListingItem({ item, node, open }) {
|
||||||
|
|
||||||
|
const { state, actions } = useListingItem(node, item);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ListingItemWrapper onClick={() => open(item.guid, item)}>
|
||||||
|
<Logo url={state.logo} width={32} height={32} radius={8} />
|
||||||
|
<div class="details">
|
||||||
|
<div class="name">{ state.name }</div>
|
||||||
|
<div class="handle">{ state.handle }</div>
|
||||||
|
</div>
|
||||||
|
</ListingItemWrapper>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
import Colors from 'constants/Colors';
|
||||||
|
|
||||||
|
export const ListingItemWrapper = styled.div`
|
||||||
|
height: 48px;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1px solid ${Colors.divider};
|
||||||
|
padding-left: 16px;
|
||||||
|
padding-right: 16px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: ${Colors.formHover};
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-left: 16px;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
|
.name {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.handle {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
@ -0,0 +1,21 @@
|
|||||||
|
import { useContext, useState, useEffect } from 'react';
|
||||||
|
import { getListingImageUrl } from 'api/getListingImageUrl';
|
||||||
|
|
||||||
|
export function useListingItem(server, item) {
|
||||||
|
|
||||||
|
const [state, setState] = useState({
|
||||||
|
logo: item.imageSet ? getListingImageUrl(server, item.guid) : null,
|
||||||
|
name: item.name,
|
||||||
|
handle: item.handle,
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateState = (value) => {
|
||||||
|
setState((s) => ({ ...s, ...value }));
|
||||||
|
}
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
};
|
||||||
|
|
||||||
|
return { state, actions };
|
||||||
|
}
|
||||||
|
|
54
net/web/src/session/listing/useListing.hook.js
Normal file
54
net/web/src/session/listing/useListing.hook.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { useContext, useState, useEffect } from 'react';
|
||||||
|
import { ProfileContext } from 'context/ProfileContext';
|
||||||
|
import { getListing } from 'api/getListing';
|
||||||
|
|
||||||
|
export function useListing() {
|
||||||
|
|
||||||
|
const [state, setState] = useState({
|
||||||
|
contacts: [],
|
||||||
|
node: null,
|
||||||
|
busy: false,
|
||||||
|
disabled: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const profile = useContext(ProfileContext);
|
||||||
|
|
||||||
|
const updateState = (value) => {
|
||||||
|
setState((s) => ({ ...s, ...value }));
|
||||||
|
}
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
onNode: (value) => {
|
||||||
|
updateState({ node: value });
|
||||||
|
},
|
||||||
|
getListing: async () => {
|
||||||
|
updateState({ busy: true });
|
||||||
|
try {
|
||||||
|
let contacts = await getListing(state.node);
|
||||||
|
console.log(contacts);
|
||||||
|
let filtered = contacts.filter(contact => (contact.guid !== profile.state.profile.guid));
|
||||||
|
console.log(filtered);
|
||||||
|
let sorted = filtered.sort((a, b) => {
|
||||||
|
if (a?.name < b?.name) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
|
console.log(sorted);
|
||||||
|
updateState({ busy: false, contacts: sorted });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
updateState({ busy: false });
|
||||||
|
throw new Error("failed to list contacts");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let node = profile?.state?.profile?.node;
|
||||||
|
updateState({ disabled: node == null || node == '', node });
|
||||||
|
}, [profile]);
|
||||||
|
|
||||||
|
return { state, actions };
|
||||||
|
}
|
@ -1,14 +1,14 @@
|
|||||||
import { useContext, useState, useEffect, useRef } from 'react';
|
import { useContext, useState, useEffect, useRef } from 'react';
|
||||||
import { CardContext } from 'context/CardContext';
|
import { CardContext } from 'context/CardContext';
|
||||||
import { StoreContext } from 'context/StoreContext';
|
import { StoreContext } from 'context/StoreContext';
|
||||||
|
import { ViewportContext } from 'context/ViewportContext';
|
||||||
|
|
||||||
export function useSession() {
|
export function useSession() {
|
||||||
|
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
cardUpdated: false,
|
cardUpdated: false,
|
||||||
contactGuid: null,
|
contactGuid: null,
|
||||||
contactNode: null,
|
contactListing: null,
|
||||||
listingNode: null,
|
|
||||||
conversation: false,
|
conversation: false,
|
||||||
details: false,
|
details: false,
|
||||||
cards: false,
|
cards: false,
|
||||||
@ -20,6 +20,7 @@ export function useSession() {
|
|||||||
|
|
||||||
const card = useContext(CardContext);
|
const card = useContext(CardContext);
|
||||||
const store = useContext(StoreContext);
|
const store = useContext(StoreContext);
|
||||||
|
const viewport = useContext(ViewportContext);
|
||||||
|
|
||||||
const storeStatus = useRef(null);
|
const storeStatus = useRef(null);
|
||||||
const cardStatus = useRef(null);
|
const cardStatus = useRef(null);
|
||||||
@ -28,6 +29,10 @@ export function useSession() {
|
|||||||
setState((s) => ({ ...s, ...value }));
|
setState((s) => ({ ...s, ...value }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateState({ display: viewport.state.display });
|
||||||
|
}, [viewport]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const contacts = Array.from(card.state.cards.values());
|
const contacts = Array.from(card.state.cards.values());
|
||||||
|
|
||||||
@ -62,14 +67,14 @@ export function useSession() {
|
|||||||
closeCards: () => {
|
closeCards: () => {
|
||||||
updateState({ cards: false });
|
updateState({ cards: false });
|
||||||
},
|
},
|
||||||
openListing: (listingNode) => {
|
openListing: () => {
|
||||||
updateState({ listing: true, listingNode });
|
updateState({ listing: true });
|
||||||
},
|
},
|
||||||
closeListing: () => {
|
closeListing: () => {
|
||||||
updateState({ listing: false });
|
updateState({ listing: false });
|
||||||
},
|
},
|
||||||
openContact: (contactGuid, contactNode) => {
|
openContact: (contactGuid, contactListing) => {
|
||||||
updateState({ contact: true, contactGuid, contactNode });
|
updateState({ contact: true, contactGuid, contactListing });
|
||||||
},
|
},
|
||||||
closeContact: () => {
|
closeContact: () => {
|
||||||
updateState({ contact: false });
|
updateState({ contact: false });
|
||||||
|
Loading…
Reference in New Issue
Block a user