Skip to content

Commit

Permalink
📇 UI work on notifications (#4434)
Browse files Browse the repository at this point in the history
* Initial tile setup

* Notification view setup

* Add context menu to list item

* Work on NotificationsWidget.tsx

* Iniital work on NotificationsView.tsx

* Add badge to avatar

* Separate NotificationsView for channel and member type

* CR fixes

* Typo

---------

Co-authored-by: Theophile Sandoz <[email protected]>
  • Loading branch information
WRadoslaw and thesan authored Sep 14, 2023
1 parent 55929e0 commit c864e61
Show file tree
Hide file tree
Showing 27 changed files with 729 additions and 538 deletions.
12 changes: 12 additions & 0 deletions packages/atlas/src/components/Avatar/Avatar.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import styled from '@emotion/styled'
import { SvgActionAddImage, SvgActionEdit, SvgIllustrativeFileFailed } from '@/assets/icons'
import { SvgAvatarSilhouette } from '@/assets/illustrations'
import { AssetImage } from '@/components/AssetImage'
import { smallBadgeStyles } from '@/components/Badge'
import { SkeletonLoader } from '@/components/_loaders/SkeletonLoader'
import { cVar, square, zIndex } from '@/styles'

Expand Down Expand Up @@ -171,3 +172,14 @@ export const StyledSvgIllustrativeFileFailed = styled(SvgIllustrativeFileFailed)
fill: ${cVar('colorCoreNeutral100')};
}
`

export const BadgeContainer = styled.div`
position: relative;
${smallBadgeStyles}
&[data-badge]::after {
position: absolute;
right: -6px;
top: -6px;
}
`
95 changes: 50 additions & 45 deletions packages/atlas/src/components/Avatar/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { validateImage } from '@/utils/image'

import {
AvatarSize,
BadgeContainer,
ChildrenWrapper,
Container,
IconAndOverlayWrapper,
Expand Down Expand Up @@ -33,6 +34,7 @@ export type AvatarProps = PropsWithChildren<{
editable?: boolean
clickable?: boolean
disableHoverDimm?: boolean
badge?: boolean | string | number
}>

export const Avatar: FC<AvatarProps> = ({
Expand All @@ -49,6 +51,7 @@ export const Avatar: FC<AvatarProps> = ({
onClick,
onImageValidation,
disableHoverDimm,
badge,
}) => {
const isEditable = !loading && editable && size !== 32 && size !== 24

Expand Down Expand Up @@ -82,51 +85,53 @@ export const Avatar: FC<AvatarProps> = ({
}, [size])

return (
<Container
as={onClick ? 'button' : 'div'}
type={onClick ? 'button' : undefined}
onClick={onClick}
size={size}
className={className}
isLoading={loading}
disableHoverDimm={disableHoverDimm}
isClickable={clickable || (clickable == null && !!onClick)} // default to true if onClick is provided
>
{(clickable || !!onClick) && (
<IconAndOverlayWrapper>
<Overlay isEdit={isEditable && !!assetUrls} />
{isEditable &&
(assetUrls ? (
<StyledSvgActionEdit width={getEditableIconSize()} height={getEditableIconSize()} />
) : (
<StyledSvgActionAddImage width={getEditableIconSize()} height={getEditableIconSize()} />
))}
</IconAndOverlayWrapper>
)}
{!children &&
(newChannel && !isEditable ? (
<NewChannelAvatar>
<SvgActionNewChannel />
</NewChannelAvatar>
) : hasAvatarUploadFailed ? (
<NewChannelAvatar>
<StyledSvgIllustrativeFileFailed />
{size === 136 && (
<Text variant="t100" as="span" margin={{ top: 2 }}>
Failed upload
</Text>
)}
</NewChannelAvatar>
) : (
<StyledImage
resolvedUrls={assetUrls}
onError={onError}
isLoading={loading}
imagePlaceholder={<SilhouetteAvatar />}
/>
))}
{children && (loading ? <StyledSkeletonLoader rounded /> : <ChildrenWrapper>{children}</ChildrenWrapper>)}
</Container>
<BadgeContainer data-badge={badge}>
<Container
as={onClick ? 'button' : 'div'}
type={onClick ? 'button' : undefined}
onClick={onClick}
size={size}
className={className}
isLoading={loading}
disableHoverDimm={disableHoverDimm}
isClickable={clickable || (clickable == null && !!onClick)} // default to true if onClick is provided
>
{(clickable || !!onClick) && (
<IconAndOverlayWrapper>
<Overlay isEdit={isEditable && !!assetUrls} />
{isEditable &&
(assetUrls ? (
<StyledSvgActionEdit width={getEditableIconSize()} height={getEditableIconSize()} />
) : (
<StyledSvgActionAddImage width={getEditableIconSize()} height={getEditableIconSize()} />
))}
</IconAndOverlayWrapper>
)}
{!children &&
(newChannel && !isEditable ? (
<NewChannelAvatar>
<SvgActionNewChannel />
</NewChannelAvatar>
) : hasAvatarUploadFailed ? (
<NewChannelAvatar>
<StyledSvgIllustrativeFileFailed />
{size === 136 && (
<Text variant="t100" as="span" margin={{ top: 2 }}>
Failed upload
</Text>
)}
</NewChannelAvatar>
) : (
<StyledImage
resolvedUrls={assetUrls}
onError={onError}
isLoading={loading}
imagePlaceholder={<SilhouetteAvatar />}
/>
))}
{children && (loading ? <StyledSkeletonLoader rounded /> : <ChildrenWrapper>{children}</ChildrenWrapper>)}
</Container>
</BadgeContainer>
)
}

Expand Down
2 changes: 1 addition & 1 deletion packages/atlas/src/components/ListItem/ListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export const ListItem = forwardRef<HTMLDivElement, ListItemProps>(
{label}
</LabelText>
</LabelContainer>
<Caption as="span" captionPosition={captionPosition} color="colorText" variant="t100">
<Caption as="span" captionPosition={captionPosition} color="colorTextMuted" variant="t100">
{caption}
</Caption>
</LabelCaptionContainer>
Expand Down
3 changes: 2 additions & 1 deletion packages/atlas/src/components/PageTabs/PageTabs.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ export const PageTabsWrapper = styled.div`
export const BackActionWrapper = styled.div`
display: flex;
align-items: center;
padding: ${sizes(2)} ${sizes(4)} ${sizes(2)} 0;
padding-right: ${sizes(4)};
position: relative;
border-right: 1px solid ${cVar('colorCoreNeutral600')};
margin-right: ${sizes(4)};
`
export const TailingContentWrapper = styled.div`
margin: ${sizes(2)} ${sizes(4)} ${sizes(2)} auto;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import { BreakpointKey, Grid, media, sizes } from '@/styles'

export type GridWrapperProps = {
grid?: Grid
gap?: number
}

const createGridBreakpoints = ({ grid = { xxs: { columns: 'auto', minItemWidth: 300 } } }: GridWrapperProps) => {
const createGridBreakpoints = ({ grid = { xxs: { columns: 'auto', minItemWidth: 300 } }, gap }: GridWrapperProps) => {
const gridKeys = Object.keys(grid) as Array<BreakpointKey>

const styles = gridKeys.map((key) => {
Expand All @@ -20,6 +21,13 @@ const createGridBreakpoints = ({ grid = { xxs: { columns: 'auto', minItemWidth:
}
`
})

if (gap) {
styles.push(css`
gap: ${sizes(gap)}!important;
`)
}

return styles
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,7 @@ export type SectionContentProps = {

export const SectionContent: FC<SectionContentProps> = (props) => {
if (props.type === 'grid') {
return (
<GridWrapper grid={props.grid} className={props.className}>
{props.children}
</GridWrapper>
)
return <GridWrapper {...props}>{props.children}</GridWrapper>
}

return <Carousel {...props}>{props.children}</Carousel>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ export const TopbarStudio: FC<StudioTopbarProps> = ({ hideChannelInfo, isMembers
setIsWorkspaceOpen(false)
}

// todo: add logic after orion is done
const unseenMemberNotifications = 1

return (
<>
<StyledTopbarBase
Expand All @@ -72,11 +75,12 @@ export const TopbarStudio: FC<StudioTopbarProps> = ({ hideChannelInfo, isMembers
{mdMatch && 'Upload video'}
</Button>
</CSSTransition>
<NotificationsWidget trigger={<NotificationsButton />} />
<NotificationsWidget type="channel" trigger={<NotificationsButton />} />
<StyledAvatar
size={40}
assetUrls={activeChannel?.avatarPhoto?.resolvedUrls}
onClick={handleDrawerToggle}
badge={unseenMemberNotifications}
/>
</StudioTopbarContainer>
) : (
Expand All @@ -88,7 +92,7 @@ export const TopbarStudio: FC<StudioTopbarProps> = ({ hideChannelInfo, isMembers
<MemberDropdown
onChannelChange={handleChannelChange}
isActive={isMemberDropdownActive}
publisher={!!hasAtLeastOneChannel}
publisher={hasAtLeastOneChannel}
closeDropdown={() => setIsMemberDropdownActive(false)}
/>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ export const TopbarViewer: FC = () => {
}

const topbarButtonLoading = isAuthenticating || membershipsLoading
// todo: add logic after orion is done
const unseenChannelNotifications = 2

if (pathname === absoluteRoutes.viewer.ypp()) {
return null
Expand Down Expand Up @@ -140,13 +142,14 @@ export const TopbarViewer: FC = () => {
{!topbarButtonLoading ? (
isLoggedIn ? (
<SignedButtonsWrapper>
<NotificationsWidget trigger={<NotificationsButton />} />
<NotificationsWidget type="member" trigger={<NotificationsButton />} />
{!mdMatch && !searchOpen && (
<StyledAvatar
size={40}
assetUrls={memberAvatarUrls}
loading={memberAvatarLoading}
onClick={handleDrawerToggle}
badge={unseenChannelNotifications}
/>
)}
{mdMatch && (
Expand All @@ -155,6 +158,7 @@ export const TopbarViewer: FC = () => {
assetUrls={memberAvatarUrls}
onClick={handleDrawerToggle}
loading={memberAvatarLoading}
badge={unseenChannelNotifications}
/>
)}
</SignedButtonsWrapper>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Meta, StoryFn } from '@storybook/react'
import { useState } from 'react'
import { BrowserRouter } from 'react-router-dom'

import { NotificationRecord } from '@/providers/notifications/notifications.types'
Expand Down Expand Up @@ -39,7 +38,6 @@ export default {

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const Template: StoryFn<any> = (args) => {
const [selected, setSelected] = useState(false)
const notification: NotificationRecord = {
id: 'id',
date: new Date(Date.now() - 10000000),
Expand All @@ -62,14 +60,7 @@ const Template: StoryFn<any> = (args) => {
title: args.videoTitle,
},
}
return (
<NotificationTile
{...args}
notification={notification}
selected={selected}
onSelect={() => setSelected(!selected)}
/>
)
return <NotificationTile {...args} notification={notification} />
}

export const Default = Template.bind({})
Loading

0 comments on commit c864e61

Please sign in to comment.