import {useCallback, useEffect, useMemo, useRef, useState} from 'react'
import {View} from 'react-native'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useFocusEffect, useIsFocused} from '@react-navigation/native'
import {useQueryClient} from '@tanstack/react-query'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
import {ComposeIcon2} from '#/lib/icons'
import {
type NativeStackScreenProps,
type NotificationsTabNavigatorParams,
} from '#/lib/routes/types'
import {s} from '#/lib/styles'
import {logger} from '#/logger'
import {isNative} from '#/platform/detection'
import {emitSoftReset, listenSoftReset} from '#/state/events'
import {RQKEY as NOTIFS_RQKEY} from '#/state/queries/notifications/feed'
import {useNotificationSettingsQuery} from '#/state/queries/notifications/settings'
import {
useUnreadNotifications,
useUnreadNotificationsApi,
} from '#/state/queries/notifications/unread'
import {truncateAndInvalidate} from '#/state/queries/util'
import {useSetMinimalShellMode} from '#/state/shell'
import {NotificationFeed} from '#/view/com/notifications/NotificationFeed'
import {Pager} from '#/view/com/pager/Pager'
import {TabBar} from '#/view/com/pager/TabBar'
import {FAB} from '#/view/com/util/fab/FAB'
import {type ListMethods} from '#/view/com/util/List'
import {LoadLatestBtn} from '#/view/com/util/load-latest/LoadLatestBtn'
import {MainScrollProvider} from '#/view/com/util/MainScrollProvider'
import {atoms as a, useTheme} from '#/alf'
import {web} from '#/alf'
import {Admonition} from '#/components/Admonition'
import {ButtonIcon} from '#/components/Button'
import {SettingsGear2_Stroke2_Corner0_Rounded as SettingsIcon} from '#/components/icons/SettingsGear2'
import * as Layout from '#/components/Layout'
import {InlineLinkText, Link} from '#/components/Link'
import {Loader} from '#/components/Loader'
// We don't currently persist this across reloads since
// you gotta visit All to clear the badge anyway.
// But let's at least persist it during the sesssion.
let lastActiveTab = 0
type Props = NativeStackScreenProps<
NotificationsTabNavigatorParams,
'Notifications'
>
export function NotificationsScreen({}: Props) {
const {_} = useLingui()
const {openComposer} = useOpenComposer()
const unreadNotifs = useUnreadNotifications()
const hasNew = !!unreadNotifs
const {checkUnread: checkUnreadAll} = useUnreadNotificationsApi()
const [isLoadingAll, setIsLoadingAll] = useState(false)
const [isLoadingMentions, setIsLoadingMentions] = useState(false)
const initialActiveTab = lastActiveTab
const [activeTab, setActiveTab] = useState(initialActiveTab)
const isLoading = activeTab === 0 ? isLoadingAll : isLoadingMentions
const onPageSelected = useCallback(
(index: number) => {
setActiveTab(index)
lastActiveTab = index
},
[setActiveTab],
)
const queryClient = useQueryClient()
const checkUnreadMentions = useCallback(
async ({invalidate}: {invalidate: boolean}) => {
if (invalidate) {
return truncateAndInvalidate(queryClient, NOTIFS_RQKEY('mentions'))
} else {
// Background polling is not implemented for the mentions tab.
// Just ignore it.
}
},
[queryClient],
)
const sections = useMemo(() => {
return [
{
title: _(msg`All`),
component: (
),
},
{
title: _(msg`Mentions`),
component: (
),
},
]
}, [
_,
hasNew,
checkUnreadAll,
checkUnreadMentions,
activeTab,
isLoadingAll,
isLoadingMentions,
])
return (
Notifications
(
section.title)}
onPressSelected={() => emitSoftReset()}
/>
)}
initialPage={initialActiveTab}>
{sections.map((section, i) => (
{section.component}
))}
openComposer({})}
icon={}
accessibilityRole="button"
accessibilityLabel={_(msg`New post`)}
accessibilityHint=""
/>
)
}
function NotificationsTab({
filter,
isActive,
isLoading,
hasNew,
checkUnread,
setIsLoadingLatest,
}: {
filter: 'all' | 'mentions'
isActive: boolean
isLoading: boolean
hasNew: boolean
checkUnread: ({invalidate}: {invalidate: boolean}) => Promise
setIsLoadingLatest: (v: boolean) => void
}) {
const {_} = useLingui()
const setMinimalShellMode = useSetMinimalShellMode()
const [isScrolledDown, setIsScrolledDown] = useState(false)
const scrollElRef = useRef(null)
const queryClient = useQueryClient()
const isScreenFocused = useIsFocused()
const isFocusedAndActive = isScreenFocused && isActive
// event handlers
// =
const scrollToTop = useCallback(() => {
scrollElRef.current?.scrollToOffset({animated: isNative, offset: 0})
setMinimalShellMode(false)
}, [scrollElRef, setMinimalShellMode])
const onPressLoadLatest = useCallback(() => {
scrollToTop()
if (hasNew) {
// render what we have now
truncateAndInvalidate(queryClient, NOTIFS_RQKEY(filter))
} else if (!isLoading) {
// check with the server
setIsLoadingLatest(true)
checkUnread({invalidate: true})
.catch(() => undefined)
.then(() => setIsLoadingLatest(false))
}
}, [
scrollToTop,
queryClient,
checkUnread,
hasNew,
isLoading,
setIsLoadingLatest,
filter,
])
const onFocusCheckLatest = useNonReactiveCallback(() => {
// on focus, check for latest, but only invalidate if the user
// isnt scrolled down to avoid moving content underneath them
let currentIsScrolledDown
if (isNative) {
currentIsScrolledDown = isScrolledDown
} else {
// On the web, this isn't always updated in time so
// we're just going to look it up synchronously.
currentIsScrolledDown = window.scrollY > 200
}
checkUnread({invalidate: !currentIsScrolledDown})
})
// on-visible setup
// =
useFocusEffect(
useCallback(() => {
if (isFocusedAndActive) {
setMinimalShellMode(false)
logger.debug('NotificationsScreen: Focus')
onFocusCheckLatest()
}
}, [setMinimalShellMode, onFocusCheckLatest, isFocusedAndActive]),
)
useEffect(() => {
if (!isFocusedAndActive) {
return
}
return listenSoftReset(onPressLoadLatest)
}, [onPressLoadLatest, isFocusedAndActive])
return (
<>
checkUnread({invalidate: true})}
onScrolledDownChange={setIsScrolledDown}
scrollElRef={scrollElRef}
ListHeaderComponent={
filter === 'mentions' ? (
) : null
}
/>
{(isScrolledDown || hasNew) && (
)}
>
)
}
function DisabledNotificationsWarning({active}: {active: boolean}) {
const t = useTheme()
const {_} = useLingui()
const {data} = useNotificationSettingsQuery({enabled: active})
if (!data) return null
if (!data.reply.list && !data.quote.list && !data.mention.list) {
// mention tab notifications are disabled
return (
You have completely disabled reply, quote, and mention
notifications, so this tab will no longer update. To adjust this,
visit your{' '}
notification settings
.
)
}
return null
}