Rough sketch
This commit is contained in:
+56
-53
@@ -73,6 +73,7 @@ import {Provider as IntentDialogProvider} from '#/components/intents/IntentDialo
|
|||||||
import {Provider as PolicyUpdateOverlayProvider} from '#/components/PolicyUpdateOverlay'
|
import {Provider as PolicyUpdateOverlayProvider} from '#/components/PolicyUpdateOverlay'
|
||||||
import {Provider as PortalProvider} from '#/components/Portal'
|
import {Provider as PortalProvider} from '#/components/Portal'
|
||||||
import {Provider as VideoVolumeProvider} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext'
|
import {Provider as VideoVolumeProvider} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext'
|
||||||
|
import {ToastProvider} from '#/components/Toast'
|
||||||
import {Splash} from '#/Splash'
|
import {Splash} from '#/Splash'
|
||||||
import {BottomSheetProvider} from '../modules/bottom-sheet'
|
import {BottomSheetProvider} from '../modules/bottom-sheet'
|
||||||
import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider'
|
import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider'
|
||||||
@@ -133,59 +134,61 @@ function InnerApp() {
|
|||||||
<ContextMenuProvider>
|
<ContextMenuProvider>
|
||||||
<Splash isReady={isReady && hasCheckedReferrer}>
|
<Splash isReady={isReady && hasCheckedReferrer}>
|
||||||
<RootSiblingParent>
|
<RootSiblingParent>
|
||||||
<VideoVolumeProvider>
|
<ToastProvider>
|
||||||
<React.Fragment
|
<VideoVolumeProvider>
|
||||||
// Resets the entire tree below when it changes:
|
<React.Fragment
|
||||||
key={currentAccount?.did}>
|
// Resets the entire tree below when it changes:
|
||||||
<QueryProvider currentDid={currentAccount?.did}>
|
key={currentAccount?.did}>
|
||||||
<PolicyUpdateOverlayProvider>
|
<QueryProvider currentDid={currentAccount?.did}>
|
||||||
<StatsigProvider>
|
<PolicyUpdateOverlayProvider>
|
||||||
<AgeAssuranceProvider>
|
<StatsigProvider>
|
||||||
<ComposerProvider>
|
<AgeAssuranceProvider>
|
||||||
<MessagesProvider>
|
<ComposerProvider>
|
||||||
{/* LabelDefsProvider MUST come before ModerationOptsProvider */}
|
<MessagesProvider>
|
||||||
<LabelDefsProvider>
|
{/* LabelDefsProvider MUST come before ModerationOptsProvider */}
|
||||||
<ModerationOptsProvider>
|
<LabelDefsProvider>
|
||||||
<LoggedOutViewProvider>
|
<ModerationOptsProvider>
|
||||||
<SelectedFeedProvider>
|
<LoggedOutViewProvider>
|
||||||
<HiddenRepliesProvider>
|
<SelectedFeedProvider>
|
||||||
<HomeBadgeProvider>
|
<HiddenRepliesProvider>
|
||||||
<UnreadNotifsProvider>
|
<HomeBadgeProvider>
|
||||||
<BackgroundNotificationPreferencesProvider>
|
<UnreadNotifsProvider>
|
||||||
<MutedThreadsProvider>
|
<BackgroundNotificationPreferencesProvider>
|
||||||
<ProgressGuideProvider>
|
<MutedThreadsProvider>
|
||||||
<ServiceAccountManager>
|
<ProgressGuideProvider>
|
||||||
<HideBottomBarBorderProvider>
|
<ServiceAccountManager>
|
||||||
<GestureHandlerRootView
|
<HideBottomBarBorderProvider>
|
||||||
style={s.h100pct}>
|
<GestureHandlerRootView
|
||||||
<GlobalGestureEventsProvider>
|
style={s.h100pct}>
|
||||||
<IntentDialogProvider>
|
<GlobalGestureEventsProvider>
|
||||||
<TestCtrls />
|
<IntentDialogProvider>
|
||||||
<Shell />
|
<TestCtrls />
|
||||||
<NuxDialogs />
|
<Shell />
|
||||||
</IntentDialogProvider>
|
<NuxDialogs />
|
||||||
</GlobalGestureEventsProvider>
|
</IntentDialogProvider>
|
||||||
</GestureHandlerRootView>
|
</GlobalGestureEventsProvider>
|
||||||
</HideBottomBarBorderProvider>
|
</GestureHandlerRootView>
|
||||||
</ServiceAccountManager>
|
</HideBottomBarBorderProvider>
|
||||||
</ProgressGuideProvider>
|
</ServiceAccountManager>
|
||||||
</MutedThreadsProvider>
|
</ProgressGuideProvider>
|
||||||
</BackgroundNotificationPreferencesProvider>
|
</MutedThreadsProvider>
|
||||||
</UnreadNotifsProvider>
|
</BackgroundNotificationPreferencesProvider>
|
||||||
</HomeBadgeProvider>
|
</UnreadNotifsProvider>
|
||||||
</HiddenRepliesProvider>
|
</HomeBadgeProvider>
|
||||||
</SelectedFeedProvider>
|
</HiddenRepliesProvider>
|
||||||
</LoggedOutViewProvider>
|
</SelectedFeedProvider>
|
||||||
</ModerationOptsProvider>
|
</LoggedOutViewProvider>
|
||||||
</LabelDefsProvider>
|
</ModerationOptsProvider>
|
||||||
</MessagesProvider>
|
</LabelDefsProvider>
|
||||||
</ComposerProvider>
|
</MessagesProvider>
|
||||||
</AgeAssuranceProvider>
|
</ComposerProvider>
|
||||||
</StatsigProvider>
|
</AgeAssuranceProvider>
|
||||||
</PolicyUpdateOverlayProvider>
|
</StatsigProvider>
|
||||||
</QueryProvider>
|
</PolicyUpdateOverlayProvider>
|
||||||
</React.Fragment>
|
</QueryProvider>
|
||||||
</VideoVolumeProvider>
|
</React.Fragment>
|
||||||
|
</VideoVolumeProvider>
|
||||||
|
</ToastProvider>
|
||||||
</RootSiblingParent>
|
</RootSiblingParent>
|
||||||
</Splash>
|
</Splash>
|
||||||
</ContextMenuProvider>
|
</ContextMenuProvider>
|
||||||
|
|||||||
+46
-44
@@ -61,7 +61,7 @@ import {Provider as PolicyUpdateOverlayProvider} from '#/components/PolicyUpdate
|
|||||||
import {Provider as PortalProvider} from '#/components/Portal'
|
import {Provider as PortalProvider} from '#/components/Portal'
|
||||||
import {Provider as ActiveVideoProvider} from '#/components/Post/Embed/VideoEmbed/ActiveVideoWebContext'
|
import {Provider as ActiveVideoProvider} from '#/components/Post/Embed/VideoEmbed/ActiveVideoWebContext'
|
||||||
import {Provider as VideoVolumeProvider} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext'
|
import {Provider as VideoVolumeProvider} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext'
|
||||||
import {ToastContainer} from '#/components/Toast'
|
import {ToastContainer, ToastProvider} from '#/components/Toast'
|
||||||
import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider'
|
import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider'
|
||||||
import {Provider as HideBottomBarBorderProvider} from './lib/hooks/useHideBottomBarBorder'
|
import {Provider as HideBottomBarBorderProvider} from './lib/hooks/useHideBottomBarBorder'
|
||||||
|
|
||||||
@@ -112,28 +112,28 @@ function InnerApp() {
|
|||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<ContextMenuProvider>
|
<ContextMenuProvider>
|
||||||
<RootSiblingParent>
|
<RootSiblingParent>
|
||||||
<VideoVolumeProvider>
|
<ToastProvider>
|
||||||
<ActiveVideoProvider>
|
<VideoVolumeProvider>
|
||||||
<React.Fragment
|
<ActiveVideoProvider>
|
||||||
// Resets the entire tree below when it changes:
|
<React.Fragment
|
||||||
key={currentAccount?.did}>
|
// Resets the entire tree below when it changes:
|
||||||
<QueryProvider currentDid={currentAccount?.did}>
|
key={currentAccount?.did}>
|
||||||
<PolicyUpdateOverlayProvider>
|
<QueryProvider currentDid={currentAccount?.did}>
|
||||||
<StatsigProvider>
|
<PolicyUpdateOverlayProvider>
|
||||||
<AgeAssuranceProvider>
|
<StatsigProvider>
|
||||||
<ComposerProvider>
|
<AgeAssuranceProvider>
|
||||||
<MessagesProvider>
|
<ComposerProvider>
|
||||||
{/* LabelDefsProvider MUST come before ModerationOptsProvider */}
|
<MessagesProvider>
|
||||||
<LabelDefsProvider>
|
{/* LabelDefsProvider MUST come before ModerationOptsProvider */}
|
||||||
<ModerationOptsProvider>
|
<LabelDefsProvider>
|
||||||
<LoggedOutViewProvider>
|
<ModerationOptsProvider>
|
||||||
<SelectedFeedProvider>
|
<LoggedOutViewProvider>
|
||||||
<HiddenRepliesProvider>
|
<SelectedFeedProvider>
|
||||||
<HomeBadgeProvider>
|
<HiddenRepliesProvider>
|
||||||
<UnreadNotifsProvider>
|
<HomeBadgeProvider>
|
||||||
<BackgroundNotificationPreferencesProvider>
|
<UnreadNotifsProvider>
|
||||||
<MutedThreadsProvider>
|
<BackgroundNotificationPreferencesProvider>
|
||||||
<SafeAreaProvider>
|
<MutedThreadsProvider>
|
||||||
<ProgressGuideProvider>
|
<ProgressGuideProvider>
|
||||||
<ServiceConfigProvider>
|
<ServiceConfigProvider>
|
||||||
<HideBottomBarBorderProvider>
|
<HideBottomBarBorderProvider>
|
||||||
@@ -144,26 +144,26 @@ function InnerApp() {
|
|||||||
</HideBottomBarBorderProvider>
|
</HideBottomBarBorderProvider>
|
||||||
</ServiceConfigProvider>
|
</ServiceConfigProvider>
|
||||||
</ProgressGuideProvider>
|
</ProgressGuideProvider>
|
||||||
</SafeAreaProvider>
|
</MutedThreadsProvider>
|
||||||
</MutedThreadsProvider>
|
</BackgroundNotificationPreferencesProvider>
|
||||||
</BackgroundNotificationPreferencesProvider>
|
</UnreadNotifsProvider>
|
||||||
</UnreadNotifsProvider>
|
</HomeBadgeProvider>
|
||||||
</HomeBadgeProvider>
|
</HiddenRepliesProvider>
|
||||||
</HiddenRepliesProvider>
|
</SelectedFeedProvider>
|
||||||
</SelectedFeedProvider>
|
</LoggedOutViewProvider>
|
||||||
</LoggedOutViewProvider>
|
</ModerationOptsProvider>
|
||||||
</ModerationOptsProvider>
|
</LabelDefsProvider>
|
||||||
</LabelDefsProvider>
|
</MessagesProvider>
|
||||||
</MessagesProvider>
|
</ComposerProvider>
|
||||||
</ComposerProvider>
|
</AgeAssuranceProvider>
|
||||||
</AgeAssuranceProvider>
|
</StatsigProvider>
|
||||||
</StatsigProvider>
|
</PolicyUpdateOverlayProvider>
|
||||||
</PolicyUpdateOverlayProvider>
|
</QueryProvider>
|
||||||
</QueryProvider>
|
<ToastContainer />
|
||||||
<ToastContainer />
|
</React.Fragment>
|
||||||
</React.Fragment>
|
</ActiveVideoProvider>
|
||||||
</ActiveVideoProvider>
|
</VideoVolumeProvider>
|
||||||
</VideoVolumeProvider>
|
</ToastProvider>
|
||||||
</RootSiblingParent>
|
</RootSiblingParent>
|
||||||
</ContextMenuProvider>
|
</ContextMenuProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
@@ -201,7 +201,9 @@ function App() {
|
|||||||
<LightboxStateProvider>
|
<LightboxStateProvider>
|
||||||
<PortalProvider>
|
<PortalProvider>
|
||||||
<StarterPackProvider>
|
<StarterPackProvider>
|
||||||
<InnerApp />
|
<SafeAreaProvider>
|
||||||
|
<InnerApp />
|
||||||
|
</SafeAreaProvider>
|
||||||
</StarterPackProvider>
|
</StarterPackProvider>
|
||||||
</PortalProvider>
|
</PortalProvider>
|
||||||
</LightboxStateProvider>
|
</LightboxStateProvider>
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import {createContext, useContext} from 'react'
|
||||||
|
|
||||||
|
import {type ToastApi} from '#/components/Toast/types'
|
||||||
|
|
||||||
|
export type ToastContext = ToastApi
|
||||||
|
|
||||||
|
export const Context = createContext<ToastContext>({
|
||||||
|
show() {},
|
||||||
|
})
|
||||||
|
|
||||||
|
export function useToast() {
|
||||||
|
return useContext(Context)
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import {createPortalGroup} from '#/components/Portal'
|
||||||
|
|
||||||
|
const portal = createPortalGroup()
|
||||||
|
|
||||||
|
export const Provider = portal.Provider
|
||||||
|
export const Outlet = portal.Outlet
|
||||||
|
export const Portal = portal.Portal
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import {useEffect, useMemo, useRef, useState} from 'react'
|
import {useCallback, useEffect, useMemo, useRef, useState} from 'react'
|
||||||
import {AccessibilityInfo} from 'react-native'
|
import {AccessibilityInfo, View} from 'react-native'
|
||||||
import {
|
import {
|
||||||
Gesture,
|
Gesture,
|
||||||
GestureDetector,
|
GestureDetector,
|
||||||
@@ -18,15 +18,100 @@ import Animated, {
|
|||||||
} from 'react-native-reanimated'
|
} from 'react-native-reanimated'
|
||||||
import RootSiblings from 'react-native-root-siblings'
|
import RootSiblings from 'react-native-root-siblings'
|
||||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||||
|
import {nanoid} from 'nanoid/non-secure'
|
||||||
|
|
||||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||||
import {atoms as a} from '#/alf'
|
import {atoms as a} from '#/alf'
|
||||||
import {DEFAULT_TOAST_DURATION} from '#/components/Toast/const'
|
import {DEFAULT_TOAST_DURATION} from '#/components/Toast/const'
|
||||||
|
import {Context} from '#/components/Toast/Context'
|
||||||
|
import * as Portal from '#/components/Toast/Portal'
|
||||||
import {Toast} from '#/components/Toast/Toast'
|
import {Toast} from '#/components/Toast/Toast'
|
||||||
import {type ToastApi, type ToastType} from '#/components/Toast/types'
|
import {
|
||||||
|
type ToastApi,
|
||||||
|
type ToastProps,
|
||||||
|
type ToastType,
|
||||||
|
} from '#/components/Toast/types'
|
||||||
|
|
||||||
|
export {useToast} from '#/components/Toast/Context'
|
||||||
|
export {Outlet} from '#/components/Toast/Portal'
|
||||||
|
|
||||||
const TOAST_ANIMATION_DURATION = 300
|
const TOAST_ANIMATION_DURATION = 300
|
||||||
|
|
||||||
|
export function ToastProvider({children}: {children: React.ReactNode}) {
|
||||||
|
const {top} = useSafeAreaInsets()
|
||||||
|
const [toasts, setToasts] = useState<
|
||||||
|
{
|
||||||
|
id: string
|
||||||
|
toast: ToastProps
|
||||||
|
timeout: NodeJS.Timeout
|
||||||
|
}[]
|
||||||
|
>([])
|
||||||
|
|
||||||
|
const show = useCallback<ToastApi['show']>(props => {
|
||||||
|
AccessibilityInfo.announceForAccessibility(props.a11yLabel)
|
||||||
|
|
||||||
|
setToasts(prevToasts => {
|
||||||
|
const id = nanoid()
|
||||||
|
return [
|
||||||
|
...prevToasts,
|
||||||
|
{
|
||||||
|
id,
|
||||||
|
toast: props,
|
||||||
|
timeout: setTimeout(() => {
|
||||||
|
setToasts(currentToasts => currentToasts.filter(t => t.id !== id))
|
||||||
|
}, props.duration || DEFAULT_TOAST_DURATION),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
setToasts(toasts => {
|
||||||
|
toasts.map(({timeout}) => {
|
||||||
|
clearTimeout(timeout)
|
||||||
|
})
|
||||||
|
return toasts
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const ctx = useMemo(
|
||||||
|
() => ({
|
||||||
|
show,
|
||||||
|
}),
|
||||||
|
[show],
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Context.Provider value={ctx}>
|
||||||
|
<Portal.Provider>
|
||||||
|
{children}
|
||||||
|
|
||||||
|
{toasts.length ? (
|
||||||
|
<Portal.Portal>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.absolute,
|
||||||
|
a.gap_sm,
|
||||||
|
{
|
||||||
|
flexDirection: 'column-reverse',
|
||||||
|
top: top,
|
||||||
|
left: a.px_lg.paddingLeft,
|
||||||
|
right: a.px_lg.paddingLeft,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
{toasts.map(({id, toast}) => (
|
||||||
|
<Toast key={id} type={toast.type} content={toast.content} />
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</Portal.Portal>
|
||||||
|
) : null}
|
||||||
|
</Portal.Provider>
|
||||||
|
</Context.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export function ToastContainer() {
|
export function ToastContainer() {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,15 +2,26 @@
|
|||||||
* Note: relies on styles in #/styles.css
|
* Note: relies on styles in #/styles.css
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {useEffect, useState} from 'react'
|
import {useCallback, useEffect, useMemo, useState} from 'react'
|
||||||
import {AccessibilityInfo, Pressable, View} from 'react-native'
|
import {AccessibilityInfo, Pressable, View} from 'react-native'
|
||||||
|
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||||
import {msg} from '@lingui/macro'
|
import {msg} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
import {nanoid} from 'nanoid/non-secure'
|
||||||
|
|
||||||
import {atoms as a, useBreakpoints} from '#/alf'
|
import {atoms as a, useBreakpoints} from '#/alf'
|
||||||
import {DEFAULT_TOAST_DURATION} from '#/components/Toast/const'
|
import {DEFAULT_TOAST_DURATION} from '#/components/Toast/const'
|
||||||
|
import {Context} from '#/components/Toast/Context'
|
||||||
|
import * as Portal from '#/components/Toast/Portal'
|
||||||
import {Toast} from '#/components/Toast/Toast'
|
import {Toast} from '#/components/Toast/Toast'
|
||||||
import {type ToastApi, type ToastType} from '#/components/Toast/types'
|
import {
|
||||||
|
type ToastApi,
|
||||||
|
type ToastProps,
|
||||||
|
type ToastType,
|
||||||
|
} from '#/components/Toast/types'
|
||||||
|
|
||||||
|
export {useToast} from '#/components/Toast/Context'
|
||||||
|
export {Outlet} from '#/components/Toast/Portal'
|
||||||
|
|
||||||
const TOAST_ANIMATION_STYLES = {
|
const TOAST_ANIMATION_STYLES = {
|
||||||
entering: {
|
entering: {
|
||||||
@@ -31,6 +42,85 @@ let globalSetActiveToast: GlobalSetActiveToast | undefined
|
|||||||
let toastTimeout: NodeJS.Timeout | undefined
|
let toastTimeout: NodeJS.Timeout | undefined
|
||||||
type ToastContainerProps = {}
|
type ToastContainerProps = {}
|
||||||
|
|
||||||
|
export function ToastProvider({children}: {children: React.ReactNode}) {
|
||||||
|
const {bottom} = useSafeAreaInsets()
|
||||||
|
const {gtPhone} = useBreakpoints()
|
||||||
|
const [toasts, setToasts] = useState<
|
||||||
|
{
|
||||||
|
id: string
|
||||||
|
toast: ToastProps
|
||||||
|
timeout: NodeJS.Timeout
|
||||||
|
}[]
|
||||||
|
>([])
|
||||||
|
|
||||||
|
const show = useCallback<ToastApi['show']>(props => {
|
||||||
|
setToasts(prevToasts => {
|
||||||
|
const id = nanoid()
|
||||||
|
return [
|
||||||
|
...prevToasts,
|
||||||
|
{
|
||||||
|
id,
|
||||||
|
toast: props,
|
||||||
|
timeout: setTimeout(() => {
|
||||||
|
setToasts(currentToasts => currentToasts.filter(t => t.id !== id))
|
||||||
|
}, props.duration || DEFAULT_TOAST_DURATION),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
setToasts(toasts => {
|
||||||
|
toasts.map(({timeout}) => {
|
||||||
|
clearTimeout(timeout)
|
||||||
|
})
|
||||||
|
return toasts
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const ctx = useMemo(
|
||||||
|
() => ({
|
||||||
|
show,
|
||||||
|
}),
|
||||||
|
[show],
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Context.Provider value={ctx}>
|
||||||
|
<Portal.Provider>
|
||||||
|
{children}
|
||||||
|
|
||||||
|
{toasts.length && (
|
||||||
|
<Portal.Portal>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.fixed,
|
||||||
|
a.gap_sm,
|
||||||
|
{
|
||||||
|
flexDirection: 'column-reverse',
|
||||||
|
left: a.px_xl.paddingLeft,
|
||||||
|
right: a.px_xl.paddingLeft,
|
||||||
|
bottom: Math.max(bottom, a.px_xl.paddingLeft),
|
||||||
|
},
|
||||||
|
gtPhone && [
|
||||||
|
{
|
||||||
|
maxWidth: 380,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]}>
|
||||||
|
{toasts.map(({id, toast}) => (
|
||||||
|
<Toast key={id} content={toast.content} type={toast.type} />
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</Portal.Portal>
|
||||||
|
)}
|
||||||
|
</Portal.Provider>
|
||||||
|
</Context.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export const ToastContainer: React.FC<ToastContainerProps> = ({}) => {
|
export const ToastContainer: React.FC<ToastContainerProps> = ({}) => {
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const {gtPhone} = useBreakpoints()
|
const {gtPhone} = useBreakpoints()
|
||||||
|
|||||||
@@ -1,24 +1,26 @@
|
|||||||
export type ToastType = 'default' | 'success' | 'error' | 'warning' | 'info'
|
export type ToastType = 'default' | 'success' | 'error' | 'warning' | 'info'
|
||||||
|
|
||||||
export type ToastApi = {
|
export type ToastProps = {
|
||||||
show: (props: {
|
/**
|
||||||
/**
|
* The type of toast to show. This determines the styling and icon used.
|
||||||
* The type of toast to show. This determines the styling and icon used.
|
*/
|
||||||
*/
|
type: ToastType
|
||||||
type: ToastType
|
/**
|
||||||
/**
|
* A string, `Text`, or `Span` components to render inside the toast. This
|
||||||
* A string, `Text`, or `Span` components to render inside the toast. This
|
* allows additional formatting of the content, but should not be used for
|
||||||
* allows additional formatting of the content, but should not be used for
|
* interactive elements link links or buttons.
|
||||||
* interactive elements link links or buttons.
|
*/
|
||||||
*/
|
content: React.ReactNode | string
|
||||||
content: React.ReactNode | string
|
/**
|
||||||
/**
|
* Accessibility label for the toast, used for screen readers.
|
||||||
* Accessibility label for the toast, used for screen readers.
|
*/
|
||||||
*/
|
a11yLabel: string
|
||||||
a11yLabel: string
|
/**
|
||||||
/**
|
* Defaults to `DEFAULT_TOAST_DURATION` from `#components/Toast/const`.
|
||||||
* Defaults to `DEFAULT_TOAST_DURATION` from `#components/Toast/const`.
|
*/
|
||||||
*/
|
duration?: number
|
||||||
duration?: number
|
}
|
||||||
}) => void
|
|
||||||
|
export type ToastApi = {
|
||||||
|
show: (props: ToastProps) => void
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import {View} from 'react-native'
|
|||||||
import Animated from 'react-native-reanimated'
|
import Animated from 'react-native-reanimated'
|
||||||
import {msg} from '@lingui/macro'
|
import {msg} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
import {useNavigation} from '@react-navigation/native'
|
||||||
|
|
||||||
import {HITSLOP_10} from '#/lib/constants'
|
import {HITSLOP_10} from '#/lib/constants'
|
||||||
import {PressableScale} from '#/lib/custom-animations/PressableScale'
|
import {PressableScale} from '#/lib/custom-animations/PressableScale'
|
||||||
@@ -16,6 +17,7 @@ import {ButtonIcon} from '#/components/Button'
|
|||||||
import {Hashtag_Stroke2_Corner0_Rounded as FeedsIcon} from '#/components/icons/Hashtag'
|
import {Hashtag_Stroke2_Corner0_Rounded as FeedsIcon} from '#/components/icons/Hashtag'
|
||||||
import * as Layout from '#/components/Layout'
|
import * as Layout from '#/components/Layout'
|
||||||
import {Link} from '#/components/Link'
|
import {Link} from '#/components/Link'
|
||||||
|
import {IS_DEV} from '#/env'
|
||||||
|
|
||||||
export function HomeHeaderLayoutMobile({
|
export function HomeHeaderLayoutMobile({
|
||||||
children,
|
children,
|
||||||
@@ -29,6 +31,7 @@ export function HomeHeaderLayoutMobile({
|
|||||||
const headerMinimalShellTransform = useMinimalShellHeaderTransform()
|
const headerMinimalShellTransform = useMinimalShellHeaderTransform()
|
||||||
const {hasSession} = useSession()
|
const {hasSession} = useSession()
|
||||||
const playHaptic = useHaptics()
|
const playHaptic = useHaptics()
|
||||||
|
const navigate = useNavigation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Animated.View
|
<Animated.View
|
||||||
@@ -55,8 +58,12 @@ export function HomeHeaderLayoutMobile({
|
|||||||
<PressableScale
|
<PressableScale
|
||||||
targetScale={0.9}
|
targetScale={0.9}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
playHaptic('Light')
|
if (IS_DEV) {
|
||||||
emitSoftReset()
|
navigate.navigate('Debug')
|
||||||
|
} else {
|
||||||
|
playHaptic('Light')
|
||||||
|
emitSoftReset()
|
||||||
|
}
|
||||||
}}>
|
}}>
|
||||||
<Logo width={30} />
|
<Logo width={30} />
|
||||||
</PressableScale>
|
</PressableScale>
|
||||||
|
|||||||
@@ -4,14 +4,30 @@ import {show as deprecatedShow} from '#/view/com/util/Toast'
|
|||||||
import {atoms as a} from '#/alf'
|
import {atoms as a} from '#/alf'
|
||||||
import {Button, ButtonText} from '#/components/Button'
|
import {Button, ButtonText} from '#/components/Button'
|
||||||
import {toast} from '#/components/Toast'
|
import {toast} from '#/components/Toast'
|
||||||
|
import {useToast} from '#/components/Toast/Context'
|
||||||
import {Toast} from '#/components/Toast/Toast'
|
import {Toast} from '#/components/Toast/Toast'
|
||||||
import {H1} from '#/components/Typography'
|
import {H1} from '#/components/Typography'
|
||||||
|
|
||||||
export function Toasts() {
|
export function Toasts() {
|
||||||
|
const {show} = useToast()
|
||||||
return (
|
return (
|
||||||
<View style={[a.gap_md]}>
|
<View style={[a.gap_md]}>
|
||||||
<H1>Toast Examples</H1>
|
<H1>Toast Examples</H1>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
label="show toast"
|
||||||
|
size="large"
|
||||||
|
color="primary"
|
||||||
|
onPress={() => {
|
||||||
|
show({
|
||||||
|
type: 'default',
|
||||||
|
content: 'This is a toast example',
|
||||||
|
a11yLabel: 'This is a toast example',
|
||||||
|
})
|
||||||
|
}}>
|
||||||
|
<ButtonText>New Toast</ButtonText>
|
||||||
|
</Button>
|
||||||
|
|
||||||
<View style={[a.gap_md]}>
|
<View style={[a.gap_md]}>
|
||||||
<Pressable
|
<Pressable
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ function StorybookInner() {
|
|||||||
<ButtonText>Dark</ButtonText>
|
<ButtonText>Dark</ButtonText>
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
|
<Toasts />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
color="primary"
|
||||||
@@ -106,7 +107,6 @@ function StorybookInner() {
|
|||||||
<Theming />
|
<Theming />
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
|
|
||||||
<Toasts />
|
|
||||||
<Buttons />
|
<Buttons />
|
||||||
<Forms />
|
<Forms />
|
||||||
<Typography />
|
<Typography />
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import {
|
|||||||
usePolicyUpdateContext,
|
usePolicyUpdateContext,
|
||||||
} from '#/components/PolicyUpdateOverlay'
|
} from '#/components/PolicyUpdateOverlay'
|
||||||
import {Outlet as PortalOutlet} from '#/components/Portal'
|
import {Outlet as PortalOutlet} from '#/components/Portal'
|
||||||
|
import {Outlet as ToastPortalOutlet} from '#/components/Toast/Portal'
|
||||||
import {RoutesContainer, TabsNavigator} from '#/Navigation'
|
import {RoutesContainer, TabsNavigator} from '#/Navigation'
|
||||||
import {BottomSheetOutlet} from '../../../modules/bottom-sheet'
|
import {BottomSheetOutlet} from '../../../modules/bottom-sheet'
|
||||||
import {updateActiveViewAsync} from '../../../modules/expo-bluesky-swiss-army/src/VisibilityView'
|
import {updateActiveViewAsync} from '../../../modules/expo-bluesky-swiss-army/src/VisibilityView'
|
||||||
@@ -172,6 +173,7 @@ function ShellInner() {
|
|||||||
<>
|
<>
|
||||||
<PortalOutlet />
|
<PortalOutlet />
|
||||||
<BottomSheetOutlet />
|
<BottomSheetOutlet />
|
||||||
|
<ToastPortalOutlet />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import {
|
|||||||
usePolicyUpdateContext,
|
usePolicyUpdateContext,
|
||||||
} from '#/components/PolicyUpdateOverlay'
|
} from '#/components/PolicyUpdateOverlay'
|
||||||
import {Outlet as PortalOutlet} from '#/components/Portal'
|
import {Outlet as PortalOutlet} from '#/components/Portal'
|
||||||
|
import {Outlet as ToastPortalOutlet} from '#/components/Toast/Portal'
|
||||||
import {FlatNavigator, RoutesContainer} from '#/Navigation'
|
import {FlatNavigator, RoutesContainer} from '#/Navigation'
|
||||||
import {Composer} from './Composer.web'
|
import {Composer} from './Composer.web'
|
||||||
import {DrawerContent} from './Drawer'
|
import {DrawerContent} from './Drawer'
|
||||||
@@ -84,6 +85,7 @@ function ShellInner() {
|
|||||||
{policyUpdateState.completed && (
|
{policyUpdateState.completed && (
|
||||||
<>
|
<>
|
||||||
<PortalOutlet />
|
<PortalOutlet />
|
||||||
|
<ToastPortalOutlet />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user