SN distributes the sats it earns back to its best stackers. These sats come from jobs, boosts, posting fees, and donations. You can see the rewards pool and make a donation here.
click for details
)
}
function RevenueNotification ({ n }) {
return (
you stacked {numWithUnits(n.earnedSats, { abbreviate: false })} in territory revenue{timeSince(new Date(n.sortTime))}
As the founder of territory ~{n.subName}, you receive 50% of the revenue it generates and the other 50% go to rewards.
)
}
function SubStatus ({ n }) {
const dueDate = nextBillingWithGrace(n.sub)
return (
{n.sub.status === 'ACTIVE'
? 'your territory is active again'
: (n.sub.status === 'GRACE'
? <>your territory payment for ~{n.sub.name} is due or your territory will be archived in >
: <>your territory ~{n.sub.name} has been archived>)}
click to visit territory and pay
)
}
function Invitification ({ n }) {
return (
<>
your invite has been redeemed by
{numWithUnits(n.invite.invitees.length, {
abbreviate: false,
unitSingular: 'stacker',
unitPlural: 'stackers'
})}
{numWithUnits(n.earnedSats, { abbreviate: false, unitSingular: 'sat was', unitPlural: 'sats were' })} withdrawn from your account
{timeSince(new Date(n.sortTime))}
{n.withdrawl.autoWithdraw && autowithdraw}
)
}
function Referral ({ n }) {
return (
someone joined via one of your referral links
{timeSince(new Date(n.sortTime))}
)
}
function Votification ({ n }) {
let forwardedSats = 0
let ForwardedUsers = null
if (n.item.forwards?.length) {
forwardedSats = Math.floor(n.earnedSats * n.item.forwards.map(fwd => fwd.pct).reduce((sum, cur) => sum + cur) / 100)
ForwardedUsers = () => n.item.forwards.map((fwd, i) =>
@{fwd.user.name}
{i !== n.item.forwards.length - 1 && ' '}
)
}
return (
<>
your {n.item.title ? 'post' : 'reply'} stacked {numWithUnits(n.earnedSats, { abbreviate: false })}
{n.item.forwards?.length > 0 &&
<>
{' '}and forwarded {numWithUnits(forwardedSats, { abbreviate: false })} to{' '}
>}
{n.item.title
?
: (
)}
>
)
}
function ForwardedVotification ({ n }) {
return (
<>
you were forwarded {numWithUnits(n.earnedSats, { abbreviate: false })} from
{n.item.title
?
: (
)}
>
)
}
function Mention ({ n }) {
return (
<>
you were mentioned in
{n.item.title
?
: (
)}
>
)
}
function ItemMention ({ n }) {
return (
<>
your item was mentioned in
{n.item?.title
?
: (
)}
>
)
}
function JobChanged ({ n }) {
return (
<>
{n.item.status === 'ACTIVE'
? 'your job is active again'
: (n.item.status === 'NOSATS'
? 'your job promotion ran out of sats'
: 'your job has been stopped')}
>
)
}
function Reply ({ n }) {
return (
{n.item.title
?
: (
)}
)
}
function FollowActivity ({ n }) {
return (
<>
a stacker you subscribe to {n.item.parentId ? 'commented' : 'posted'}
{n.item.title
?
: (
)}
>
)
}
function TerritoryPost ({ n }) {
return (
<>
new post in ~{n.item.sub.name}
>
)
}
function TerritoryTransfer ({ n }) {
return (
<>
~{n.sub.name} was transferred to you
{timeSince(new Date(n.sortTime))}
>
)
}
function Reminder ({ n }) {
return (
<>
you asked to be reminded of this {n.item.title ? 'post' : 'comment'}
{n.item.title
?
: (
)}
>
)
}
export function NotificationAlert () {
const [showAlert, setShowAlert] = useState(false)
const [hasSubscription, setHasSubscription] = useState(false)
const [error, setError] = useState(null)
const [supported, setSupported] = useState(false)
const sw = useServiceWorker()
useEffect(() => {
const isSupported = sw.support.serviceWorker && sw.support.pushManager && sw.support.notification
if (isSupported) {
const isDefaultPermission = sw.permission.notification === 'default'
setShowAlert(isDefaultPermission && !window.localStorage.getItem('hideNotifyPrompt'))
sw.registration?.pushManager.getSubscription().then(subscription => setHasSubscription(!!subscription))
setSupported(true)
}
}, [sw])
const close = () => {
window.localStorage.setItem('hideNotifyPrompt', 'yep')
setShowAlert(false)
}
return (
error
? (
setError(null)}>
{error.toString()}
)
: showAlert
? (
Enable push notifications?
)
: (
)
)
}
const nid = n => n.__typename + n.id + n.sortTime
export default function Notifications ({ ssrData }) {
const { data, fetchMore } = useQuery(NOTIFICATIONS)
const router = useRouter()
const dat = useData(data, ssrData)
const { notifications: clientNotifications } = useClientNotifications()
const { notifications, lastChecked, cursor } = useMemo(() => {
if (!dat?.notifications) return {}
// make sure we're using the oldest lastChecked we've seen
const retDat = { ...dat.notifications }
if (ssrData?.notifications?.lastChecked < retDat.lastChecked) {
retDat.lastChecked = ssrData.notifications.lastChecked
}
return retDat
}, [dat])
useEffect(() => {
if (lastChecked && !router?.query?.checkedAt) {
router.replace({
pathname: router.pathname,
query: {
...router.query,
nodata: true, // make sure nodata is set so we don't fetch on back/forward
checkedAt: lastChecked
}
}, router.asPath, { ...router.options, shallow: true })
}
}, [router?.query?.checkedAt, lastChecked])
if (!dat) return
const sorted = [...clientNotifications, ...notifications]
.sort((a, b) => new Date(b.sortTime).getTime() - new Date(a.sortTime).getTime())
return (
<>
{sorted.map(n =>
new Date(router?.query?.checkedAt ?? lastChecked)}
/>)}
>
)
}
function CommentsFlatSkeleton () {
const comments = new Array(21).fill(null)
return (