adding dark mode to contact page

This commit is contained in:
Roland Osborne 2024-02-28 13:41:33 -08:00
parent d245081ecf
commit 5db44ebaff
15 changed files with 312 additions and 194 deletions

View File

@ -65,6 +65,7 @@ export const LightTheme = {
inputBorder: '#888888',
sectionBorder: '#bbbbbb',
headerBorder: '#aaaaaa',
drawerBorder: '#cccccc',
};
export const DarkTheme = {
@ -94,5 +95,6 @@ export const DarkTheme = {
inputBorder: '#aaaaaa',
sectionBorder: '#777777',
headerBorder: '#aaaaaa',
drawerBorder: '#444444',
};

View File

@ -52,6 +52,7 @@ export function useCardContext() {
};
const resyncCard = async (cardId) => {
let success = true;
if (!syncing.current) {
syncing.current = true;
@ -68,10 +69,12 @@ export function useCardContext() {
}
catch(err) {
console.log(err);
success = false;
}
syncing.current = false;
await sync();
}
return success;
}
const sync = async () => {
@ -359,7 +362,7 @@ export function useCardContext() {
await resync();
},
resyncCard: async (cardId) => {
await resyncCard(cardId);
return await resyncCard(cardId);
},
}

View File

@ -192,7 +192,7 @@ export function Session() {
</div>
)}
{ state.contact && (
<div class="reframe">
<div class="reframe base">
<Contact close={actions.closeContact} guid={state.contactGuid} listing={state.contactListing} />
</div>
)}
@ -212,9 +212,9 @@ export function Session() {
{ state.cards && (
<div class="reframe">
<Cards closeCards={closeCards} openContact={actions.openContact} openChannel={openConversation} openListing={actions.openListing} />
<Drawer bodyStyle={drawerStyle} visible={state.listing} closable={false}
onClose={actions.closeListing} getContainer={false} height={'100%'}
style={{ width: '80%', position: 'absolute', overflow: 'hidden' }}>
<Drawer bodyStyle={drawerStyle} visible={state.listing} closable={false} getContainer={false}
onClose={actions.closeListing} height={'100%'} width={'80%'}
style={{ position: 'absolute', overflow: 'hidden' }}>
<Listing closeListing={actions.closeListing} openContact={actions.openContact} />
</Drawer>
</div>
@ -318,7 +318,7 @@ export function Session() {
</div>
)}
{ state.contact && (
<div class="reframe">
<div class="reframe base">
<Contact close={actions.closeContact} guid={state.contactGuid} listing={state.contactListing} />
</div>
)}

View File

@ -239,10 +239,15 @@ export const SessionWrapper = styled.div`
display: flex;
flex-direction: column;
.base {
background-color: ${props => props.theme.frameArea};
}
.top {
flex-grow: 1;
position: relative;
}
.bottom {
height: 40px;
position: relative;

View File

@ -1,5 +1,5 @@
import { AccountWrapper } from './Account.styled';
import { RightOutlined } from '@ant-design/icons';
import { CloseOutlined } from '@ant-design/icons';
import { SettingOutlined } from '@ant-design/icons';
import { AccountAccess } from './profile/accountAccess/AccountAccess';
import { useAccount } from './useAccount.hook';
@ -13,7 +13,7 @@ export function Account({ closeAccount, openProfile }) {
<div className="header">
<div className="label">{state.strings.settings}</div>
<div className="dismiss" onClick={closeAccount}>
<RightOutlined />
<CloseOutlined />
</div>
</div>
<div className="content">

View File

@ -4,7 +4,7 @@ import { LogoutContent, ProfileWrapper, ProfileDetailsWrapper, ProfileImageWrapp
import { useProfile } from './useProfile.hook';
import { Logo } from 'logo/Logo';
import { AccountAccess } from './accountAccess/AccountAccess';
import { LogoutOutlined, RightOutlined, EditOutlined, BookOutlined, EnvironmentOutlined } from '@ant-design/icons';
import { LogoutOutlined, CloseOutlined, EditOutlined, BookOutlined, EnvironmentOutlined } from '@ant-design/icons';
import Cropper from 'react-easy-crop';
export function Profile({ closeProfile }) {
@ -82,7 +82,7 @@ export function Profile({ closeProfile }) {
<div className="middleHeader">
<div className="handle">{ state.handle }</div>
<div className="close" onClick={closeProfile}>
<RightOutlined />
<CloseOutlined />
</div>
</div>
)}

View File

@ -97,7 +97,6 @@ export const ProfileWrapper = styled.div`
justify-content: center;
padding-top: 64px;
padding-left: 32px;
align-items: center;
}
.rightContent {
@ -110,6 +109,10 @@ export const ProfileWrapper = styled.div`
border-radius: 4px;
padding: 8px;
background-color: ${props => props.theme.selectedArea};
.details {
align-items: center;
}
}
.rightAccess {
@ -126,7 +129,6 @@ export const ProfileWrapper = styled.div`
.details {
display: flex;
flex-direction: column;
padding-top: 16px;
padding-left: 16px;
padding-right: 16px;
@ -140,7 +142,6 @@ export const ProfileWrapper = styled.div`
flex-direction: row;
align-items: baseline;
cursor: pointer;
justify-content: center;
&:hover .icon {
color: ${props => props.theme.linkText};
@ -176,6 +177,7 @@ export const ProfileWrapper = styled.div`
flex-direction: row;
align-items: flex-start;
padding-top: 8px;
max-width: 500px;
.data {
padding-left: 8px;

View File

@ -1,6 +1,6 @@
import { Input, Modal, List, Button } from 'antd';
import { CardsWrapper } from './Cards.styled';
import { SortAscendingOutlined, RightOutlined, UserOutlined, SearchOutlined } from '@ant-design/icons';
import { SortAscendingOutlined, CloseOutlined, UserOutlined, SearchOutlined } from '@ant-design/icons';
import { useCards } from './useCards.hook';
import { CardItem } from './cardItem/CardItem';
@ -62,7 +62,7 @@ export function Cards({ closeCards, openContact, openChannel, openListing }) {
{ state.display === 'xlarge' && (
<div className="inline">
<div className="dismiss" onClick={closeCards} >
<RightOutlined />
<CloseOutlined />
</div>
</div>
)}

View File

@ -31,6 +31,7 @@ export const CardsWrapper = styled.div`
border-bottom: 1px solid ${props => props.theme.sectionBorder};
display: flex;
flex-direction: row;
height: 48px;
.filter {
border: 1px solid ${props => props.theme.sectionBorder};

View File

@ -1,14 +1,17 @@
import { Modal } from 'antd';
import { Modal, Button, Tooltip } from 'antd';
import { ContactWrapper } from './Contact.styled';
import { useContact } from './useContact.hook';
import { Logo } from 'logo/Logo';
import { DatabaseOutlined, CloseOutlined, RightOutlined, BookOutlined, EnvironmentOutlined } from '@ant-design/icons';
import { SyncOutlined, UserAddOutlined, UserDeleteOutlined, UserSwitchOutlined, StopOutlined, DeleteOutlined, DatabaseOutlined, CloseOutlined, BookOutlined, EnvironmentOutlined } from '@ant-design/icons';
export function Contact({ close, guid, listing }) {
const [ modal, modalContext ] = Modal.useModal();
const { state, actions } = useContact(guid, listing, close);
console.log(state.busy);
const updateContact = async (action) => {
try {
await action();
@ -16,8 +19,9 @@ export function Contact({ close, guid, listing }) {
catch (err) {
console.log(err);
modal.error({
title: 'Failed to Update Contact',
content: 'Please try again.',
title: <span style={state.menuStyle}>{state.strings.operationFailed}</span>,
content: <span style={state.menuStyle}>{state.strings.tryAgain}</span>,
bodyStyle: { borderRadius: 8, padding: 16, ...state.menuStyle },
});
}
}
@ -25,119 +29,140 @@ export function Contact({ close, guid, listing }) {
return (
<ContactWrapper>
{ modalContext }
{ state.display === 'xlarge' && (
<div className="header">
<div className="handle">{ state.handle }</div>
<div className="close" onClick={close}>
<RightOutlined />
</div>
</div>
)}
{ state.display !== 'xlarge' && (
<div className="view">
<div className="title">
<div className="close" />
<div className={ state.display === 'small' || state.display === 'xlarge' ? 'frame' : 'drawer' }>
{ (state.display === 'xlarge' || state.display === 'small') && (
<div className="header">
<div className="handle">{ state.handle }</div>
{ state.display === 'small' && (
<div className="close" onClick={close}>
<CloseOutlined />
</div>
)}
{ state.display !== 'small' && (
<div className="close" onClick={close}>
<RightOutlined />
</div>
)}
<div className="close" onClick={close}>
<CloseOutlined />
</div>
</div>
</div>
)}
)}
{ (state.display !== 'xlarge' && state.display !== 'small') && (
<div className="top">{ state.handle }</div>
)}
<div className={ state.display === 'xlarge' ? 'midContent' : 'rightContent' }>
<div className="logo">
<Logo url={state.logo} width={'100%'} radius={8} />
</div>
<div className="details">
<div className="name">
{ state.name && (
<div className="data">{ state.name }</div>
)}
{ !state.name && (
<div className="data notset">name</div>
<div className={ state.display === 'xlarge' ? 'midContent' : 'rightContent' }>
<div className="logo">
{ state.logoSet && (
<Logo url={state.logo} width={'100%'} radius={8} />
)}
</div>
{ state.node && (
<div className="details">
<div className="name">
{ state.name && (
<div className="data">{ state.name }</div>
)}
{ !state.name && (
<div className="data notset">Name</div>
)}
</div>
{ state.node && (
<div className="location">
<DatabaseOutlined />
<div className="data">{ state.node }</div>
</div>
)}
<div className="location">
<DatabaseOutlined />
<div className="data">{ state.node }</div>
<EnvironmentOutlined />
{ state.location && (
<div className="data">{ state.location }</div>
)}
{ !state.location && (
<div className="data notset">Location</div>
)}
</div>
<div className="description">
<BookOutlined />
{ state.description && (
<div className="data">{ state.description }</div>
)}
{ !state.description && (
<div className="data notset">Description</div>
)}
</div>
)}
<div className="location">
<EnvironmentOutlined />
{ state.location && (
<div className="data">{ state.location }</div>
)}
{ !state.location && (
<div className="data notset">location</div>
)}
</div>
<div className="description">
<BookOutlined />
{ state.description && (
<div className="data">{ state.description }</div>
)}
{ !state.description && (
<div className="data notset">description</div>
)}
</div>
{ state.status === 'connected' && (
<div className="controls">
<div className={ state.buttonStatus } onClick={() => updateContact(actions.disconnect)}>Disconnect</div>
<div className={ state.buttonStatus } onClick={() => updateContact(actions.disconnectRemove)}>Delete Contact</div>
</div>
)}
{ state.status === 'pending' && (
<div className="controls">
<div className={ state.buttonStatus } onClick={() => updateContact(actions.confirmContact)}>Save Contact</div>
<div className={ state.buttonStatus } onClick={() => updateContact(actions.connect)}>Save and Accept</div>
<div className={ state.buttonStatus } onClick={() => updateContact(actions.remove)}>Ignore Request</div>
</div>
)}
{ state.status === 'request received' && (
<div className="controls">
<div className={ state.buttonStatus } onClick={() => updateContact(actions.saveConnect)}>Accept Connection</div>
<div className={ state.buttonStatus } onClick={() => updateContact(actions.disconnect)}>Ignore Request</div>
</div>
)}
{ state.status === 'request sent' && (
<div className="controls">
<div className={ state.buttonStatus } onClick={() => updateContact(actions.disconnect)}>Cancel Request</div>
<div className={ state.buttonStatus } onClick={() => updateContact(actions.disconnectRemove)}>Delete Contact</div>
</div>
)}
{ state.status === 'saved' && (
<div className="controls">
<div className={ state.buttonStatus } onClick={() => updateContact(actions.connect)}>Request Connection</div>
<div className={ state.buttonStatus } onClick={() => updateContact(actions.remove)}>Delete Contact</div>
</div>
)}
{ state.status === 'unsaved' && (
<div className="controls">
<div className={ state.buttonStatus } onClick={() => updateContact(actions.saveContact)}>Save Contact</div>
<div className={ state.buttonStatus } onClick={() => updateContact(actions.saveConnect)}>Save and Request</div>
</div>
)}
</div>
</div>
<div className="footer">
<div className="status">Status: { state.status }</div>
<div className="actions">
<div className="label">Actions</div>
<div className="controls">
{ state.status === 'connected' && (
<Tooltip placement="top" title="Disconnect Contact">
<Button className="button" type="primary" loading={state.busy} icon={<UserDeleteOutlined />} size="medium" onClick={() => updateContact(actions.disconnect)}>Disconnect</Button>
</Tooltip>
)}
{ state.status === 'connected' && (
<Tooltip placement="top" title="Delete Contact">
<Button className="button" type="primary" loading={state.busy} icon={<DeleteOutlined />} size="medium" onClick={() => updateContact(actions.disconnectRemove)}>Delete</Button>
</Tooltip>
)}
{ state.status === 'pending' && (
<Tooltip placement="top" title="Save Contact">
<Button className="button" type="primary" loading={state.busy} icon={<UserAddOutlined />} size="medium" onClick={() => updateContact(actions.confirmContact)}>Save</Button>
</Tooltip>
)}
{ state.status === 'pending' && (
<Tooltip placement="top" title="Save and Accept">
<Button className="button" type="primary" loading={state.busy} icon={<UserSwitchOutlined />} size="medium" onClick={() => updateContact(actions.connect)}>Connect</Button>
</Tooltip>
)}
{ state.status === 'pending' && (
<Tooltip placement="top" title="Ignore Request">
<Button className="button" type="primary" loading={state.busy} icon={<StopOutlined />} size="medium" onClick={() => updateContact(actions.remove)}>Cancel</Button>
</Tooltip>
)}
{ state.status === 'request received' && (
<Tooltip placement="top" title="Accept Connection">
<Button className="button" type="primary" loading={state.busy} icon={<UserSwitchOutlined />} size="medium" onClick={() => updateContact(actions.saveConnect)}>Connect</Button>
</Tooltip>
)}
{ state.status === 'request received' && (
<Tooltip placement="top" title="Ignore Request">
<Button className="button" type="primary" loading={state.busy} icon={<StopOutlined />} size="medium" onClick={() => updateContact(actions.disconnect)}>Cancel</Button>
</Tooltip>
)}
{ state.status === 'request sent' && (
<Tooltip placement="top" title="Cancel Request">
<Button className="button" type="primary" loading={state.busy} icon={<StopOutlined />} size="medium" onClick={() => updateContact(actions.disconnect)}>Cancel</Button>
</Tooltip>
)}
{ state.status === 'request sent' && (
<Tooltip placement="top" title="Delete Contact">
<Button className="button" type="primary" loading={state.busy} icon={<DeleteOutlined />} size="medium" onClick={() => updateContact(actions.disconnectRemove)}>Delete</Button>
</Tooltip>
)}
{ state.status === 'saved' && (
<Tooltip placement="top" title="Request Connection">
<Button className="button" type="primary" loading={state.busy} icon={<UserSwitchOutlined />} size="medium" onClick={() => updateContact(actions.connect)}>Connect</Button>
</Tooltip>
)}
{ state.status === 'saved' && (
<Tooltip placement="top" title="Delete Contact">
<Button className="button" type="primary" loading={state.busy} icon={<DeleteOutlined />} size="medium" onClick={() => updateContact(actions.remove)}>Delete</Button>
</Tooltip>
)}
{ state.status === 'unsaved' && (
<Tooltip placement="top" title="Save Contact">
<Button className="button" type="primary" loading={state.busy} icon={<UserAddOutlined />} size="medium" onClick={() => updateContact(actions.saveContact)}>Save</Button>
</Tooltip>
)}
{ state.status === 'unsaved' && (
<Tooltip placement="top" title="Save and Request">
<Button className="button" type="primary" loading={state.busy} icon={<UserSwitchOutlined />} size="medium" onClick={() => updateContact(actions.saveConnect)}>Connect</Button>
</Tooltip>
)}
{ state.offsync && (
<Tooltip placement="top" title="Resync Contact">
<Button className="button" type="primary" loading={state.busy} icon={<SyncOutlined />} size="medium" onClick={() => updateContact(actions.resync)}>Resync</Button>
</Tooltip>
)}
</div>
</div>
<div className="footer">
<div className="status">Status: { state.status }</div>
</div>
</div>
</ContactWrapper>
);

View File

@ -1,34 +1,84 @@
import styled from 'styled-components';
import { Colors } from 'constants/Colors';
export const ContactWrapper = styled.div`
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
background-color: ${Colors.profileForm};
.frame {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
color: ${props => props.theme.mainText};
}
.drawer {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
border-left: 1px solid ${props => props.theme.drawerBorder};
background-color: ${props => props.theme.selectedArea};
color: ${props => props.theme.mainText};
}
.actions {
display: flex;
flex-grow: 1;
justify-content: center;
padding-top: 16px;
flex-direction: column;
align-items: center;
.label {
padding-top: 16px;
border-bottom: 1px solid ${props => props.theme.sectionBorder};
color: ${props => props.theme.hintText};
font-size: 12px;
width: 50%;
max-width: 300px;
display: flex;
align-items: center;
justify-content: center;
}
}
.top {
height: 48px;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
font-weight: bold;
}
.header {
margin-left: 16px;
margin-right: 16px;
height: 48px;
border-bottom: 1px solid ${Colors.profileDivider};
display: flex;
border-bottom: 1px solid ${props => props.theme.headerBorder};
flex-direction: row;
align-items: center;
justify-content: center;
flex-shrink: 0;
.handle {
font-size: 20px;
font-weight: bold;
flex-grow: 1;
padding-left: 16px;
}
.handle {
font-size: 20px;
font-weight: bold;
flex-grow: 1;
padding-left: 16px;
}
.close {
font-size: 18px;
color: ${Colors.primary};
color: ${props => props.theme.hintText};
cursor: pointer;
padding-right: 16px;
}
@ -51,6 +101,18 @@ export const ContactWrapper = styled.div`
display: flex;
flex-direction: column;
align-items: center;
.details {
align-items: center;
padding-left: 16px;
padding-right: 16px;
max-width: 400px;
}
.logo {
margin-top: 16px;
margin-bottom: 8px;
}
}
.logo {
@ -58,6 +120,8 @@ export const ContactWrapper = styled.div`
width: 20vw;
margin-right: 32px;
margin-left: 32px;
width: 192px;
height: 192px;
}
.details {
@ -66,13 +130,14 @@ export const ContactWrapper = styled.div`
.notset {
font-style: italic;
color: ${Colors.grey};
color: ${props => props.theme.hintText};
}
.name {
display: flex;
flex-direction: row;
align-items: center;
padding-bottom: 8px;
.data {
padding-right: 8px;
@ -127,7 +192,7 @@ export const ContactWrapper = styled.div`
}
.close {
color: ${Colors.primary};
color: ${props => props.theme.mainText};
cursor: pointer;
width: 64px;
display: flex;
@ -140,19 +205,17 @@ export const ContactWrapper = styled.div`
.controls {
padding-top: 16px;
padding-bottom: 16px;
display: flex;
flex-direction: row;
gap: 16px;
.button {
width: 192px;
padding-top: 2px;
padding-bottom: 2px;
margin-top: 16px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
border-radius: 2px;
color: ${Colors.white};
background-color: ${Colors.primary};
}
.anticon {
font-size: 18px;
padding-top: 2px;
}
.label {
@ -173,12 +236,11 @@ export const ContactWrapper = styled.div`
}
.footer {
flex-grow: 1;
display: flex;
align-items: flex-end;
justify-content: center;
padding-bottom: 16px;
color: ${Colors.grey};
color: ${props => props.theme.hintText};
}
`

View File

@ -7,7 +7,9 @@ import { getCardByGuid } from 'context/cardUtil';
export function useContact(guid, listing, close) {
const [state, setState] = useState({
offsync: false,
logo: null,
logoSet: false,
name: null,
cardId: null,
location: null,
@ -17,6 +19,8 @@ export function useContact(guid, listing, close) {
status: null,
busy: false,
buttonStatus: 'button idle',
strings: {},
menuStyle: {},
});
const card = useContext(CardContext);
@ -47,22 +51,24 @@ export function useContact(guid, listing, close) {
const { imageSet, name, location, description, handle, node } = profile;
const status = statusMap(detail.status);
const cardId = contact.id;
const offsync = contact.offsync;
const logo = imageSet ? card.actions.getCardImageUrl(cardId) : null;
updateState({ logo, name, location, description, handle, node, status, cardId });
updateState({ logoSet: true, offsync, logo, name, location, description, handle, node, status, cardId });
}
else if (listing) {
const { logo, name, location, description, handle, node } = listing;
updateState({ logo, name, location, description, handle, node, status: 'unsaved', cardId: null });
updateState({ logoSet: true, logo, name, location, description, handle, node, status: 'unsaved', cardId: null });
}
else {
updateState({ logo: null, name: null, cardId: null, location: null, description: null, handle: null,
updateState({ logoSet: true, logo: null, name: null, cardId: null, location: null, description: null, handle: null,
status: null });
}
// eslint-disable-next-line
}, [card.state, guid, listing]);
useEffect(() => {
updateState({ display: settings.state.display });
const { display, strings, menuStyle } = settings.state;
updateState({ display, strings, menuStyle });
}, [settings.state]);
const applyAction = async (action) => {
@ -149,6 +155,14 @@ export function useContact(guid, listing, close) {
close();
});
},
resync: async () => {
await applyAction(async () => {
const success = await card.actions.resyncCard(state.cardId);
if (!success) {
throw new Error("resync failed");
}
});
},
remove: async () => {
await applyAction(async () => {
await card.actions.removeCard(state.cardId);

View File

@ -26,50 +26,52 @@ export function Listing({ closeListing, openContact }) {
return (
<ListingWrapper>
{ modalContext }
<div className="search">
{ !state.showFilter && (
<div className="showfilter" onClick={actions.showFilter}>
<FilterOutlined />
</div>
)}
{ state.showFilter && (
<div className="hidefilter" onClick={actions.hideFilter}>
<FilterOutlined />
</div>
)}
<div className="params">
<div className="node">
<Input className="nodeControl" bordered={false} placeholder="Server"
prefix={<DatabaseOutlined />} value={state.node} spellCheck="false"
disabled={state.disabled} onChange={(e) => actions.onNode(e.target.value)} />
</div>
<div className={ state.display === 'small' ? 'frame' : 'drawer' }>
<div className="search">
{ !state.showFilter && (
<div className="showfilter" onClick={actions.showFilter}>
<FilterOutlined />
</div>
)}
{ state.showFilter && (
<div className="hidefilter" onClick={actions.hideFilter}>
<FilterOutlined />
</div>
)}
<div className="params">
<div className="node">
<Input className="nodeControl" bordered={false} placeholder="Username"
prefix={<UserOutlined />} value={state.username} spellCheck="false"
onChange={(e) => actions.setUsername(e.target.value)} />
<Input className="nodeControl" bordered={false} placeholder="Server"
prefix={<DatabaseOutlined />} value={state.node} spellCheck="false"
disabled={state.disabled} onChange={(e) => actions.onNode(e.target.value)} />
</div>
{ state.showFilter && (
<div className="node">
<Input className="nodeControl" bordered={false} placeholder="Username"
prefix={<UserOutlined />} value={state.username} spellCheck="false"
onChange={(e) => actions.setUsername(e.target.value)} />
</div>
)}
</div>
<div className="inline">
<Button type="text" icon={<SearchOutlined />} loading={state.busy} onClick={getListing}></Button>
</div>
{ state.display === 'small' && (
<div className="inline">
<Button type="text" icon={<CloseOutlined />} onClick={closeListing}></Button>
</div>
)}
</div>
<div className="inline">
<Button type="text" icon={<SearchOutlined />} loading={state.busy} onClick={getListing}></Button>
<div className="view">
{ state.contacts.length > 0 && (
<List local={{ emptyText: '' }} itemLayout="horizontal" dataSource={state.contacts} gutter="0"
renderItem={item => (
<ListingItem item={item} open={() => openContact(item.guid, item)} />
)} />
)}
{ state.contacts.length === 0 && (
<div className="empty">No Contacts</div>
)}
</div>
{ state.display === 'small' && (
<div className="inline">
<Button type="text" icon={<CloseOutlined />} onClick={closeListing}></Button>
</div>
)}
</div>
<div className="view">
{ state.contacts.length > 0 && (
<List local={{ emptyText: '' }} itemLayout="horizontal" dataSource={state.contacts} gutter="0"
renderItem={item => (
<ListingItem item={item} open={() => openContact(item.guid, item)} />
)} />
)}
{ state.contacts.length === 0 && (
<div className="empty">No Contacts</div>
)}
</div>
</ListingWrapper>
);

View File

@ -1,23 +1,26 @@
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: ${props => props.theme.itemArea};
color: ${props => props.theme.mainText};
.drawer {
width: 100%;
height: 100%;
border-left: 1px solid ${props => props.theme.sectionBorder};
display: flex;
border-left: 1px solid ${props => props.theme.drawerBorder};
flex-direction: column;
background-color: ${props => props.theme.itemArea};
color: ${props => props.theme.mainText};
}
.screen {
.frame {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
background-color: ${props => props.theme.itemArea};
color: ${props => props.theme.mainText};
}
.view {

View File

@ -1,5 +1,4 @@
import styled from 'styled-components';
import { Colors } from 'constants/Colors';
export const ListingItemWrapper = styled.div`
height: 48px;