Ephemeral item support (#570)
backend impl and some of the UI for ephemeral item support more to come, this is just a WIP so far Consolidate client-side ephemeral fee logic in FeeButton components for easier reuse * update the update_item function to handle the case where an item was not ephemeral, but now is, so we charge the user accordingly * introduce `hasDeleteCommand` for a better logical abstraction for some use cases in the code * introduce `EPHEMERAL_FEE_SATS` which is derived from `EPHEMERAL_FEE_MSATS`, so we don't have to the same calculation over and over Remove fees for ephemeral items * remove unused markdownField prop in FeeButton * remove empty migration minor code cleanup Centralize delete item by author code to reduce duplication
This commit is contained in:
parent
e713387920
commit
1d394bebe1
@ -16,7 +16,7 @@ import { parse } from 'tldts'
|
|||||||
import uu from 'url-unshort'
|
import uu from 'url-unshort'
|
||||||
import { advSchema, amountSchema, bountySchema, commentSchema, discussionSchema, jobSchema, linkSchema, pollSchema, ssValidate } from '../../lib/validate'
|
import { advSchema, amountSchema, bountySchema, commentSchema, discussionSchema, jobSchema, linkSchema, pollSchema, ssValidate } from '../../lib/validate'
|
||||||
import { sendUserNotification } from '../webPush'
|
import { sendUserNotification } from '../webPush'
|
||||||
import { defaultCommentSort, isJob } from '../../lib/item'
|
import { defaultCommentSort, isJob, deleteItemByAuthor, getDeleteCommand, hasDeleteCommand } from '../../lib/item'
|
||||||
import { notifyItemParents, notifyUserSubscribers, notifyZapped } from '../../lib/push-notifications'
|
import { notifyItemParents, notifyUserSubscribers, notifyZapped } from '../../lib/push-notifications'
|
||||||
import { datePivot } from '../../lib/time'
|
import { datePivot } from '../../lib/time'
|
||||||
|
|
||||||
@ -654,21 +654,7 @@ export default {
|
|||||||
throw new GraphQLError('item does not belong to you', { extensions: { code: 'FORBIDDEN' } })
|
throw new GraphQLError('item does not belong to you', { extensions: { code: 'FORBIDDEN' } })
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = { deletedAt: new Date() }
|
return await deleteItemByAuthor({ models, id, item: old })
|
||||||
if (old.text) {
|
|
||||||
data.text = '*deleted by author*'
|
|
||||||
}
|
|
||||||
if (old.title) {
|
|
||||||
data.title = 'deleted by author'
|
|
||||||
}
|
|
||||||
if (old.url) {
|
|
||||||
data.url = null
|
|
||||||
}
|
|
||||||
if (old.pollCost) {
|
|
||||||
data.pollCost = null
|
|
||||||
}
|
|
||||||
|
|
||||||
return await models.item.update({ where: { id: Number(id) }, data })
|
|
||||||
},
|
},
|
||||||
upsertLink: async (parent, { id, hash, hmac, ...item }, { me, models, lnd }) => {
|
upsertLink: async (parent, { id, hash, hmac, ...item }, { me, models, lnd }) => {
|
||||||
await ssValidate(linkSchema, item, { models, me })
|
await ssValidate(linkSchema, item, { models, me })
|
||||||
@ -1109,6 +1095,12 @@ export const updateItem = async (parent, { sub: subName, forward, options, ...it
|
|||||||
|
|
||||||
await createMentions(item, models)
|
await createMentions(item, models)
|
||||||
|
|
||||||
|
if (hasDeleteCommand(old.text)) {
|
||||||
|
// delete any deletion jobs that were created from a prior version of the item
|
||||||
|
await clearDeletionJobs(item, models)
|
||||||
|
}
|
||||||
|
await enqueueDeletionJob(item, models)
|
||||||
|
|
||||||
item.comments = []
|
item.comments = []
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
@ -1138,12 +1130,27 @@ export const createItem = async (parent, { forward, options, ...item }, { me, mo
|
|||||||
|
|
||||||
await createMentions(item, models)
|
await createMentions(item, models)
|
||||||
|
|
||||||
|
await enqueueDeletionJob(item, models)
|
||||||
|
|
||||||
notifyUserSubscribers({ models, item })
|
notifyUserSubscribers({ models, item })
|
||||||
|
|
||||||
item.comments = []
|
item.comments = []
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const clearDeletionJobs = async (item, models) => {
|
||||||
|
await models.$queryRawUnsafe(`DELETE FROM pgboss.job WHERE name = 'deleteItem' AND data->>'id' = '${item.id}';`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const enqueueDeletionJob = async (item, models) => {
|
||||||
|
const deleteCommand = getDeleteCommand(item.text)
|
||||||
|
if (deleteCommand) {
|
||||||
|
await models.$queryRawUnsafe(`
|
||||||
|
INSERT INTO pgboss.job (name, data, startafter)
|
||||||
|
VALUES ('deleteItem', jsonb_build_object('id', ${item.id}), now() + interval '${deleteCommand.number} ${deleteCommand.unit}s');`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const getForwardUsers = async (models, forward) => {
|
const getForwardUsers = async (models, forward) => {
|
||||||
const fwdUsers = []
|
const fwdUsers = []
|
||||||
if (forward) {
|
if (forward) {
|
||||||
|
35
lib/item.js
35
lib/item.js
@ -11,3 +11,38 @@ export const defaultCommentSort = (pinned, bio, createdAt) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const isJob = item => typeof item.maxBid !== 'undefined'
|
export const isJob = item => typeof item.maxBid !== 'undefined'
|
||||||
|
|
||||||
|
const deletePattern = /\B@delete\s+in\s+(\d+)\s+(second|minute|hour|day|week|month|year)s?/gi
|
||||||
|
|
||||||
|
export const getDeleteCommand = (text = '') => {
|
||||||
|
const matches = [...text.matchAll(deletePattern)]
|
||||||
|
const commands = matches?.map(match => ({ number: match[1], unit: match[2] }))
|
||||||
|
return commands.length ? commands[commands.length - 1] : undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export const hasDeleteCommand = (text) => !!getDeleteCommand(text)
|
||||||
|
|
||||||
|
export const deleteItemByAuthor = async ({ models, id, item }) => {
|
||||||
|
if (!item) {
|
||||||
|
item = await models.item.findUnique({ where: { id: Number(id) } })
|
||||||
|
}
|
||||||
|
if (!item) {
|
||||||
|
console.log('attempted to delete an item that does not exist', id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const updateData = { deletedAt: new Date() }
|
||||||
|
if (item.text) {
|
||||||
|
updateData.text = '*deleted by author*'
|
||||||
|
}
|
||||||
|
if (item.title) {
|
||||||
|
updateData.title = 'deleted by author'
|
||||||
|
}
|
||||||
|
if (item.url) {
|
||||||
|
updateData.url = null
|
||||||
|
}
|
||||||
|
if (item.pollCost) {
|
||||||
|
updateData.pollCost = null
|
||||||
|
}
|
||||||
|
|
||||||
|
return await models.item.update({ where: { id: Number(id) }, data: updateData })
|
||||||
|
}
|
||||||
|
13
worker/ephemeralItems.js
Normal file
13
worker/ephemeralItems.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { deleteItemByAuthor } from '../lib/item.js'
|
||||||
|
|
||||||
|
export function deleteItem ({ models }) {
|
||||||
|
return async function ({ data: eventData }) {
|
||||||
|
console.log('deleteItem', eventData)
|
||||||
|
const { id } = eventData
|
||||||
|
try {
|
||||||
|
await deleteItemByAuthor({ models, id })
|
||||||
|
} catch (err) {
|
||||||
|
console.error('failed to delete item', err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,6 +15,7 @@ import fetch from 'cross-fetch'
|
|||||||
import { authenticatedLndGrpc } from 'ln-service'
|
import { authenticatedLndGrpc } from 'ln-service'
|
||||||
import { views, rankViews } from './views.js'
|
import { views, rankViews } from './views.js'
|
||||||
import { imgproxy } from './imgproxy.js'
|
import { imgproxy } from './imgproxy.js'
|
||||||
|
import { deleteItem } from './ephemeralItems.js'
|
||||||
|
|
||||||
const { loadEnvConfig } = nextEnv
|
const { loadEnvConfig } = nextEnv
|
||||||
const { ApolloClient, HttpLink, InMemoryCache } = apolloClient
|
const { ApolloClient, HttpLink, InMemoryCache } = apolloClient
|
||||||
@ -68,6 +69,7 @@ async function work () {
|
|||||||
await boss.work('views', views(args))
|
await boss.work('views', views(args))
|
||||||
await boss.work('rankViews', rankViews(args))
|
await boss.work('rankViews', rankViews(args))
|
||||||
await boss.work('imgproxy', imgproxy(args))
|
await boss.work('imgproxy', imgproxy(args))
|
||||||
|
await boss.work('deleteItem', deleteItem(args))
|
||||||
|
|
||||||
console.log('working jobs')
|
console.log('working jobs')
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user