-
-
Notifications
You must be signed in to change notification settings - Fork 274
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2f74478
commit deaaf72
Showing
20 changed files
with
648 additions
and
166 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { useContacts } from './useContacts'; | ||
|
||
export const useContactPhoneNumber = (phoneNumber: string) => { | ||
const [contacts] = useContacts(); | ||
const contact = contacts.find((contacts) => contacts.phone_number === phoneNumber); | ||
return contact; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
src/ui/src/components/Main/Notifications/Notification/ExtendedNotification.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { Notification as NotificationType } from '@/contexts/NotificationContext'; | ||
import { Notification } from '.'; | ||
import { motion } from 'framer-motion'; | ||
|
||
interface ExtendedNotificationProps { | ||
notification: NotificationType; | ||
onClose: () => void; | ||
onClick: () => void; | ||
onDragChange: (isDragging: boolean) => void; | ||
} | ||
|
||
export const ExtendedNotification = ({ | ||
notification, | ||
onDragChange, | ||
onClick, | ||
onClose, | ||
}: ExtendedNotificationProps) => { | ||
return ( | ||
<motion.div | ||
layout | ||
key={notification.id} | ||
initial={{ opacity: 0 }} | ||
animate={{ opacity: 1 }} | ||
exit={{ opacity: 0, x: -400 }} | ||
> | ||
<Notification | ||
notification={notification} | ||
onClick={onClick} | ||
onClose={onClose} | ||
onDragChange={onDragChange} | ||
/> | ||
</motion.div> | ||
); | ||
}; |
73 changes: 73 additions & 0 deletions
73
src/ui/src/components/Main/Notifications/Notification/NotificationContent.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { useContactPhoneNumber } from '@/api/hooks/useContactPhoneNumber'; | ||
import { useApp } from '@/contexts/AppsContext/useApp'; | ||
import { Notification } from '@/contexts/NotificationContext'; | ||
import { clsx } from 'clsx'; | ||
import { DateTime } from 'luxon'; | ||
import { ReactNode } from 'react'; | ||
|
||
interface NotificationContentProps { | ||
notification: Notification; | ||
extended?: boolean; | ||
className?: string; | ||
onClick?: () => void; | ||
Actions?: ReactNode; | ||
} | ||
|
||
export const NotificationContent = ({ | ||
Actions, | ||
notification, | ||
className, | ||
onClick, | ||
extended = false, | ||
}: NotificationContentProps) => { | ||
const contact = useContactPhoneNumber(notification.title); | ||
const { title, description, overline, created_at, appId = 'settings' } = notification; | ||
const app = useApp(appId); | ||
const AppIcon = app ? app.Icon : '📢'; | ||
|
||
return ( | ||
<div | ||
onClick={onClick} | ||
className={clsx( | ||
'flex p-3 items-center gap-3 rounded-lg relative outline outline-1 outline-secondary outline-offset-1', | ||
!extended ? 'dark:bg-gray-950 bg-primary shadow-xl' : 'backdrop-blur-md bg-opacity-10', | ||
className, | ||
)} | ||
> | ||
<span className="p-1 bg-secondary rounded-lg text-2xl w-10 h-10 flex flex-col items-center justify-center"> | ||
{AppIcon} | ||
</span> | ||
<div className="flex flex-col overflow-hidden"> | ||
{overline && ( | ||
<span className="uppercase text-[10px] font-semibold line-clamp-3 tracking-wider"> | ||
{overline} | ||
</span> | ||
)} | ||
<span | ||
className={clsx( | ||
'text-primary font-semibold text-sm', | ||
!extended && 'whitespace-pre overflow-ellipsis overflow-hidden', | ||
)} | ||
> | ||
{contact ? contact.name : title} | ||
</span> | ||
<span | ||
className={clsx( | ||
'text-secondary', | ||
!extended && 'whitespace-pre overflow-ellipsis overflow-hidden', | ||
)} | ||
> | ||
{description} | ||
</span> | ||
</div> | ||
|
||
{Actions ? ( | ||
<div className="ml-auto">{Actions}</div> | ||
) : ( | ||
<span className="text-xs opacity-40 absolute top-0 right-0 m-3"> | ||
{DateTime.fromISO(created_at).toFormat('t')} | ||
</span> | ||
)} | ||
</div> | ||
); | ||
}; |
117 changes: 117 additions & 0 deletions
117
src/ui/src/components/Main/Notifications/Notification/NotificationsExtendedList.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import { | ||
AnimatePresence, | ||
motion, | ||
PanInfo, | ||
useAnimate, | ||
useMotionValue, | ||
useSpring, | ||
useTransform, | ||
} from 'framer-motion'; | ||
import { useState } from 'react'; | ||
import { clsx } from 'clsx'; | ||
import { FooterLine } from '@/components/FooterLine'; | ||
import { useNotifications } from '@/contexts/NotificationContext/useNotifications'; | ||
import { Button } from '@/components/ui/button'; | ||
import { ExtendedNotification } from './ExtendedNotification'; | ||
|
||
interface NotificationsExtendedListProps { | ||
onChangeOpen: (isOpen: boolean) => void; | ||
} | ||
export const NotificationsExtendedList = ({ | ||
onChangeOpen: onToggle, | ||
}: NotificationsExtendedListProps) => { | ||
const rootHeight = document.getElementById('root')?.clientHeight; | ||
const [isDraggingNotification, setIsDraggingNotification] = useState(false); | ||
const height = rootHeight || 844; | ||
|
||
const { notifications, remove, clear } = useNotifications(); | ||
const [scope, animate] = useAnimate(); | ||
|
||
const y = useMotionValue(-height + 32); | ||
const transition = useSpring(y, { stiffness: 300, damping: 30 }); | ||
const opacity = useTransform(transition, [-height + 32, -height + 32 * 12, 0], [0, 1, 1]); | ||
|
||
const handleDragEnd = async (_: unknown, panInfo: PanInfo) => { | ||
if (isDraggingNotification) { | ||
return; | ||
} | ||
|
||
const velocity = Math.abs(panInfo.velocity.y); | ||
|
||
if (velocity < 70) { | ||
const isOpen = panInfo.point.y < height / 2; | ||
updateNotificationState(!isOpen); | ||
return; | ||
} | ||
|
||
const isOpen = panInfo.velocity.y > -100; | ||
updateNotificationState(isOpen); | ||
}; | ||
|
||
const updateNotificationState = (isOpen: boolean) => { | ||
if (isOpen) { | ||
animate(scope.current, { y: 0 }); | ||
} else { | ||
animate(scope.current, { y: -height + 32 }); | ||
} | ||
|
||
onToggle(isOpen); | ||
}; | ||
|
||
return ( | ||
<motion.div | ||
ref={scope} | ||
drag={!isDraggingNotification ? 'y' : false} | ||
style={{ y: transition, opacity }} | ||
dragConstraints={{ top: -height + 32, bottom: 0 }} | ||
variants={{ | ||
open: { y: '0%', opacity: 1 }, | ||
closed: { y: 'calc(-100% + 32px)', opacity: 1 }, | ||
}} | ||
transition={{ duration: 0.5 }} | ||
className={clsx( | ||
'absolute top-0 left-0 right-0 z-10 flex flex-col gap-2 h-full backdrop-blur-xl bg-gray-500 bg-opacity-5', | ||
)} | ||
onDragEnd={handleDragEnd} | ||
> | ||
<div className="flex justify-between p-6 pb-2"> | ||
<span className="tracking-wide font-semibold text-xl text-primary">Notifications</span> | ||
|
||
<Button variant="ghost" onClick={clear}> | ||
Clear all | ||
</Button> | ||
</div> | ||
|
||
<div className="flex flex-col overflow-auto mr-2 scrollbar scroll-m-4"> | ||
<div className="flex flex-col gap-2 p-4 pr-2"> | ||
<AnimatePresence> | ||
{notifications.map((notification) => ( | ||
<motion.div | ||
layout | ||
key={notification.id} | ||
initial={{ opacity: 0 }} | ||
animate={{ opacity: 1 }} | ||
exit={{ opacity: 0, x: -400 }} | ||
> | ||
<ExtendedNotification | ||
notification={notification} | ||
onClick={() => onToggle(false)} | ||
onClose={() => remove(notification.id)} | ||
onDragChange={setIsDraggingNotification} | ||
/> | ||
</motion.div> | ||
))} | ||
</AnimatePresence> | ||
</div> | ||
</div> | ||
|
||
{notifications.length === 0 && ( | ||
<div className="flex flex-1 items-center justify-center">No notifications</div> | ||
)} | ||
|
||
<div className="h-8 px-6 flex gap-4 items-center mt-auto justify-center"> | ||
<FooterLine primary /> | ||
</div> | ||
</motion.div> | ||
); | ||
}; |
Oops, something went wrong.