Activity notification settings (#8485)
Co-authored-by: Eric Bailey <git@esb.lol> Co-authored-by: Samuel Newman <mozzius@protonmail.com> Co-authored-by: hailey <me@haileyok.com>
This commit is contained in:
@@ -3,6 +3,7 @@ import {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
@@ -30,31 +31,33 @@ const BUBBLE_SHADOW_OFFSET = ARROW_SIZE / 3 // vibes-based, provide more shadow
|
||||
|
||||
type TooltipContextType = {
|
||||
position: 'top' | 'bottom'
|
||||
ready: boolean
|
||||
visible: boolean
|
||||
onVisibleChange: (visible: boolean) => void
|
||||
}
|
||||
|
||||
type TargetMeasurements = {
|
||||
x: number
|
||||
y: number
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
|
||||
type TargetContextType = {
|
||||
targetMeasurements:
|
||||
| {
|
||||
x: number
|
||||
y: number
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
| undefined
|
||||
targetRef: React.RefObject<View>
|
||||
targetMeasurements: TargetMeasurements | undefined
|
||||
setTargetMeasurements: (measurements: TargetMeasurements) => void
|
||||
shouldMeasure: boolean
|
||||
}
|
||||
|
||||
const TooltipContext = createContext<TooltipContextType>({
|
||||
position: 'bottom',
|
||||
ready: false,
|
||||
visible: false,
|
||||
onVisibleChange: () => {},
|
||||
})
|
||||
|
||||
const TargetContext = createContext<TargetContextType>({
|
||||
targetMeasurements: undefined,
|
||||
targetRef: {current: null},
|
||||
setTargetMeasurements: () => {},
|
||||
shouldMeasure: false,
|
||||
})
|
||||
|
||||
export function Outer({
|
||||
@@ -68,21 +71,12 @@ export function Outer({
|
||||
visible: boolean
|
||||
onVisibleChange: (visible: boolean) => void
|
||||
}) {
|
||||
/**
|
||||
* Whether we have measured the target and are ready to show the tooltip.
|
||||
*/
|
||||
const [ready, setReady] = useState(false)
|
||||
/**
|
||||
* Lagging state to track the externally-controlled visibility of the
|
||||
* tooltip.
|
||||
* tooltip, which needs to wait for the target to be measured before
|
||||
* actually being shown.
|
||||
*/
|
||||
const [prevRequestVisible, setPrevRequestVisible] = useState<
|
||||
boolean | undefined
|
||||
>()
|
||||
/**
|
||||
* Needs to reference the element this Tooltip is attached to.
|
||||
*/
|
||||
const targetRef = useRef<View>(null)
|
||||
const [visible, setVisible] = useState<boolean>(false)
|
||||
const [targetMeasurements, setTargetMeasurements] = useState<
|
||||
| {
|
||||
x: number
|
||||
@@ -93,33 +87,24 @@ export function Outer({
|
||||
| undefined
|
||||
>(undefined)
|
||||
|
||||
if (requestVisible && !prevRequestVisible) {
|
||||
setPrevRequestVisible(true)
|
||||
|
||||
if (targetRef.current) {
|
||||
/*
|
||||
* Once opened, measure the dimensions and position of the target
|
||||
*/
|
||||
targetRef.current.measure((_x, _y, width, height, pageX, pageY) => {
|
||||
if (pageX !== undefined && pageY !== undefined && width && height) {
|
||||
setTargetMeasurements({x: pageX, y: pageY, width, height})
|
||||
setReady(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if (!requestVisible && prevRequestVisible) {
|
||||
setPrevRequestVisible(false)
|
||||
if (requestVisible && !visible && targetMeasurements) {
|
||||
setVisible(true)
|
||||
} else if (!requestVisible && visible) {
|
||||
setVisible(false)
|
||||
setTargetMeasurements(undefined)
|
||||
setReady(false)
|
||||
}
|
||||
|
||||
const ctx = useMemo(
|
||||
() => ({position, ready, onVisibleChange}),
|
||||
[position, ready, onVisibleChange],
|
||||
() => ({position, visible, onVisibleChange}),
|
||||
[position, visible, onVisibleChange],
|
||||
)
|
||||
const targetCtx = useMemo(
|
||||
() => ({targetMeasurements, targetRef}),
|
||||
[targetMeasurements, targetRef],
|
||||
() => ({
|
||||
targetMeasurements,
|
||||
setTargetMeasurements,
|
||||
shouldMeasure: requestVisible,
|
||||
}),
|
||||
[requestVisible, targetMeasurements, setTargetMeasurements],
|
||||
)
|
||||
|
||||
return (
|
||||
@@ -132,7 +117,20 @@ export function Outer({
|
||||
}
|
||||
|
||||
export function Target({children}: {children: React.ReactNode}) {
|
||||
const {targetRef} = useContext(TargetContext)
|
||||
const {shouldMeasure, setTargetMeasurements} = useContext(TargetContext)
|
||||
const targetRef = useRef<View>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (!shouldMeasure) return
|
||||
/*
|
||||
* Once opened, measure the dimensions and position of the target
|
||||
*/
|
||||
targetRef.current?.measure((_x, _y, width, height, pageX, pageY) => {
|
||||
if (pageX !== undefined && pageY !== undefined && width && height) {
|
||||
setTargetMeasurements({x: pageX, y: pageY, width, height})
|
||||
}
|
||||
})
|
||||
}, [shouldMeasure, setTargetMeasurements])
|
||||
|
||||
return (
|
||||
<View collapsable={false} ref={targetRef}>
|
||||
@@ -148,13 +146,13 @@ export function Content({
|
||||
children: React.ReactNode
|
||||
label: string
|
||||
}) {
|
||||
const {position, ready, onVisibleChange} = useContext(TooltipContext)
|
||||
const {position, visible, onVisibleChange} = useContext(TooltipContext)
|
||||
const {targetMeasurements} = useContext(TargetContext)
|
||||
const requestClose = useCallback(() => {
|
||||
onVisibleChange(false)
|
||||
}, [onVisibleChange])
|
||||
|
||||
if (!ready || !targetMeasurements) return null
|
||||
if (!visible || !targetMeasurements) return null
|
||||
|
||||
return (
|
||||
<Portal>
|
||||
|
||||
@@ -13,10 +13,12 @@ import {Text} from '#/components/Typography'
|
||||
|
||||
type TooltipContextType = {
|
||||
position: 'top' | 'bottom'
|
||||
onVisibleChange: (open: boolean) => void
|
||||
}
|
||||
|
||||
const TooltipContext = createContext<TooltipContextType>({
|
||||
position: 'bottom',
|
||||
onVisibleChange: () => {},
|
||||
})
|
||||
|
||||
export function Outer({
|
||||
@@ -30,7 +32,10 @@ export function Outer({
|
||||
visible: boolean
|
||||
onVisibleChange: (visible: boolean) => void
|
||||
}) {
|
||||
const ctx = useMemo(() => ({position}), [position])
|
||||
const ctx = useMemo(
|
||||
() => ({position, onVisibleChange}),
|
||||
[position, onVisibleChange],
|
||||
)
|
||||
return (
|
||||
<Popover.Root open={visible} onOpenChange={onVisibleChange}>
|
||||
<TooltipContext.Provider value={ctx}>{children}</TooltipContext.Provider>
|
||||
@@ -54,7 +59,7 @@ export function Content({
|
||||
label: string
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const {position} = useContext(TooltipContext)
|
||||
const {position, onVisibleChange} = useContext(TooltipContext)
|
||||
return (
|
||||
<Popover.Portal>
|
||||
<Popover.Content
|
||||
@@ -63,6 +68,7 @@ export function Content({
|
||||
side={position}
|
||||
sideOffset={4}
|
||||
collisionPadding={MIN_EDGE_SPACE}
|
||||
onInteractOutside={() => onVisibleChange(false)}
|
||||
style={flatten([
|
||||
a.rounded_sm,
|
||||
select(t.name, {
|
||||
|
||||
Reference in New Issue
Block a user