Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create sidekick with portal #2749

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/apps/app-router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import { featureFlags } from '../lib/feature-flags';
import { useSelector } from 'react-redux';
import { RootState } from '../store/reducer';
import { Provider as AuthenticationContextProvider } from '../components/authentication/context';
import { Container as SidekickContainer } from '../components/sidekick/components/container';
import { Header as SidekickHeader } from '../components/sidekick/components/header';
import { CurrentUserDetails } from '../components/sidekick/components/current-user-details';

const redirectToRoot = () => <Redirect to={'/'} />;

Expand All @@ -22,6 +25,11 @@ export const AppRouter = () => {

return (
<AuthenticationContextProvider value={{ isAuthenticated }}>
<SidekickContainer>
<SidekickHeader>
<CurrentUserDetails />
</SidekickHeader>
</SidekickContainer>
<Switch>
<Route path='/conversation/:conversationId' component={MessengerApp} />
<Route path='/' exact component={MessengerApp} />
Expand Down
4 changes: 4 additions & 0 deletions src/apps/feed/components/feed-chat/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import { send } from '../../../../store/messages';
import { config } from '../../../../config';
import { Spinner } from '@zero-tech/zui/components/LoadingIndicator';

jest.mock('../../../../components/sidekick/variants/members-sidekick', () => ({
MembersSidekick: () => <div data-testid='members-sidekick' />,
}));

describe('FeedChatContainer', () => {
const subject = (props: any = {}) => {
const allProps = {
Expand Down
123 changes: 50 additions & 73 deletions src/apps/feed/components/sidekick/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,89 +2,66 @@ import { ReactNode } from 'react';
import { useHistory } from 'react-router-dom';

import { useSidekick } from './lib/useSidekick';
import { Container as SidekickContainer } from '../../../../components/sidekick/components/container';
import { UserProfileContainer } from '../../../../components/messenger/user-profile/container';
import { Header } from '../../../../components/sidekick/components/header';
import { CurrentUserDetails } from '../../../../components/sidekick/components/current-user-details';
import { ScrollbarContainer } from '../../../../components/scrollbar-container';
import { Input } from '@zero-tech/zui/components/Input/Input';
import { LoadingIndicator } from '@zero-tech/zui/components/LoadingIndicator';
import { IconBellOff1, IconSearchMd } from '@zero-tech/zui/icons';
import { Panel, PanelBody } from '../../../../components/layout/panel';
import { IconSearchMd, IconBellOff1 } from '@zero-tech/zui/icons';
import {
ContentPortal as SidekickContentPortal,
Content as SidekickContent,
Scroll as SidekickScroll,
} from '../../../../components/sidekick';

import classNames from 'classnames';
import styles from './styles.module.scss';

export const Sidekick = () => {
const {
isErrorZids,
isLoadingZids,
isProfileOpen,
selectedZId,
zids,
search,
setSearch,
unreadCounts,
mutedChannels,
} = useSidekick();
const { isErrorZids, isLoadingZids, selectedZId, zids, search, setSearch, unreadCounts, mutedChannels } =
useSidekick();

return (
<SidekickContainer className={styles.Container}>
<Panel className={styles.Panel}>
<PanelBody className={styles.Body}>
{isProfileOpen ? (
<UserProfileContainer />
) : (
<>
<Header>
<CurrentUserDetails />
</Header>
<div className={styles.Sidekick}>
<Input
className={styles.Search}
onChange={setSearch}
size={'small'}
startEnhancer={<IconSearchMd size={16} color={'var(--color-greyscale-11)'} />}
type={'search'}
value={search}
wrapperClassName={styles.SearchWrapper}
/>
<ScrollbarContainer variant='on-hover' className={styles.Scroll}>
<ul className={styles.List}>
{isLoadingZids && <LoadingIndicator />}
{isErrorZids && <li>Error loading channels</li>}
{zids?.map((zid) => {
const hasUnreadHighlights = unreadCounts[zid]?.highlight > 0;
const hasUnreadTotal = unreadCounts[zid]?.total > 0;
const isMuted = mutedChannels[zid];
const isUnread = hasUnreadHighlights || hasUnreadTotal;
return (
<FeedItem key={zid} route={`/feed/${zid}`} isSelected={selectedZId === zid}>
<div className={classNames(styles.FeedName, { [styles.Unread]: isUnread })}>
<span>0://</span>
<div>{zid}</div>
</div>
<SidekickContentPortal>
<SidekickContent>
<Input
className={styles.Search}
onChange={setSearch}
size={'small'}
startEnhancer={<IconSearchMd size={16} color={'var(--color-greyscale-11)'} />}
type={'search'}
value={search}
wrapperClassName={styles.SearchWrapper}
/>
<SidekickScroll>
<ul className={styles.List}>
{isLoadingZids && <LoadingIndicator />}
{isErrorZids && <li>Error loading channels</li>}
{zids?.map((zid) => {
const hasUnreadHighlights = unreadCounts[zid]?.highlight > 0;
const hasUnreadTotal = unreadCounts[zid]?.total > 0;
const isMuted = mutedChannels[zid];
const isUnread = hasUnreadHighlights || hasUnreadTotal;
return (
<FeedItem key={zid} route={`/feed/${zid}`} isSelected={selectedZId === zid}>
<div className={classNames(styles.FeedName, { [styles.Unread]: isUnread })}>
<span>0://</span>
<div>{zid}</div>
</div>

<div className={styles.ItemIcons}>
{isMuted && <IconBellOff1 className={styles.MutedIcon} size={16} />}
{!hasUnreadHighlights && hasUnreadTotal && (
<div className={styles.UnreadCount}>{unreadCounts[zid]?.total}</div>
)}
{hasUnreadHighlights && (
<div className={styles.UnreadHighlight}>{unreadCounts[zid]?.highlight}</div>
)}
</div>
</FeedItem>
);
})}
</ul>
</ScrollbarContainer>
</div>
</>
)}
</PanelBody>
</Panel>
</SidekickContainer>
<div className={styles.ItemIcons}>
{isMuted && <IconBellOff1 className={styles.MutedIcon} size={16} />}
{!hasUnreadHighlights && hasUnreadTotal && (
<div className={styles.UnreadCount}>{unreadCounts[zid]?.total}</div>
)}
{hasUnreadHighlights && (
<div className={styles.UnreadHighlight}>{unreadCounts[zid]?.highlight}</div>
)}
</div>
</FeedItem>
);
})}
</ul>
</SidekickScroll>
</SidekickContent>
</SidekickContentPortal>
);
};

Expand Down
6 changes: 0 additions & 6 deletions src/apps/feed/components/sidekick/lib/useSidekick.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import { useRouteMatch } from 'react-router-dom';

import { useOwnedZids } from '../../../../../lib/hooks/useOwnedZids';
import { useSelector } from 'react-redux';
import { RootState } from '../../../../../store/reducer';
import { Stage as ProfileStage } from '../../../../../store/user-profile';
import { useState } from 'react';
import { selectMutedChannels, selectSocialChannelsUnreadCounts } from './selectors';

Expand All @@ -15,7 +13,6 @@ export interface UnreadCount {
interface UseSidekickReturn {
isErrorZids: boolean;
isLoadingZids: boolean;
isProfileOpen: boolean;
selectedZId?: string;
zids?: string[];
search: string;
Expand All @@ -30,8 +27,6 @@ export const useSidekick = (): UseSidekickReturn => {
const route = useRouteMatch('/feed/:zid');
const selectedZId = route?.params?.zid;

const isProfileOpen = useSelector((state: RootState) => state.userProfile.stage !== ProfileStage.None);

const { zids, isLoading, isError } = useOwnedZids();

const worldZids = zids?.map((zid) => zid.split('.')[0]);
Expand All @@ -45,7 +40,6 @@ export const useSidekick = (): UseSidekickReturn => {
return {
isErrorZids: isError,
isLoadingZids: isLoading,
isProfileOpen,
selectedZId,
zids: filteredZids,
search,
Expand Down
45 changes: 1 addition & 44 deletions src/apps/feed/components/sidekick/styles.module.scss
Original file line number Diff line number Diff line change
@@ -1,39 +1,5 @@
@import '../../../../glass';

.Container {
width: 100%;
max-width: 288px;

[class*='Legacy'] {
background: transparent;
backdrop-filter: none;
}

[class*='_Content'] {
border-radius: unset;
}
}

.Panel {
height: 100%;
}

.Body {
height: 100%;
padding: 0;
}

.Sidekick {
width: 100%;
padding: 16px;

box-sizing: border-box;

display: flex;
flex-direction: column;
gap: 16px;
}

.Search {
width: 100%;
box-sizing: border-box;
Expand All @@ -45,13 +11,8 @@

.List {
list-style: none;

width: 100%;
padding: 0;

display: flex;
flex-direction: column;
box-sizing: border-box;
margin: 0;

li {
padding: 6px 8px;
Expand Down Expand Up @@ -87,10 +48,6 @@
}
}

.Scroll > div {
overflow-x: hidden;
}

.FeedItem {
display: flex;
align-items: center;
Expand Down
2 changes: 2 additions & 0 deletions src/components/edit-profile/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
flex-direction: column;
margin-bottom: 16px;
flex-grow: 1;
max-width: 100%;

&__header-container {
display: flex;
padding: 16px;
}

&__content-wrapper {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@
display: flex;
flex-direction: column;
gap: 24px;
min-width: 270px;
max-width: 100%;

&__back-icon-container {
display: flex;
padding: 16px 0 0 16px;
z-index: 1;
}

Expand All @@ -30,7 +29,6 @@
}

&__body {
margin: 0px 16px;
flex-grow: 1;
display: flex;
flex-direction: column;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ $side-padding: 16px;
overflow-x: auto;
white-space: nowrap;
padding: 8px 0;
margin: 0 16px;

&::-webkit-scrollbar-thumb {
display: none;
Expand Down Expand Up @@ -115,15 +114,13 @@ $side-padding: 16px;
display: flex;
gap: 16px;
flex-direction: column;
padding: 16px 0 0;

animation: conversation-slide-in 600ms ease-in forwards;

&-actions {
display: flex;
align-items: center;
gap: 8px;
padding: 0 $side-padding;
}

&-conversations-search {
Expand All @@ -141,8 +138,6 @@ $side-padding: 16px;
flex-grow: 1;
// Forcing a height here allows the flex-grow to fill the size without growing too big
height: 1px;
// accounts for 4px scrollbar width on right side of container
padding: 0 12px 0 16px;
}

&__empty {
Expand Down
29 changes: 0 additions & 29 deletions src/components/messenger/list/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@ import { ConversationListPanel } from './conversation-list-panel';
import { GroupDetailsPanel } from './group-details-panel';
import { Stage } from '../../../store/create-conversation';
import { previewDisplayDate } from '../../../lib/chat/chat-message';
import { CurrentUserDetails } from '../../sidekick/components/current-user-details';
import { ErrorDialog } from '../../error-dialog';
import { bem } from '../../../lib/bem';
import { IconButton } from '@zero-tech/zui/components/IconButton';
const c = bem('.direct-message-members');

const mockSearchMyNetworksByName = jest.fn();
Expand Down Expand Up @@ -55,33 +53,6 @@ describe('messenger-list', () => {
return shallow(<DirectMessageChat {...allProps} />);
};

it('render direct message members', function () {
const wrapper = subject({});

expect(wrapper.find('.direct-message-members').exists()).toBe(true);
});

it('starts create conversation saga', async function () {
const startCreateConversation = jest.fn();
const wrapper = subject({ startCreateConversation });

wrapper.find(IconButton).at(1).prop('onClick')(undefined);

expect(startCreateConversation).toHaveBeenCalled();
});

it('renders user UserDetails when stage is equal to none', function () {
const wrapper = subject({ stage: Stage.None });

expect(wrapper).toHaveElement(CurrentUserDetails);
});

it('does not render UserDetails when stage is not equal to none', function () {
const wrapper = subject({ stage: Stage.InitiateConversation });

expect(wrapper).not.toHaveElement(CurrentUserDetails);
});

it('renders CreateConversationPanel', function () {
const wrapper = subject({ stage: Stage.InitiateConversation });

Expand Down
Loading
Loading