Use react-query to cache notifications (#625)
* Use react-query to cache notifications * Fix imports * Cleanup * Limit unseen notifs query * Catch the bounced query * Don't use interval * Unused var * Avoid flash of page nav * Give notification question priority & 2 lines * Right justify timestamps * Rewording * Margin * Simplify error msg * Be explicit about limit for unseen notifs * Pass limit > 0
This commit is contained in:
parent
a22b29ad6d
commit
cfbb78af48
|
@ -12,7 +12,7 @@ import {
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useUser } from 'web/hooks/use-user'
|
import { usePrivateUser, useUser } from 'web/hooks/use-user'
|
||||||
import { firebaseLogout, User } from 'web/lib/firebase/users'
|
import { firebaseLogout, User } from 'web/lib/firebase/users'
|
||||||
import { ManifoldLogo } from './manifold-logo'
|
import { ManifoldLogo } from './manifold-logo'
|
||||||
import { MenuButton } from './menu'
|
import { MenuButton } from './menu'
|
||||||
|
@ -26,8 +26,9 @@ import { groupPath } from 'web/lib/firebase/groups'
|
||||||
import { trackCallback, withTracking } from 'web/lib/service/analytics'
|
import { trackCallback, withTracking } from 'web/lib/service/analytics'
|
||||||
import { Group } from 'common/group'
|
import { Group } from 'common/group'
|
||||||
import { Spacer } from '../layout/spacer'
|
import { Spacer } from '../layout/spacer'
|
||||||
import { usePreferredNotifications } from 'web/hooks/use-notifications'
|
import { useUnseenPreferredNotifications } from 'web/hooks/use-notifications'
|
||||||
import { setNotificationsAsSeen } from 'web/pages/notifications'
|
import { setNotificationsAsSeen } from 'web/pages/notifications'
|
||||||
|
import { PrivateUser } from 'common/user'
|
||||||
|
|
||||||
function getNavigation() {
|
function getNavigation() {
|
||||||
return [
|
return [
|
||||||
|
@ -186,6 +187,7 @@ export default function Sidebar(props: { className?: string }) {
|
||||||
const currentPage = router.pathname
|
const currentPage = router.pathname
|
||||||
|
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
|
const privateUser = usePrivateUser(user?.id)
|
||||||
const navigationOptions = !user ? signedOutNavigation : getNavigation()
|
const navigationOptions = !user ? signedOutNavigation : getNavigation()
|
||||||
const mobileNavigationOptions = !user
|
const mobileNavigationOptions = !user
|
||||||
? signedOutMobileNavigation
|
? signedOutMobileNavigation
|
||||||
|
@ -220,11 +222,13 @@ export default function Sidebar(props: { className?: string }) {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{privateUser && (
|
||||||
<GroupsList
|
<GroupsList
|
||||||
currentPage={router.asPath}
|
currentPage={router.asPath}
|
||||||
memberItems={memberItems}
|
memberItems={memberItems}
|
||||||
user={user}
|
privateUser={privateUser}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Desktop navigation */}
|
{/* Desktop navigation */}
|
||||||
|
@ -243,11 +247,13 @@ export default function Sidebar(props: { className?: string }) {
|
||||||
<div className="h-[1px] bg-gray-300" />
|
<div className="h-[1px] bg-gray-300" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{privateUser && (
|
||||||
<GroupsList
|
<GroupsList
|
||||||
currentPage={router.asPath}
|
currentPage={router.asPath}
|
||||||
memberItems={memberItems}
|
memberItems={memberItems}
|
||||||
user={user}
|
privateUser={privateUser}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
)
|
)
|
||||||
|
@ -256,13 +262,16 @@ export default function Sidebar(props: { className?: string }) {
|
||||||
function GroupsList(props: {
|
function GroupsList(props: {
|
||||||
currentPage: string
|
currentPage: string
|
||||||
memberItems: Item[]
|
memberItems: Item[]
|
||||||
user: User | null | undefined
|
privateUser: PrivateUser
|
||||||
}) {
|
}) {
|
||||||
const { currentPage, memberItems, user } = props
|
const { currentPage, memberItems, privateUser } = props
|
||||||
const preferredNotifications = usePreferredNotifications(user?.id, {
|
const preferredNotifications = useUnseenPreferredNotifications(
|
||||||
unseenOnly: true,
|
privateUser,
|
||||||
|
{
|
||||||
customHref: '/group/',
|
customHref: '/group/',
|
||||||
})
|
},
|
||||||
|
memberItems.length > 0 ? memberItems.length : undefined
|
||||||
|
)
|
||||||
|
|
||||||
// Set notification as seen if our current page is equal to the isSeenOnHref property
|
// Set notification as seen if our current page is equal to the isSeenOnHref property
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -4,45 +4,53 @@ import { Row } from 'web/components/layout/row'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { usePrivateUser, useUser } from 'web/hooks/use-user'
|
import { usePrivateUser, useUser } from 'web/hooks/use-user'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { usePreferredGroupedNotifications } from 'web/hooks/use-notifications'
|
import { useUnseenPreferredNotificationGroups } from 'web/hooks/use-notifications'
|
||||||
import { NOTIFICATIONS_PER_PAGE } from 'web/pages/notifications'
|
import { NOTIFICATIONS_PER_PAGE } from 'web/pages/notifications'
|
||||||
import { requestBonuses } from 'web/lib/firebase/api-call'
|
import { requestBonuses } from 'web/lib/firebase/api-call'
|
||||||
|
import { PrivateUser } from 'common/user'
|
||||||
|
|
||||||
export default function NotificationsIcon(props: { className?: string }) {
|
export default function NotificationsIcon(props: { className?: string }) {
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
const privateUser = usePrivateUser(user?.id)
|
const privateUser = usePrivateUser(user?.id)
|
||||||
const notifications = usePreferredGroupedNotifications(privateUser?.id, {
|
|
||||||
unseenOnly: true,
|
|
||||||
})
|
|
||||||
const [seen, setSeen] = useState(false)
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!privateUser) return
|
if (
|
||||||
|
privateUser &&
|
||||||
if (Date.now() - (privateUser.lastTimeCheckedBonuses ?? 0) > 65 * 1000)
|
privateUser.lastTimeCheckedBonuses &&
|
||||||
requestBonuses({}).catch((error) => {
|
Date.now() - privateUser.lastTimeCheckedBonuses > 1000 * 70
|
||||||
console.log("couldn't get bonuses:", error.message)
|
)
|
||||||
})
|
requestBonuses({}).catch(() => console.log('no bonuses for you (yet)'))
|
||||||
}, [privateUser])
|
}, [privateUser])
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
useEffect(() => {
|
|
||||||
if (router.pathname.endsWith('notifications')) return setSeen(true)
|
|
||||||
else setSeen(false)
|
|
||||||
}, [router.pathname])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row className={clsx('justify-center')}>
|
<Row className={clsx('justify-center')}>
|
||||||
<div className={'relative'}>
|
<div className={'relative'}>
|
||||||
{!seen && notifications && notifications.length > 0 && (
|
{privateUser && <UnseenNotificationsBubble privateUser={privateUser} />}
|
||||||
<div className="-mt-0.75 absolute ml-3.5 min-w-[15px] rounded-full bg-indigo-500 p-[2px] text-center text-[10px] leading-3 text-white lg:-mt-1 lg:ml-2">
|
|
||||||
{notifications.length > NOTIFICATIONS_PER_PAGE
|
|
||||||
? `${NOTIFICATIONS_PER_PAGE}+`
|
|
||||||
: notifications.length}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<BellIcon className={clsx(props.className)} />
|
<BellIcon className={clsx(props.className)} />
|
||||||
</div>
|
</div>
|
||||||
</Row>
|
</Row>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
function UnseenNotificationsBubble(props: { privateUser: PrivateUser }) {
|
||||||
|
const router = useRouter()
|
||||||
|
const { privateUser } = props
|
||||||
|
const [seen, setSeen] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (router.pathname.endsWith('notifications')) return setSeen(true)
|
||||||
|
else setSeen(false)
|
||||||
|
}, [router.pathname])
|
||||||
|
|
||||||
|
const notifications = useUnseenPreferredNotificationGroups(privateUser)
|
||||||
|
if (!notifications || notifications.length === 0 || seen) {
|
||||||
|
return <div />
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="-mt-0.75 absolute ml-3.5 min-w-[15px] rounded-full bg-indigo-500 p-[2px] text-center text-[10px] leading-3 text-white lg:-mt-1 lg:ml-2">
|
||||||
|
{notifications.length > NOTIFICATIONS_PER_PAGE
|
||||||
|
? `${NOTIFICATIONS_PER_PAGE}+`
|
||||||
|
: notifications.length}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { listenForPrivateUser } from 'web/lib/firebase/users'
|
|
||||||
import { notification_subscribe_types, PrivateUser } from 'common/user'
|
import { notification_subscribe_types, PrivateUser } from 'common/user'
|
||||||
import { Notification } from 'common/notification'
|
import { Notification } from 'common/notification'
|
||||||
import { listenForNotifications } from 'web/lib/firebase/notifications'
|
import {
|
||||||
|
getNotificationsQuery,
|
||||||
|
listenForNotifications,
|
||||||
|
} from 'web/lib/firebase/notifications'
|
||||||
import { groupBy, map } from 'lodash'
|
import { groupBy, map } from 'lodash'
|
||||||
|
import { useFirestoreQuery } from '@react-query-firebase/firestore'
|
||||||
|
import { NOTIFICATIONS_PER_PAGE } from 'web/pages/notifications'
|
||||||
|
|
||||||
export type NotificationGroup = {
|
export type NotificationGroup = {
|
||||||
notifications: Notification[]
|
notifications: Notification[]
|
||||||
|
@ -13,15 +17,30 @@ export type NotificationGroup = {
|
||||||
type: 'income' | 'normal'
|
type: 'income' | 'normal'
|
||||||
}
|
}
|
||||||
|
|
||||||
export function usePreferredGroupedNotifications(
|
// For some reason react-query subscriptions don't actually listen for notifications
|
||||||
userId: string | undefined,
|
// Use useUnseenPreferredNotificationGroups to listen for new notifications
|
||||||
options: { unseenOnly: boolean }
|
export function usePreferredGroupedNotifications(privateUser: PrivateUser) {
|
||||||
) {
|
|
||||||
const [notificationGroups, setNotificationGroups] = useState<
|
const [notificationGroups, setNotificationGroups] = useState<
|
||||||
NotificationGroup[] | undefined
|
NotificationGroup[] | undefined
|
||||||
>(undefined)
|
>(undefined)
|
||||||
|
const [notifications, setNotifications] = useState<Notification[]>([])
|
||||||
|
const key = `notifications-${privateUser.id}-all`
|
||||||
|
|
||||||
|
const result = useFirestoreQuery([key], getNotificationsQuery(privateUser.id))
|
||||||
|
useEffect(() => {
|
||||||
|
if (result.isLoading) return
|
||||||
|
if (!result.data) return setNotifications([])
|
||||||
|
const notifications = result.data.docs.map(
|
||||||
|
(doc) => doc.data() as Notification
|
||||||
|
)
|
||||||
|
|
||||||
|
const notificationsToShow = getAppropriateNotifications(
|
||||||
|
notifications,
|
||||||
|
privateUser.notificationPreferences
|
||||||
|
).filter((n) => !n.isSeenOnHref)
|
||||||
|
setNotifications(notificationsToShow)
|
||||||
|
}, [privateUser.notificationPreferences, result.data, result.isLoading])
|
||||||
|
|
||||||
const notifications = usePreferredNotifications(userId, options)
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!notifications) return
|
if (!notifications) return
|
||||||
|
|
||||||
|
@ -32,6 +51,20 @@ export function usePreferredGroupedNotifications(
|
||||||
return notificationGroups
|
return notificationGroups
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useUnseenPreferredNotificationGroups(privateUser: PrivateUser) {
|
||||||
|
const notifications = useUnseenPreferredNotifications(privateUser, {})
|
||||||
|
const [notificationGroups, setNotificationGroups] = useState<
|
||||||
|
NotificationGroup[] | undefined
|
||||||
|
>(undefined)
|
||||||
|
useEffect(() => {
|
||||||
|
if (!notifications) return
|
||||||
|
|
||||||
|
const groupedNotifications = groupNotifications(notifications)
|
||||||
|
setNotificationGroups(groupedNotifications)
|
||||||
|
}, [notifications])
|
||||||
|
return notificationGroups
|
||||||
|
}
|
||||||
|
|
||||||
export function groupNotifications(notifications: Notification[]) {
|
export function groupNotifications(notifications: Notification[]) {
|
||||||
let notificationGroups: NotificationGroup[] = []
|
let notificationGroups: NotificationGroup[] = []
|
||||||
const notificationGroupsByDay = groupBy(notifications, (notification) =>
|
const notificationGroupsByDay = groupBy(notifications, (notification) =>
|
||||||
|
@ -85,32 +118,24 @@ export function groupNotifications(notifications: Notification[]) {
|
||||||
return notificationGroups
|
return notificationGroups
|
||||||
}
|
}
|
||||||
|
|
||||||
export function usePreferredNotifications(
|
export function useUnseenPreferredNotifications(
|
||||||
userId: string | undefined,
|
privateUser: PrivateUser,
|
||||||
options: { unseenOnly: boolean; customHref?: string }
|
options: { customHref?: string },
|
||||||
|
limit: number = NOTIFICATIONS_PER_PAGE
|
||||||
) {
|
) {
|
||||||
const { unseenOnly, customHref } = options
|
const { customHref } = options
|
||||||
const [privateUser, setPrivateUser] = useState<PrivateUser | null>(null)
|
|
||||||
const [notifications, setNotifications] = useState<Notification[]>([])
|
const [notifications, setNotifications] = useState<Notification[]>([])
|
||||||
const [userAppropriateNotifications, setUserAppropriateNotifications] =
|
const [userAppropriateNotifications, setUserAppropriateNotifications] =
|
||||||
useState<Notification[]>([])
|
useState<Notification[]>([])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (userId) listenForPrivateUser(userId, setPrivateUser)
|
return listenForNotifications(privateUser.id, setNotifications, {
|
||||||
}, [userId])
|
unseenOnly: true,
|
||||||
|
limit,
|
||||||
|
})
|
||||||
|
}, [limit, privateUser.id])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (privateUser)
|
|
||||||
return listenForNotifications(
|
|
||||||
privateUser.id,
|
|
||||||
setNotifications,
|
|
||||||
unseenOnly
|
|
||||||
)
|
|
||||||
}, [privateUser, unseenOnly])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!privateUser) return
|
|
||||||
|
|
||||||
const notificationsToShow = getAppropriateNotifications(
|
const notificationsToShow = getAppropriateNotifications(
|
||||||
notifications,
|
notifications,
|
||||||
privateUser.notificationPreferences
|
privateUser.notificationPreferences
|
||||||
|
@ -118,7 +143,7 @@ export function usePreferredNotifications(
|
||||||
customHref ? n.isSeenOnHref?.includes(customHref) : !n.isSeenOnHref
|
customHref ? n.isSeenOnHref?.includes(customHref) : !n.isSeenOnHref
|
||||||
)
|
)
|
||||||
setUserAppropriateNotifications(notificationsToShow)
|
setUserAppropriateNotifications(notificationsToShow)
|
||||||
}, [privateUser, notifications, customHref])
|
}, [notifications, customHref, privateUser.notificationPreferences])
|
||||||
|
|
||||||
return userAppropriateNotifications
|
return userAppropriateNotifications
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,36 @@
|
||||||
import { collection, query, where } from 'firebase/firestore'
|
import { collection, limit, orderBy, query, where } from 'firebase/firestore'
|
||||||
import { Notification } from 'common/notification'
|
import { Notification } from 'common/notification'
|
||||||
import { db } from 'web/lib/firebase/init'
|
import { db } from 'web/lib/firebase/init'
|
||||||
import { listenForValues } from 'web/lib/firebase/utils'
|
import { listenForValues } from 'web/lib/firebase/utils'
|
||||||
|
import { NOTIFICATIONS_PER_PAGE } from 'web/pages/notifications'
|
||||||
|
|
||||||
function getNotificationsQuery(userId: string, unseenOnly?: boolean) {
|
export function getNotificationsQuery(
|
||||||
|
userId: string,
|
||||||
|
unseenOnlyOptions?: { unseenOnly: boolean; limit: number }
|
||||||
|
) {
|
||||||
const notifsCollection = collection(db, `/users/${userId}/notifications`)
|
const notifsCollection = collection(db, `/users/${userId}/notifications`)
|
||||||
if (unseenOnly) return query(notifsCollection, where('isSeen', '==', false))
|
if (unseenOnlyOptions?.unseenOnly)
|
||||||
return query(notifsCollection)
|
return query(
|
||||||
|
notifsCollection,
|
||||||
|
where('isSeen', '==', false),
|
||||||
|
orderBy('createdTime', 'desc'),
|
||||||
|
limit(unseenOnlyOptions.limit)
|
||||||
|
)
|
||||||
|
return query(
|
||||||
|
notifsCollection,
|
||||||
|
orderBy('createdTime', 'desc'),
|
||||||
|
// Nobody's going through 10 pages of notifications, right?
|
||||||
|
limit(NOTIFICATIONS_PER_PAGE * 10)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function listenForNotifications(
|
export function listenForNotifications(
|
||||||
userId: string,
|
userId: string,
|
||||||
setNotifications: (notifs: Notification[]) => void,
|
setNotifications: (notifs: Notification[]) => void,
|
||||||
unseenOnly?: boolean
|
unseenOnlyOptions?: { unseenOnly: boolean; limit: number }
|
||||||
) {
|
) {
|
||||||
return listenForValues<Notification>(
|
return listenForValues<Notification>(
|
||||||
getNotificationsQuery(userId, unseenOnly),
|
getNotificationsQuery(userId, unseenOnlyOptions),
|
||||||
(notifs) => {
|
(notifs) => {
|
||||||
notifs.sort((n1, n2) => n2.createdTime - n1.createdTime)
|
notifs.sort((n1, n2) => n2.createdTime - n1.createdTime)
|
||||||
setNotifications(notifs)
|
setNotifications(notifs)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Tabs } from 'web/components/layout/tabs'
|
import { Tabs } from 'web/components/layout/tabs'
|
||||||
import { useUser } from 'web/hooks/use-user'
|
import { usePrivateUser, useUser } from 'web/hooks/use-user'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { Notification, notification_source_types } from 'common/notification'
|
import { Notification, notification_source_types } from 'common/notification'
|
||||||
import { Avatar, EmptyAvatar } from 'web/components/avatar'
|
import { Avatar, EmptyAvatar } from 'web/components/avatar'
|
||||||
|
@ -8,8 +8,6 @@ import { Page } from 'web/components/page'
|
||||||
import { Title } from 'web/components/title'
|
import { Title } from 'web/components/title'
|
||||||
import { doc, updateDoc } from 'firebase/firestore'
|
import { doc, updateDoc } from 'firebase/firestore'
|
||||||
import { db } from 'web/lib/firebase/init'
|
import { db } from 'web/lib/firebase/init'
|
||||||
import { CopyLinkDateTimeComponent } from 'web/components/feed/copy-link-date-time'
|
|
||||||
import Custom404 from 'web/pages/404'
|
|
||||||
import { UserLink } from 'web/components/user-page'
|
import { UserLink } from 'web/components/user-page'
|
||||||
import { notification_subscribe_types, PrivateUser } from 'common/user'
|
import { notification_subscribe_types, PrivateUser } from 'common/user'
|
||||||
import { Contract } from 'common/contract'
|
import { Contract } from 'common/contract'
|
||||||
|
@ -35,54 +33,83 @@ import { formatMoney } from 'common/util/format'
|
||||||
import { groupPath } from 'web/lib/firebase/groups'
|
import { groupPath } from 'web/lib/firebase/groups'
|
||||||
import { UNIQUE_BETTOR_BONUS_AMOUNT } from 'common/numeric-constants'
|
import { UNIQUE_BETTOR_BONUS_AMOUNT } from 'common/numeric-constants'
|
||||||
import { groupBy, sum, uniq } from 'lodash'
|
import { groupBy, sum, uniq } from 'lodash'
|
||||||
|
import Custom404 from 'web/pages/404'
|
||||||
|
|
||||||
export const NOTIFICATIONS_PER_PAGE = 30
|
export const NOTIFICATIONS_PER_PAGE = 30
|
||||||
const MULTIPLE_USERS_KEY = 'multipleUsers'
|
const MULTIPLE_USERS_KEY = 'multipleUsers'
|
||||||
|
|
||||||
export default function Notifications() {
|
export default function Notifications() {
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
const [page, setPage] = useState(1)
|
const privateUser = usePrivateUser(user?.id)
|
||||||
|
|
||||||
const groupedNotifications = usePreferredGroupedNotifications(user?.id, {
|
|
||||||
unseenOnly: false,
|
|
||||||
})
|
|
||||||
const [paginatedNotificationGroups, setPaginatedNotificationGroups] =
|
|
||||||
useState<NotificationGroup[]>([])
|
|
||||||
useEffect(() => {
|
|
||||||
if (!groupedNotifications) return
|
|
||||||
const start = (page - 1) * NOTIFICATIONS_PER_PAGE
|
|
||||||
const end = start + NOTIFICATIONS_PER_PAGE
|
|
||||||
const maxNotificationsToShow = groupedNotifications.slice(start, end)
|
|
||||||
const remainingNotification = groupedNotifications.slice(end)
|
|
||||||
for (const notification of remainingNotification) {
|
|
||||||
if (notification.isSeen) break
|
|
||||||
else setNotificationsAsSeen(notification.notifications)
|
|
||||||
}
|
|
||||||
setPaginatedNotificationGroups(maxNotificationsToShow)
|
|
||||||
}, [groupedNotifications, page])
|
|
||||||
|
|
||||||
if (user === undefined) {
|
|
||||||
return <LoadingIndicator />
|
|
||||||
}
|
|
||||||
if (user === null) {
|
|
||||||
return <Custom404 />
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (!user) return <Custom404 />
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
<div className={'p-2 sm:p-4'}>
|
<div className={'p-2 sm:p-4'}>
|
||||||
<Title text={'Notifications'} className={'hidden md:block'} />
|
<Title text={'Notifications'} className={'hidden md:block'} />
|
||||||
|
<div>
|
||||||
<Tabs
|
<Tabs
|
||||||
labelClassName={'pb-2 pt-1 '}
|
labelClassName={'pb-2 pt-1 '}
|
||||||
|
className={'mb-0 sm:mb-2'}
|
||||||
defaultIndex={0}
|
defaultIndex={0}
|
||||||
tabs={[
|
tabs={[
|
||||||
{
|
{
|
||||||
title: 'Notifications',
|
title: 'Notifications',
|
||||||
content: groupedNotifications ? (
|
content: privateUser ? (
|
||||||
|
<NotificationsList privateUser={privateUser} />
|
||||||
|
) : (
|
||||||
|
<LoadingIndicator />
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Settings',
|
||||||
|
content: (
|
||||||
<div className={''}>
|
<div className={''}>
|
||||||
{paginatedNotificationGroups.length === 0 &&
|
<NotificationSettings />
|
||||||
"You don't have any notifications. Try changing your settings to see more."}
|
</div>
|
||||||
{paginatedNotificationGroups.map((notification) =>
|
),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Page>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function NotificationsList(props: { privateUser: PrivateUser }) {
|
||||||
|
const { privateUser } = props
|
||||||
|
const [page, setPage] = useState(1)
|
||||||
|
const allGroupedNotifications = usePreferredGroupedNotifications(privateUser)
|
||||||
|
const [paginatedGroupedNotifications, setPaginatedGroupedNotifications] =
|
||||||
|
useState<NotificationGroup[] | undefined>(undefined)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!allGroupedNotifications) return
|
||||||
|
const start = (page - 1) * NOTIFICATIONS_PER_PAGE
|
||||||
|
const end = start + NOTIFICATIONS_PER_PAGE
|
||||||
|
const maxNotificationsToShow = allGroupedNotifications.slice(start, end)
|
||||||
|
const remainingNotification = allGroupedNotifications.slice(end)
|
||||||
|
for (const notification of remainingNotification) {
|
||||||
|
if (notification.isSeen) break
|
||||||
|
else setNotificationsAsSeen(notification.notifications)
|
||||||
|
}
|
||||||
|
setPaginatedGroupedNotifications(maxNotificationsToShow)
|
||||||
|
}, [allGroupedNotifications, page])
|
||||||
|
|
||||||
|
if (!paginatedGroupedNotifications || !allGroupedNotifications)
|
||||||
|
return <LoadingIndicator />
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={'min-h-[100vh]'}>
|
||||||
|
{paginatedGroupedNotifications.length === 0 && (
|
||||||
|
<div className={'mt-2'}>
|
||||||
|
You don't have any notifications. Try changing your settings to see
|
||||||
|
more.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{paginatedGroupedNotifications.map((notification) =>
|
||||||
notification.type === 'income' ? (
|
notification.type === 'income' ? (
|
||||||
<IncomeNotificationGroupItem
|
<IncomeNotificationGroupItem
|
||||||
notificationGroup={notification}
|
notificationGroup={notification}
|
||||||
|
@ -100,7 +127,8 @@ export default function Notifications() {
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
{groupedNotifications.length > NOTIFICATIONS_PER_PAGE && (
|
{paginatedGroupedNotifications.length > 0 &&
|
||||||
|
allGroupedNotifications.length > NOTIFICATIONS_PER_PAGE && (
|
||||||
<nav
|
<nav
|
||||||
className="flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6"
|
className="flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6"
|
||||||
aria-label="Pagination"
|
aria-label="Pagination"
|
||||||
|
@ -109,9 +137,7 @@ export default function Notifications() {
|
||||||
<p className="text-sm text-gray-700">
|
<p className="text-sm text-gray-700">
|
||||||
Showing{' '}
|
Showing{' '}
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{page === 1
|
{page === 1 ? page : (page - 1) * NOTIFICATIONS_PER_PAGE}
|
||||||
? page
|
|
||||||
: (page - 1) * NOTIFICATIONS_PER_PAGE}
|
|
||||||
</span>{' '}
|
</span>{' '}
|
||||||
to{' '}
|
to{' '}
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
|
@ -119,7 +145,7 @@ export default function Notifications() {
|
||||||
</span>{' '}
|
</span>{' '}
|
||||||
of{' '}
|
of{' '}
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{groupedNotifications.length}
|
{allGroupedNotifications.length}
|
||||||
</span>{' '}
|
</span>{' '}
|
||||||
results
|
results
|
||||||
</p>
|
</p>
|
||||||
|
@ -137,8 +163,8 @@ export default function Notifications() {
|
||||||
className="relative ml-3 inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50"
|
className="relative ml-3 inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
page <
|
page <
|
||||||
groupedNotifications?.length /
|
allGroupedNotifications?.length / NOTIFICATIONS_PER_PAGE &&
|
||||||
NOTIFICATIONS_PER_PAGE && setPage(page + 1)
|
setPage(page + 1)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Next
|
Next
|
||||||
|
@ -147,22 +173,6 @@ export default function Notifications() {
|
||||||
</nav>
|
</nav>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
<LoadingIndicator />
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Settings',
|
|
||||||
content: (
|
|
||||||
<div className={''}>
|
|
||||||
<NotificationSettings />
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Page>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,20 +271,22 @@ function IncomeNotificationGroupItem(props: {
|
||||||
)}
|
)}
|
||||||
<Row className={'items-center text-gray-500 sm:justify-start'}>
|
<Row className={'items-center text-gray-500 sm:justify-start'}>
|
||||||
<TrendingUpIcon className={'text-primary h-7 w-7'} />
|
<TrendingUpIcon className={'text-primary h-7 w-7'} />
|
||||||
<div className={'flex truncate'}>
|
|
||||||
<div
|
<div
|
||||||
|
className={'flex w-full flex-row flex-wrap pl-1 sm:pl-0'}
|
||||||
onClick={() => setExpanded(!expanded)}
|
onClick={() => setExpanded(!expanded)}
|
||||||
className={'line-clamp-1 cursor-pointer pl-1 sm:pl-0'}
|
|
||||||
>
|
>
|
||||||
<span>
|
<div className={'flex w-full flex-row justify-between'}>
|
||||||
|
<div>
|
||||||
{'Daily Income Summary: '}
|
{'Daily Income Summary: '}
|
||||||
<span className={'text-primary'}>
|
<span className={'text-primary'}>
|
||||||
{'+' + formatMoney(totalIncome)}
|
{'+' + formatMoney(totalIncome)}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</div>
|
||||||
|
<div className={'inline-block'}>
|
||||||
<RelativeTimestamp time={notifications[0].createdTime} />
|
<RelativeTimestamp time={notifications[0].createdTime} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</Row>
|
</Row>
|
||||||
<div>
|
<div>
|
||||||
<div className={clsx('mt-1 md:text-base', expanded ? 'pl-4' : '')}>
|
<div className={clsx('mt-1 md:text-base', expanded ? 'pl-4' : '')}>
|
||||||
|
@ -329,13 +341,7 @@ function IncomeNotificationItem(props: {
|
||||||
justSummary?: boolean
|
justSummary?: boolean
|
||||||
}) {
|
}) {
|
||||||
const { notification, justSummary } = props
|
const { notification, justSummary } = props
|
||||||
const {
|
const { sourceType, sourceUserName, sourceUserUsername } = notification
|
||||||
sourceType,
|
|
||||||
sourceUserName,
|
|
||||||
reason,
|
|
||||||
sourceUserUsername,
|
|
||||||
createdTime,
|
|
||||||
} = notification
|
|
||||||
const [highlighted] = useState(!notification.isSeen)
|
const [highlighted] = useState(!notification.isSeen)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -354,7 +360,7 @@ function IncomeNotificationItem(props: {
|
||||||
} else if (sourceType === 'tip') {
|
} else if (sourceType === 'tip') {
|
||||||
reasonText = !simple ? `tipped you` : `in tips on`
|
reasonText = !simple ? `tipped you` : `in tips on`
|
||||||
}
|
}
|
||||||
return <span className={'flex-shrink-0'}>{reasonText}</span>
|
return reasonText
|
||||||
}
|
}
|
||||||
|
|
||||||
if (justSummary) {
|
if (justSummary) {
|
||||||
|
@ -374,7 +380,7 @@ function IncomeNotificationItem(props: {
|
||||||
</div>
|
</div>
|
||||||
<span className={'flex truncate'}>
|
<span className={'flex truncate'}>
|
||||||
{getReasonForShowingIncomeNotification(true)}
|
{getReasonForShowingIncomeNotification(true)}
|
||||||
<NotificationLink notification={notification} />
|
<NotificationLink notification={notification} noClick={true} />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -392,8 +398,7 @@ function IncomeNotificationItem(props: {
|
||||||
>
|
>
|
||||||
<a href={getSourceUrl(notification)}>
|
<a href={getSourceUrl(notification)}>
|
||||||
<Row className={'items-center text-gray-500 sm:justify-start'}>
|
<Row className={'items-center text-gray-500 sm:justify-start'}>
|
||||||
<div className={'flex max-w-xl shrink '}>
|
<div className={'line-clamp-2 flex max-w-xl shrink '}>
|
||||||
{sourceType && reason && (
|
|
||||||
<div className={'inline'}>
|
<div className={'inline'}>
|
||||||
<span className={'mr-1'}>
|
<span className={'mr-1'}>
|
||||||
<NotificationTextLabel
|
<NotificationTextLabel
|
||||||
|
@ -402,7 +407,8 @@ function IncomeNotificationItem(props: {
|
||||||
notification={notification}
|
notification={notification}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
|
<span>
|
||||||
{sourceType != 'bonus' &&
|
{sourceType != 'bonus' &&
|
||||||
(sourceUserUsername === MULTIPLE_USERS_KEY ? (
|
(sourceUserUsername === MULTIPLE_USERS_KEY ? (
|
||||||
<span className={'mr-1 truncate'}>Multiple users</span>
|
<span className={'mr-1 truncate'}>Multiple users</span>
|
||||||
|
@ -414,20 +420,11 @@ function IncomeNotificationItem(props: {
|
||||||
justFirstName={true}
|
justFirstName={true}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
{getReasonForShowingIncomeNotification(false)} {' on'}
|
||||||
)}
|
|
||||||
{getReasonForShowingIncomeNotification(false)}
|
|
||||||
<span className={'ml-1 flex hidden sm:inline-block'}>
|
|
||||||
on
|
|
||||||
<NotificationLink notification={notification} />
|
<NotificationLink notification={notification} />
|
||||||
</span>
|
</span>
|
||||||
<RelativeTimestamp time={createdTime} />
|
|
||||||
</div>
|
</div>
|
||||||
</Row>
|
</Row>
|
||||||
<span className={'flex truncate text-gray-500 sm:hidden'}>
|
|
||||||
on
|
|
||||||
<NotificationLink notification={notification} />
|
|
||||||
</span>
|
|
||||||
<div className={'mt-4 border-b border-gray-300'} />
|
<div className={'mt-4 border-b border-gray-300'} />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -473,24 +470,26 @@ function NotificationGroupItem(props: {
|
||||||
)}
|
)}
|
||||||
<Row className={'items-center text-gray-500 sm:justify-start'}>
|
<Row className={'items-center text-gray-500 sm:justify-start'}>
|
||||||
<EmptyAvatar multi />
|
<EmptyAvatar multi />
|
||||||
<div className={'flex truncate pl-2'}>
|
|
||||||
<div
|
<div
|
||||||
onClick={() => setExpanded(!expanded)}
|
className={'line-clamp-2 flex w-full flex-row flex-wrap pl-1 sm:pl-0'}
|
||||||
className={' flex cursor-pointer truncate pl-1 sm:pl-0'}
|
|
||||||
>
|
>
|
||||||
{sourceContractTitle ? (
|
{sourceContractTitle ? (
|
||||||
<>
|
<div className={'flex w-full flex-row justify-between'}>
|
||||||
<span className={'flex-shrink-0'}>{'Activity on '}</span>
|
<div className={'ml-2'}>
|
||||||
<span className={'truncate'}>
|
Activity on
|
||||||
<NotificationLink notification={notifications[0]} />
|
<NotificationLink notification={notifications[0]} />
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
'Other activity'
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className={'hidden sm:inline-block'}>
|
||||||
<RelativeTimestamp time={notifications[0].createdTime} />
|
<RelativeTimestamp time={notifications[0].createdTime} />
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<span>
|
||||||
|
Other activity
|
||||||
|
<RelativeTimestamp time={notifications[0].createdTime} />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</Row>
|
</Row>
|
||||||
<div>
|
<div>
|
||||||
<div className={clsx('mt-1 md:text-base', expanded ? 'pl-4' : '')}>
|
<div className={clsx('mt-1 md:text-base', expanded ? 'pl-4' : '')}>
|
||||||
|
@ -528,7 +527,7 @@ function NotificationGroupItem(props: {
|
||||||
notification={notification}
|
notification={notification}
|
||||||
key={notification.id}
|
key={notification.id}
|
||||||
justSummary={false}
|
justSummary={false}
|
||||||
hideTitle={true}
|
isChildOfGroup={true}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
@ -545,22 +544,18 @@ function NotificationGroupItem(props: {
|
||||||
function NotificationItem(props: {
|
function NotificationItem(props: {
|
||||||
notification: Notification
|
notification: Notification
|
||||||
justSummary?: boolean
|
justSummary?: boolean
|
||||||
hideTitle?: boolean
|
isChildOfGroup?: boolean
|
||||||
}) {
|
}) {
|
||||||
const { notification, justSummary, hideTitle } = props
|
const { notification, justSummary, isChildOfGroup } = props
|
||||||
const {
|
const {
|
||||||
sourceType,
|
sourceType,
|
||||||
sourceId,
|
|
||||||
sourceUserName,
|
sourceUserName,
|
||||||
sourceUserAvatarUrl,
|
sourceUserAvatarUrl,
|
||||||
sourceUpdateType,
|
sourceUpdateType,
|
||||||
reasonText,
|
reasonText,
|
||||||
reason,
|
reason,
|
||||||
sourceUserUsername,
|
sourceUserUsername,
|
||||||
createdTime,
|
|
||||||
sourceText,
|
sourceText,
|
||||||
sourceContractCreatorUsername,
|
|
||||||
sourceContractSlug,
|
|
||||||
} = notification
|
} = notification
|
||||||
|
|
||||||
const [defaultNotificationText, setDefaultNotificationText] =
|
const [defaultNotificationText, setDefaultNotificationText] =
|
||||||
|
@ -629,44 +624,38 @@ function NotificationItem(props: {
|
||||||
className={'mr-2'}
|
className={'mr-2'}
|
||||||
username={sourceUserName}
|
username={sourceUserName}
|
||||||
/>
|
/>
|
||||||
<div className={'flex-1 overflow-hidden sm:flex'}>
|
<div className={'flex w-full flex-row pl-1 sm:pl-0'}>
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
'flex max-w-xl shrink overflow-hidden text-ellipsis pl-1 sm:pl-0'
|
'line-clamp-2 sm:line-clamp-none flex w-full flex-row justify-between'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
<div>
|
||||||
{sourceUpdateType != 'closed' && (
|
{sourceUpdateType != 'closed' && (
|
||||||
<UserLink
|
<UserLink
|
||||||
name={sourceUserName || ''}
|
name={sourceUserName || ''}
|
||||||
username={sourceUserUsername || ''}
|
username={sourceUserUsername || ''}
|
||||||
className={'mr-0 flex-shrink-0'}
|
className={'mr-1 flex-shrink-0'}
|
||||||
justFirstName={true}
|
justFirstName={true}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{sourceType && reason && (
|
{getReasonForShowingNotification(
|
||||||
<div className={'inline flex truncate'}>
|
notification,
|
||||||
<span className={'ml-1 flex-shrink-0'}>
|
false,
|
||||||
{getReasonForShowingNotification(notification, false, true)}
|
isChildOfGroup
|
||||||
</span>
|
)}
|
||||||
{!hideTitle && (
|
{isChildOfGroup ? (
|
||||||
|
<RelativeTimestamp time={notification.createdTime} />
|
||||||
|
) : (
|
||||||
<NotificationLink notification={notification} />
|
<NotificationLink notification={notification} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
{sourceId &&
|
|
||||||
sourceContractSlug &&
|
|
||||||
sourceContractCreatorUsername ? (
|
|
||||||
<CopyLinkDateTimeComponent
|
|
||||||
prefix={sourceContractCreatorUsername}
|
|
||||||
slug={sourceContractSlug}
|
|
||||||
createdTime={createdTime}
|
|
||||||
elementId={getSourceIdForLinkComponent(sourceId)}
|
|
||||||
className={'-mx-1 inline-flex sm:inline-block'}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<RelativeTimestamp time={createdTime} />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
{!isChildOfGroup && (
|
||||||
|
<div className={'hidden sm:inline-block'}>
|
||||||
|
<RelativeTimestamp time={notification.createdTime} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Row>
|
</Row>
|
||||||
<div className={'mt-1 ml-1 md:text-base'}>
|
<div className={'mt-1 ml-1 md:text-base'}>
|
||||||
|
@ -697,8 +686,11 @@ export const setNotificationsAsSeen = (notifications: Notification[]) => {
|
||||||
return notifications
|
return notifications
|
||||||
}
|
}
|
||||||
|
|
||||||
function NotificationLink(props: { notification: Notification }) {
|
function NotificationLink(props: {
|
||||||
const { notification } = props
|
notification: Notification
|
||||||
|
noClick?: boolean
|
||||||
|
}) {
|
||||||
|
const { notification, noClick } = props
|
||||||
const {
|
const {
|
||||||
sourceType,
|
sourceType,
|
||||||
sourceContractTitle,
|
sourceContractTitle,
|
||||||
|
@ -707,8 +699,17 @@ function NotificationLink(props: { notification: Notification }) {
|
||||||
sourceSlug,
|
sourceSlug,
|
||||||
sourceTitle,
|
sourceTitle,
|
||||||
} = notification
|
} = notification
|
||||||
|
if (noClick)
|
||||||
|
return (
|
||||||
|
<span className={'ml-1 font-bold '}>
|
||||||
|
{sourceContractTitle || sourceTitle}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
|
className={
|
||||||
|
'ml-1 font-bold hover:underline hover:decoration-indigo-400 hover:decoration-2 '
|
||||||
|
}
|
||||||
href={
|
href={
|
||||||
sourceContractCreatorUsername
|
sourceContractCreatorUsername
|
||||||
? `/${sourceContractCreatorUsername}/${sourceContractSlug}`
|
? `/${sourceContractCreatorUsername}/${sourceContractSlug}`
|
||||||
|
@ -716,9 +717,6 @@ function NotificationLink(props: { notification: Notification }) {
|
||||||
? `${groupPath(sourceSlug)}`
|
? `${groupPath(sourceSlug)}`
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
className={
|
|
||||||
'ml-1 inline max-w-xs truncate font-bold text-gray-500 hover:underline hover:decoration-indigo-400 hover:decoration-2 sm:max-w-sm'
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{sourceContractTitle || sourceTitle}
|
{sourceContractTitle || sourceTitle}
|
||||||
</a>
|
</a>
|
||||||
|
@ -852,14 +850,6 @@ function getReasonForShowingNotification(
|
||||||
reasonText = !simple ? 'tagged you on' : 'tagged you'
|
reasonText = !simple ? 'tagged you on' : 'tagged you'
|
||||||
else if (reason === 'reply_to_users_comment')
|
else if (reason === 'reply_to_users_comment')
|
||||||
reasonText = !simple ? 'replied to you on' : 'replied'
|
reasonText = !simple ? 'replied to you on' : 'replied'
|
||||||
else if (reason === 'on_users_contract')
|
|
||||||
reasonText = !simple ? `commented on your question` : 'commented'
|
|
||||||
else if (reason === 'on_contract_with_users_comment')
|
|
||||||
reasonText = `commented on`
|
|
||||||
else if (reason === 'on_contract_with_users_answer')
|
|
||||||
reasonText = `commented on`
|
|
||||||
else if (reason === 'on_contract_with_users_shares_in')
|
|
||||||
reasonText = `commented on`
|
|
||||||
else reasonText = `commented on`
|
else reasonText = `commented on`
|
||||||
break
|
break
|
||||||
case 'contract':
|
case 'contract':
|
||||||
|
@ -871,12 +861,6 @@ function getReasonForShowingNotification(
|
||||||
break
|
break
|
||||||
case 'answer':
|
case 'answer':
|
||||||
if (reason === 'on_users_contract') reasonText = `answered your question `
|
if (reason === 'on_users_contract') reasonText = `answered your question `
|
||||||
else if (reason === 'on_contract_with_users_comment')
|
|
||||||
reasonText = `answered`
|
|
||||||
else if (reason === 'on_contract_with_users_answer')
|
|
||||||
reasonText = `answered`
|
|
||||||
else if (reason === 'on_contract_with_users_shares_in')
|
|
||||||
reasonText = `answered`
|
|
||||||
else reasonText = `answered`
|
else reasonText = `answered`
|
||||||
break
|
break
|
||||||
case 'follow':
|
case 'follow':
|
||||||
|
@ -897,12 +881,7 @@ function getReasonForShowingNotification(
|
||||||
default:
|
default:
|
||||||
reasonText = ''
|
reasonText = ''
|
||||||
}
|
}
|
||||||
|
return replaceOn ? reasonText.replace(' on', '') : reasonText
|
||||||
return (
|
|
||||||
<span className={'flex-shrink-0'}>
|
|
||||||
{replaceOn ? reasonText.replace(' on', '') : reasonText}
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: where should we put referral bonus notifications?
|
// TODO: where should we put referral bonus notifications?
|
||||||
|
|
Loading…
Reference in New Issue
Block a user