From bdf4df5844464ed4f84d445d9c6a0d2d9420ba63 Mon Sep 17 00:00:00 2001 From: Roland Osborne Date: Wed, 17 Aug 2022 00:47:39 -0700 Subject: [PATCH] rendering contact profile --- .../src/context/useViewportContext.hook.js | 1 + net/web/src/session/Session.jsx | 24 ++-- net/web/src/session/cards/Cards.jsx | 6 +- net/web/src/session/cards/Cards.styled.js | 3 +- .../src/session/cards/cardItem/CardItem.jsx | 2 +- net/web/src/session/contact/Contact.jsx | 61 +++++++++- net/web/src/session/contact/Contact.styled.js | 110 ++++++++++++++++++ .../src/session/contact/useContact.hook.js | 70 +++++++++++ .../src/session/identity/Identity.styled.js | 1 + net/web/src/session/listing/Listing.jsx | 43 ++++++- net/web/src/session/listing/Listing.styled.js | 45 +++++++ .../listing/listingItem/ListingItem.jsx | 18 +++ .../listing/listingItem/ListingItem.styled.js | 41 +++++++ .../listingItem/useListingItem.hook.js | 21 ++++ .../src/session/listing/useListing.hook.js | 54 +++++++++ net/web/src/session/useSession.hook.js | 17 ++- 16 files changed, 490 insertions(+), 27 deletions(-) create mode 100644 net/web/src/session/contact/useContact.hook.js create mode 100644 net/web/src/session/listing/Listing.styled.js create mode 100644 net/web/src/session/listing/listingItem/ListingItem.jsx create mode 100644 net/web/src/session/listing/listingItem/ListingItem.styled.js create mode 100644 net/web/src/session/listing/listingItem/useListingItem.hook.js create mode 100644 net/web/src/session/listing/useListing.hook.js diff --git a/net/web/src/context/useViewportContext.hook.js b/net/web/src/context/useViewportContext.hook.js index 883fb1fc..f3b53fe6 100644 --- a/net/web/src/context/useViewportContext.hook.js +++ b/net/web/src/context/useViewportContext.hook.js @@ -27,6 +27,7 @@ export function useViewportContext() { }; useEffect(() => { + setTimeout(handleResize, 1000); //cludge for my mobile browser handleResize(); window.addEventListener('resize', handleResize); window.addEventListener('orientationchange', handleResize); diff --git a/net/web/src/session/Session.jsx b/net/web/src/session/Session.jsx index 291ae7b9..d60eda8b 100644 --- a/net/web/src/session/Session.jsx +++ b/net/web/src/session/Session.jsx @@ -3,7 +3,6 @@ import { Drawer } from 'antd'; import { useNavigate } from 'react-router-dom'; import { SessionWrapper } from './Session.styled'; import { AppContext } from 'context/AppContext'; -import { ViewportContext } from 'context/ViewportContext'; import { useSession } from './useSession.hook'; import { Conversation } from './conversation/Conversation'; import { Details } from './details/Details'; @@ -21,7 +20,6 @@ export function Session() { const { state, actions } = useSession(); const app = useContext(AppContext); - const viewport = useContext(ViewportContext); const navigate = useNavigate(); useEffect(() => { @@ -60,7 +58,7 @@ console.log(state); return ( - { (viewport.state.display === 'xlarge') && ( + { (state.display === 'xlarge') && (
@@ -95,8 +93,9 @@ console.log(state); { state.cards && (
- +
@@ -109,7 +108,7 @@ console.log(state);
)} - { (viewport.state.display === 'large' || viewport.state.display === 'medium') && ( + { (state.display === 'large' || state.display === 'medium') && (
@@ -129,17 +128,18 @@ console.log(state);
)} - + { state.cards && ( )} - + { state.contact && ( - + )} @@ -151,7 +151,7 @@ console.log(state);
)} - { (viewport.state.display === 'small') && ( + { (state.display === 'small') && (
@@ -179,7 +179,7 @@ console.log(state); )} { state.contact && (
- +
)} { (state.profile || state.account) && ( diff --git a/net/web/src/session/cards/Cards.jsx b/net/web/src/session/cards/Cards.jsx index 8280a0b1..45485417 100644 --- a/net/web/src/session/cards/Cards.jsx +++ b/net/web/src/session/cards/Cards.jsx @@ -1,6 +1,6 @@ import { Drawer, Input, List } from 'antd'; 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 { CardItem } from './cardItem/CardItem'; @@ -50,8 +50,8 @@ export function Cards({ closeCards, openContact, openListing }) { { state.display !== 'small' && (
- -
New Contact
+ +
Find New Contact
)} diff --git a/net/web/src/session/cards/Cards.styled.js b/net/web/src/session/cards/Cards.styled.js index 8f7c6093..6c5b97a1 100644 --- a/net/web/src/session/cards/Cards.styled.js +++ b/net/web/src/session/cards/Cards.styled.js @@ -86,8 +86,7 @@ export const CardsWrapper = styled.div` .add { display: flex; flex-direction: row; - background-color: ${Colors.primary}; - color: ${Colors.white}; + color: ${Colors.primary}; align-items: center; justify-content: center; padding-left: 16px; diff --git a/net/web/src/session/cards/cardItem/CardItem.jsx b/net/web/src/session/cards/cardItem/CardItem.jsx index e2a2cc1e..2dc6ed3a 100644 --- a/net/web/src/session/cards/cardItem/CardItem.jsx +++ b/net/web/src/session/cards/cardItem/CardItem.jsx @@ -21,7 +21,7 @@ export function CardItem({ item, open }) { } return ( - open(profile.guid, profile.node)}> + open(profile.guid)}>
{ profile?.name }
diff --git a/net/web/src/session/contact/Contact.jsx b/net/web/src/session/contact/Contact.jsx index fd9d9ca1..c50bbecc 100644 --- a/net/web/src/session/contact/Contact.jsx +++ b/net/web/src/session/contact/Contact.jsx @@ -1,6 +1,63 @@ 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 }) { - return ; +export function Contact({ close, guid, listing }) { + + const { state, actions } = useContact(guid, listing); + + const Image = ( + + ); + + const Details = ( +
+
+
{ state.name }
+
+
+ +
{ state.location }
+
+
+ +
{ state.description }
+
+
+ ); + + + return ( + + { state.init && state.display === 'xlarge' && ( + <> +
+
{ state.handle }
+
+ +
+
+ +
+ { Image } + { Details } +
+ + )} + { state.init && state.display !== 'xlarge' && ( +
+
{ state.handle }
+
Contact Profile
+
+ { Image } + { Details } +
+
+ )} +
+ ); } diff --git a/net/web/src/session/contact/Contact.styled.js b/net/web/src/session/contact/Contact.styled.js index 7a590c44..782ed0ed 100644 --- a/net/web/src/session/contact/Contact.styled.js +++ b/net/web/src/session/contact/Contact.styled.js @@ -7,5 +7,115 @@ export const ContactWrapper = styled.div` display: flex; flex-direction: column; 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; + } + } + } ` diff --git a/net/web/src/session/contact/useContact.hook.js b/net/web/src/session/contact/useContact.hook.js new file mode 100644 index 00000000..706848dd --- /dev/null +++ b/net/web/src/session/contact/useContact.hook.js @@ -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 }; +} + diff --git a/net/web/src/session/identity/Identity.styled.js b/net/web/src/session/identity/Identity.styled.js index 647ff9c8..98a39ff6 100644 --- a/net/web/src/session/identity/Identity.styled.js +++ b/net/web/src/session/identity/Identity.styled.js @@ -35,6 +35,7 @@ export const IdentityWrapper = styled.div` flex-direction: column; align-items: center; justify-content: center; + line-height: 16px; .name { font-size: 14px; diff --git a/net/web/src/session/listing/Listing.jsx b/net/web/src/session/listing/Listing.jsx index cc5e3590..231c596d 100644 --- a/net/web/src/session/listing/Listing.jsx +++ b/net/web/src/session/listing/Listing.jsx @@ -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 }) { - return
openContact('asdf', 'qwer')}>LISTING
+ + 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 ( + + +
+ ( + + )} /> +
+
+ ); } diff --git a/net/web/src/session/listing/Listing.styled.js b/net/web/src/session/listing/Listing.styled.js new file mode 100644 index 00000000..878004bf --- /dev/null +++ b/net/web/src/session/listing/Listing.styled.js @@ -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; + } + } +`; diff --git a/net/web/src/session/listing/listingItem/ListingItem.jsx b/net/web/src/session/listing/listingItem/ListingItem.jsx new file mode 100644 index 00000000..ee1d0bac --- /dev/null +++ b/net/web/src/session/listing/listingItem/ListingItem.jsx @@ -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 ( + open(item.guid, item)}> + +
+
{ state.name }
+
{ state.handle }
+
+
+ ); +} diff --git a/net/web/src/session/listing/listingItem/ListingItem.styled.js b/net/web/src/session/listing/listingItem/ListingItem.styled.js new file mode 100644 index 00000000..3b1191a8 --- /dev/null +++ b/net/web/src/session/listing/listingItem/ListingItem.styled.js @@ -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; + } + } +` + diff --git a/net/web/src/session/listing/listingItem/useListingItem.hook.js b/net/web/src/session/listing/listingItem/useListingItem.hook.js new file mode 100644 index 00000000..6989ce33 --- /dev/null +++ b/net/web/src/session/listing/listingItem/useListingItem.hook.js @@ -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 }; +} + diff --git a/net/web/src/session/listing/useListing.hook.js b/net/web/src/session/listing/useListing.hook.js new file mode 100644 index 00000000..582b5df7 --- /dev/null +++ b/net/web/src/session/listing/useListing.hook.js @@ -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 }; +} diff --git a/net/web/src/session/useSession.hook.js b/net/web/src/session/useSession.hook.js index f940e70b..94c3117b 100644 --- a/net/web/src/session/useSession.hook.js +++ b/net/web/src/session/useSession.hook.js @@ -1,14 +1,14 @@ import { useContext, useState, useEffect, useRef } from 'react'; import { CardContext } from 'context/CardContext'; import { StoreContext } from 'context/StoreContext'; +import { ViewportContext } from 'context/ViewportContext'; export function useSession() { const [state, setState] = useState({ cardUpdated: false, contactGuid: null, - contactNode: null, - listingNode: null, + contactListing: null, conversation: false, details: false, cards: false, @@ -20,6 +20,7 @@ export function useSession() { const card = useContext(CardContext); const store = useContext(StoreContext); + const viewport = useContext(ViewportContext); const storeStatus = useRef(null); const cardStatus = useRef(null); @@ -28,6 +29,10 @@ export function useSession() { setState((s) => ({ ...s, ...value })); } + useEffect(() => { + updateState({ display: viewport.state.display }); + }, [viewport]); + useEffect(() => { const contacts = Array.from(card.state.cards.values()); @@ -62,14 +67,14 @@ export function useSession() { closeCards: () => { updateState({ cards: false }); }, - openListing: (listingNode) => { - updateState({ listing: true, listingNode }); + openListing: () => { + updateState({ listing: true }); }, closeListing: () => { updateState({ listing: false }); }, - openContact: (contactGuid, contactNode) => { - updateState({ contact: true, contactGuid, contactNode }); + openContact: (contactGuid, contactListing) => { + updateState({ contact: true, contactGuid, contactListing }); }, closeContact: () => { updateState({ contact: false });