Territory post notifications (#745)
* Notify founders of new posts * Only merge notifications of same territory * Show territory posts in /notifications * Don't notify on own posts
This commit is contained in:
parent
39c9775c4c
commit
bdf9b1f0fd
|
@ -17,7 +17,7 @@ import uu from 'url-unshort'
|
|||
import { actSchema, advSchema, bountySchema, commentSchema, discussionSchema, jobSchema, linkSchema, pollSchema, ssValidate } from '../../lib/validate'
|
||||
import { sendUserNotification } from '../webPush'
|
||||
import { defaultCommentSort, isJob, deleteItemByAuthor, getDeleteCommand, hasDeleteCommand } from '../../lib/item'
|
||||
import { notifyItemParents, notifyUserSubscribers, notifyZapped } from '../../lib/push-notifications'
|
||||
import { notifyItemParents, notifyUserSubscribers, notifyZapped, notifyFounders } from '../../lib/push-notifications'
|
||||
import { datePivot, whenRange } from '../../lib/time'
|
||||
import { imageFeesInfo, uploadIdsFromText } from './image'
|
||||
import assertGofacYourself from './ofac'
|
||||
|
@ -1228,6 +1228,8 @@ export const createItem = async (parent, { forward, options, ...item }, { me, mo
|
|||
|
||||
notifyUserSubscribers({ models, item })
|
||||
|
||||
notifyFounders({ models, item })
|
||||
|
||||
item.comments = []
|
||||
return item
|
||||
}
|
||||
|
|
|
@ -107,6 +107,17 @@ export default {
|
|||
LIMIT ${LIMIT}+$3`
|
||||
)
|
||||
|
||||
if (meFull.noteTerritoryPosts) {
|
||||
itemDrivenQueries.push(
|
||||
`SELECT "Item".*, "Item".created_at AS "sortTime", 'TerritoryPost' AS type
|
||||
FROM "Item"
|
||||
JOIN "Sub" ON "Item"."subName" = "Sub".name
|
||||
WHERE "Sub"."userId" = $1 AND "Item"."userId" <> $1
|
||||
ORDER BY "sortTime" DESC
|
||||
LIMIT ${LIMIT}+$3`
|
||||
)
|
||||
}
|
||||
|
||||
// mentions
|
||||
if (meFull.noteMentions) {
|
||||
itemDrivenQueries.push(
|
||||
|
@ -137,6 +148,7 @@ export default {
|
|||
WHEN type = 'Mention' THEN 1
|
||||
WHEN type = 'Reply' THEN 2
|
||||
WHEN type = 'FollowActivity' THEN 3
|
||||
WHEN type = 'TerritoryPost' THEN 4
|
||||
END ASC
|
||||
)`
|
||||
)
|
||||
|
@ -348,6 +360,9 @@ export default {
|
|||
FollowActivity: {
|
||||
item: async (n, args, { models, me }) => getItem(n, { id: n.id }, { models, me })
|
||||
},
|
||||
TerritoryPost: {
|
||||
item: async (n, args, { models, me }) => getItem(n, { id: n.id }, { models, me })
|
||||
},
|
||||
JobChanged: {
|
||||
item: async (n, args, { models, me }) => getItem(n, { id: n.id }, { models, me })
|
||||
},
|
||||
|
|
|
@ -102,9 +102,15 @@ export default gql`
|
|||
sortTime: Date!
|
||||
}
|
||||
|
||||
type TerritoryPost {
|
||||
id: ID!
|
||||
item: Item!
|
||||
sortTime: Date!
|
||||
}
|
||||
|
||||
union Notification = Reply | Votification | Mention
|
||||
| Invitification | Earn | JobChanged | InvoicePaid | Referral
|
||||
| Streak | FollowActivity | ForwardedVotification | Revenue | SubStatus
|
||||
| Streak | FollowActivity | ForwardedVotification | Revenue | SubStatus | TerritoryPost
|
||||
|
||||
type Notifications {
|
||||
lastChecked: Date
|
||||
|
|
|
@ -69,6 +69,7 @@ export default gql`
|
|||
nostrPubkey: String
|
||||
nostrRelays: [String!]
|
||||
noteAllDescendants: Boolean!
|
||||
noteTerritoryPosts: Boolean!
|
||||
noteCowboyHat: Boolean!
|
||||
noteDeposits: Boolean!
|
||||
noteEarning: Boolean!
|
||||
|
@ -125,6 +126,7 @@ export default gql`
|
|||
nostrPubkey: String
|
||||
nostrRelays: [String!]
|
||||
noteAllDescendants: Boolean!
|
||||
noteTerritoryPosts: Boolean!
|
||||
noteCowboyHat: Boolean!
|
||||
noteDeposits: Boolean!
|
||||
noteEarning: Boolean!
|
||||
|
|
|
@ -35,6 +35,7 @@ const createUserFilter = (tag) => {
|
|||
// filter users by notification settings
|
||||
const tagMap = {
|
||||
REPLY: 'noteAllDescendants',
|
||||
TERRITORY_POST: 'noteTerritoryPosts',
|
||||
MENTION: 'noteMentions',
|
||||
TIP: 'noteItemSats',
|
||||
FORWARDEDTIP: 'noteForwardedSats',
|
||||
|
|
|
@ -47,7 +47,8 @@ function Notification ({ n, fresh }) {
|
|||
(type === 'JobChanged' && <JobChanged n={n} />) ||
|
||||
(type === 'Reply' && <Reply n={n} />) ||
|
||||
(type === 'SubStatus' && <SubStatus n={n} />) ||
|
||||
(type === 'FollowActivity' && <FollowActivity n={n} />)
|
||||
(type === 'FollowActivity' && <FollowActivity n={n} />) ||
|
||||
(type === 'TerritoryPost' && <TerritoryPost n={n} />)
|
||||
}
|
||||
</NotificationLayout>
|
||||
)
|
||||
|
@ -419,6 +420,19 @@ function FollowActivity ({ n }) {
|
|||
)
|
||||
}
|
||||
|
||||
function TerritoryPost ({ n }) {
|
||||
return (
|
||||
<>
|
||||
<small className='fw-bold text-info ms-2'>
|
||||
new post in ~{n.item.sub.name}
|
||||
</small>
|
||||
<div>
|
||||
<Item item={n.item} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export function NotificationAlert () {
|
||||
const [showAlert, setShowAlert] = useState(false)
|
||||
const [hasSubscription, setHasSubscription] = useState(false)
|
||||
|
|
|
@ -86,6 +86,14 @@ export const NOTIFICATIONS = gql`
|
|||
text
|
||||
}
|
||||
}
|
||||
... on TerritoryPost {
|
||||
id
|
||||
sortTime
|
||||
item {
|
||||
...ItemFullFields
|
||||
text
|
||||
}
|
||||
}
|
||||
... on Invitification {
|
||||
id
|
||||
sortTime
|
||||
|
|
|
@ -23,6 +23,7 @@ export const ME = gql`
|
|||
lastCheckedJobs
|
||||
nostrCrossposting
|
||||
noteAllDescendants
|
||||
noteTerritoryPosts
|
||||
noteCowboyHat
|
||||
noteDeposits
|
||||
noteEarning
|
||||
|
@ -57,6 +58,7 @@ export const SETTINGS_FIELDS = gql`
|
|||
noteItemSats
|
||||
noteEarning
|
||||
noteAllDescendants
|
||||
noteTerritoryPosts
|
||||
noteMentions
|
||||
noteDeposits
|
||||
noteInvites
|
||||
|
|
|
@ -96,3 +96,29 @@ export const notifyZapped = async ({ models, id }) => {
|
|||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
export const notifyFounders = async ({ models, item }) => {
|
||||
try {
|
||||
const isPost = !!item.title
|
||||
|
||||
// only notify on posts in subs
|
||||
if (!isPost || !item.subName) return
|
||||
|
||||
const author = await models.user.findUnique({ where: { id: item.userId } })
|
||||
const sub = await models.sub.findUnique({ where: { name: item.subName } })
|
||||
|
||||
// don't send notifications on own posts to founders
|
||||
if (sub.userId === author.id) return
|
||||
|
||||
const tag = `TERRITORY_POST-${sub.name}`
|
||||
await sendUserNotification(sub.userId, {
|
||||
title: `@${author.name} created a post in ~${sub.name}`,
|
||||
body: item.title,
|
||||
item,
|
||||
data: { subName: sub.name },
|
||||
tag
|
||||
})
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,6 +68,7 @@ export default function Settings ({ ssrData }) {
|
|||
noteItemSats: settings?.noteItemSats,
|
||||
noteEarning: settings?.noteEarning,
|
||||
noteAllDescendants: settings?.noteAllDescendants,
|
||||
noteTerritoryPosts: settings?.noteTerritoryPosts,
|
||||
noteMentions: settings?.noteMentions,
|
||||
noteDeposits: settings?.noteDeposits,
|
||||
noteInvites: settings?.noteInvites,
|
||||
|
@ -196,6 +197,11 @@ export default function Settings ({ ssrData }) {
|
|||
name='noteAllDescendants'
|
||||
groupClassName='mb-0'
|
||||
/>
|
||||
<Checkbox
|
||||
label='someone writes a post in a territory I founded'
|
||||
name='noteTerritoryPosts'
|
||||
groupClassName='mb-0'
|
||||
/>
|
||||
<Checkbox
|
||||
label='someone joins using my invite or referral links'
|
||||
name='noteInvites'
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
-- AlterTable
|
||||
ALTER TABLE "users" ADD COLUMN "noteTerritoryPosts" BOOLEAN NOT NULL DEFAULT true;
|
|
@ -35,6 +35,7 @@ model User {
|
|||
lastSeenAt DateTime?
|
||||
stackedMsats BigInt @default(0)
|
||||
noteAllDescendants Boolean @default(true)
|
||||
noteTerritoryPosts Boolean @default(true)
|
||||
noteDeposits Boolean @default(true)
|
||||
noteEarning Boolean @default(true)
|
||||
noteInvites Boolean @default(true)
|
||||
|
|
|
@ -113,7 +113,7 @@ const mergeAndShowNotification = async (sw, payload, currentNotifications, tag,
|
|||
// merge notifications into single notification payload
|
||||
// ---
|
||||
// tags that need to know the amount of notifications with same tag for merging
|
||||
const AMOUNT_TAGS = ['REPLY', 'MENTION', 'REFERRAL', 'INVITE', 'FOLLOW']
|
||||
const AMOUNT_TAGS = ['REPLY', 'MENTION', 'REFERRAL', 'INVITE', 'FOLLOW', 'TERRITORY_POST']
|
||||
// tags that need to know the sum of sats of notifications with same tag for merging
|
||||
const SUM_SATS_TAGS = ['DEPOSIT']
|
||||
// this should reflect the amount of notifications that were already merged before
|
||||
|
@ -135,7 +135,7 @@ const mergeAndShowNotification = async (sw, payload, currentNotifications, tag,
|
|||
log(`[sw:push] ${nid} - merged payload: ${JSON.stringify(mergedPayload)}`)
|
||||
|
||||
// calculate title from merged payload
|
||||
const { amount, followeeName, subType, sats } = mergedPayload
|
||||
const { amount, followeeName, subName, subType, sats } = mergedPayload
|
||||
let title = ''
|
||||
if (AMOUNT_TAGS.includes(compareTag)) {
|
||||
if (compareTag === 'REPLY') {
|
||||
|
@ -148,6 +148,8 @@ const mergeAndShowNotification = async (sw, payload, currentNotifications, tag,
|
|||
title = `your invite has been redeemed by ${amount} stackers`
|
||||
} else if (compareTag === 'FOLLOW') {
|
||||
title = `@${followeeName} ${subType === 'POST' ? `created ${amount} posts` : `replied ${amount} times`}`
|
||||
} else if (compareTag === 'TERRITORY_POST') {
|
||||
title = `you have ${amount} new posts in ~${subName}`
|
||||
}
|
||||
} else if (SUM_SATS_TAGS.includes(compareTag)) {
|
||||
// there is only DEPOSIT in this array
|
||||
|
|
Loading…
Reference in New Issue