Improve notifications UX (#337)

Improve notification UX
This commit is contained in:
Andrew Prifer 2022-11-13 14:37:24 +01:00 committed by GitHub
parent a8a9b5ef05
commit a55a34d48f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 59 additions and 39 deletions

View file

@ -11,6 +11,8 @@ import type {
import {useVal} from '@theatre/react'
import getStudio from './getStudio'
import {marked} from 'marked'
import useTooltip from './uiComponents/Popover/useTooltip'
import MinimalTooltip from './uiComponents/Popover/MinimalTooltip'
/**
* Creates a string key unique to a notification with a certain title and message.
@ -181,9 +183,9 @@ const COLORS = {
const IndicatorDot = styled.div<{type: NotificationType}>`
display: flex;
align-items: center;
justify-content: center;
margin-left: 12px;
padding-top: 21px;
::before {
content: '';
@ -308,7 +310,7 @@ const NotifierContainer = styled.div`
flex-direction: column;
gap: 8px;
position: fixed;
right: 8px;
right: 92px;
top: 50px;
width: 500px;
height: 85vh;
@ -330,27 +332,31 @@ const NotificationScroller = styled.div`
`
const EmptyState = styled.div`
align-self: flex-end;
width: fit-content;
padding: 12px;
padding: 8px;
border-radius: 4px;
display: flex;
flex-direction: column;
gap: 12px;
${pointerEventsAutoInNormalMode};
background-color: rgba(40, 43, 47, 0.8);
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.25), 0 2px 6px rgba(0, 0, 0, 0.15);
backdrop-filter: blur(14px);
color: #b4b4b4;
font-size: 12px;
line-height: 1.4;
@supports not (backdrop-filter: blur()) {
background: rgba(40, 43, 47, 0.95);
}
`
//endregion
export const useEmptyNotificationsTooltip = () => {
const {hasNotifications} = useNotifications()
return useTooltip({enabled: !hasNotifications}, () => (
<MinimalTooltip>
<EmptyState>
<NotificationTitle>No notifications</NotificationTitle>
Notifications will appear here when you get them.
</EmptyState>
</MinimalTooltip>
))
}
/**
* The component responsible for rendering the notifications.
*/
@ -363,26 +369,26 @@ export const Notifier = () => {
return (
<NotifierContainer>
{!pinNotifications ? null : toasts.length > 0 ? (
<NotificationScroller onMouseEnter={startPause} onMouseLeave={endPause}>
<div>
{toasts.map((toast) => {
return (
<div key={toast.id}>
{/* message is always a function in our case */}
{/* @ts-ignore */}
{toast.message(toast)}
</div>
)
})}
</div>
</NotificationScroller>
) : (
<EmptyState>
<NotificationTitle>No notifications</NotificationTitle>
Notifications will appear here when you get them.
</EmptyState>
)}
{!pinNotifications
? null
: toasts.length > 0 && (
<NotificationScroller
onMouseEnter={startPause}
onMouseLeave={endPause}
>
<div>
{toasts.map((toast) => {
return (
<div key={toast.id}>
{/* message is always a function in our case */}
{/* @ts-ignore */}
{toast.message(toast)}
</div>
)
})}
</div>
</NotificationScroller>
)}
<ButtonContainer align="side">
{pinNotifications && toasts.length > 0 && (
<Button

View file

@ -20,7 +20,10 @@ import DoubleChevronRight from '@theatre/studio/uiComponents/icons/DoubleChevron
import ToolbarIconButton from '@theatre/studio/uiComponents/toolbar/ToolbarIconButton'
import usePopover from '@theatre/studio/uiComponents/Popover/usePopover'
import MoreMenu from './MoreMenu/MoreMenu'
import {useNotifications} from '@theatre/studio/notify'
import {
useNotifications,
useEmptyNotificationsTooltip,
} from '@theatre/studio/notify'
const Container = styled.div`
height: 36px;
@ -147,6 +150,9 @@ const GlobalToolbar: React.FC = () => {
const {hasNotifications} = useNotifications()
const [notificationsTooltip, notificationsTriggerRef] =
useEmptyNotificationsTooltip()
return (
<Container>
<SubContainer>
@ -174,7 +180,9 @@ const GlobalToolbar: React.FC = () => {
<ExtensionToolbar showLeftDivider toolbarId="global" />
</SubContainer>
<SubContainer>
<ToolbarIconButton
{notificationsTooltip}
<PinButton
ref={notificationsTriggerRef as $IntentionalAny}
onClick={() => {
getStudio().transaction(({stateEditors, drafts}) => {
stateEditors.studio.ahistoric.setPinNotifications(
@ -182,10 +190,13 @@ const GlobalToolbar: React.FC = () => {
)
})
}}
icon={<Bell />}
pinHintIcon={<Bell />}
unpinHintIcon={<Bell />}
pinned={useVal(getStudio().atomP.ahistoric.pinNotifications) ?? false}
>
<Bell />
{hasNotifications && <HasUpdatesBadge type="warning" />}
</ToolbarIconButton>
</PinButton>
{moreMenu.node}
<ToolbarIconButton
ref={moreMenuTriggerRef}

View file

@ -20,7 +20,10 @@ interface PinButtonProps extends ComponentPropsWithRef<'button'> {
}
const PinButton = forwardRef<HTMLButtonElement, PinButtonProps>(
({hint, pinned, icon, pinHintIcon, unpinHintIcon, ...props}, ref) => {
(
{children, hint, pinned, icon, pinHintIcon, unpinHintIcon, ...props},
ref,
) => {
const [hovered, setHovered] = useState(false)
const showHint = hovered || hint
@ -48,6 +51,7 @@ const PinButton = forwardRef<HTMLButtonElement, PinButtonProps>(
? unpinHintIcon
: icon}
</div>
{children}
</Container>
)
},

View file

@ -12,8 +12,7 @@ function Bell(props: React.SVGProps<SVGSVGElement>) {
>
<path
d="M8 1.57c-.416 0-.752.36-.752.804v.482c-1.715.372-3.006 1.994-3.006 3.938v.473c0 1.18-.407 2.32-1.14 3.205l-.173.208a.85.85 0 00-.125.864.75.75 0 00.686.475h9.019a.752.752 0 00.686-.475.845.845 0 00-.125-.864l-.174-.208a5.026 5.026 0 01-1.139-3.205v-.473c0-1.944-1.291-3.566-3.006-3.938v-.482c0-.445-.336-.804-.752-.804zm1.063 12.39c.282-.301.44-.71.44-1.138H6.496c0 .428.158.837.44 1.138.281.302.664.47 1.063.47.4 0 .783-.168 1.064-.47z"
fill="#fff"
fillOpacity={0.6}
fill="currentColor"
/>
</svg>
)