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 { advSchema, amountSchema, bountySchema, commentSchema, discussionSchema, jobSchema, linkSchema, pollSchema, ssValidate } from '../../lib/validate'
|
||||
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 { datePivot } from '../../lib/time'
|
||||
|
||||
@ -654,21 +654,7 @@ export default {
|
||||
throw new GraphQLError('item does not belong to you', { extensions: { code: 'FORBIDDEN' } })
|
||||
}
|
||||
|
||||
const data = { deletedAt: new Date() }
|
||||
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 })
|
||||
return await deleteItemByAuthor({ models, id, item: old })
|
||||
},
|
||||
upsertLink: async (parent, { id, hash, hmac, ...item }, { me, models, lnd }) => {
|
||||
await ssValidate(linkSchema, item, { models, me })
|
||||
@ -1109,6 +1095,12 @@ export const updateItem = async (parent, { sub: subName, forward, options, ...it
|
||||
|
||||
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 = []
|
||||
return item
|
||||
}
|
||||
@ -1138,12 +1130,27 @@ export const createItem = async (parent, { forward, options, ...item }, { me, mo
|
||||
|
||||
await createMentions(item, models)
|
||||
|
||||
await enqueueDeletionJob(item, models)
|
||||
|
||||
notifyUserSubscribers({ models, item })
|
||||
|
||||
item.comments = []
|
||||
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 fwdUsers = []
|
||||
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'
|
||||
|
||||
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 { views, rankViews } from './views.js'
|
||||
import { imgproxy } from './imgproxy.js'
|
||||
import { deleteItem } from './ephemeralItems.js'
|
||||
|
||||
const { loadEnvConfig } = nextEnv
|
||||
const { ApolloClient, HttpLink, InMemoryCache } = apolloClient
|
||||
@ -68,6 +69,7 @@ async function work () {
|
||||
await boss.work('views', views(args))
|
||||
await boss.work('rankViews', rankViews(args))
|
||||
await boss.work('imgproxy', imgproxy(args))
|
||||
await boss.work('deleteItem', deleteItem(args))
|
||||
|
||||
console.log('working jobs')
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user