Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@remindme bot support #1159

Merged
merged 10 commits into from
May 19, 2024
Merged

@remindme bot support #1159

merged 10 commits into from
May 19, 2024

Conversation

SatsAllDay
Copy link
Contributor

@SatsAllDay SatsAllDay commented May 7, 2024

Description

Closes #510

This PR adds support for a @remindme bot, similar to the @delete bot.

It uses the same directive pattern, meaning you mention @remindme followed by a known syntax to enable the functionality. The pattern is as follows:

@remindme in <num> <units>

where <num> is a positive integer with no separators, and <units> is one of second, minute, hours, day, week, month, year, or their plural counterparts. Some examples:

  1. @remindme in 1 day
  2. @remindme in 2 minutes
  3. @remindme in 4 months
  4. etc.

When you include this in either a post or a comment, a reminder is scheduled and you are given feedback in the UI that tells you when you will be reminded of this item, localized to your browser's timezone/locale.

If you have a mention of @remindme but the reminder was not scheduled, a toast message is shown to help guide you to the correct syntax for using the bot.

If you delete an item that has a @remindme bot mention, the reminder is canceled (deleted). Note: if the reminder has already occurred, it is not deleted, since we can't "unremind" you. That means it will still show up in your notification history.

If you edit an item that has a @remindme bot mention, the existing reminder is canceled (deleted). If the updated text contains another @remindme bot mention, the new reminder is scheduled accordingly. Similar to deleting an item with a @remindme mention, if the reminder has already occurred before you edit the containing item, the reminder is not deleted, so it will still show up in your notification history.

Reminders are sent via notifications in your notifications page, as well as via push notification, if you have that enabled. The reminder links you to the item in which you mentioned @remindme.

So, for example, if someone else makes a post and you want to be reminded of it, you would reply via comment to the post with @remindme in 1 week. 1 week later, you will be notified of your comment, and therefore the parent item implicitly.

Technical overview:

  1. New Reminder model added to the DB. It has 3 interesting fields:
    1. itemId
    2. userId
    3. remindAt
  2. Updated item API resolver for create/update/delete item, to parse for @remindme mentions and extract the configuration, and create the Reminder instances, and also schedule the reminder jobs.
  3. New worker job type reminder - scheduled via the item API resolver, and handled via a new worker module
  4. Updates to all UI forms to surface feedback via toasts, for either a successful reminder scheduled, or a failed attempt to schedule reminder.
  5. Updates to the user API resolver to include reminder notifications in hasNewNotes
  6. Updates to the notifications API resolver to include reminders that have occurred (i.e. remindAt in the past) in the notifications listing
  7. Updates to the notifications UI to render reminder notifications appropriately

Screenshots

Successful reminder scheduled toast
Successful reminder scheduled toast

Reminders in notifications page
Reminders in notifications page

Additional Context

  1. We could get away with removing userId from the Reminder model, since with the implementation, the userId in the Reminder is always the owner of the item corresponding to itemId, so we could just look up the user id when needed. I originally modeled it with both userId and itemId since that seemed natural, but it might be overkill.

    This approach does future-proof us if we ever decide to allow you to be directly reminded of someone else's items, though. So maybe it's fine? Open to input here.

Checklist

Are your changes backwards compatible? Please answer below:
Yes. New DB model but it is backwards compatible.

Did you QA this? Could we deploy this straight to production? Please answer below:
Yes.

For frontend changes: Tested on mobile? Please answer below:
No new patterns implemented, so I didn't test it on mobile.

Did you introduce any new environment variables? If so, call them out explicitly here:
No.

SatsAllDay added 3 commits May 9, 2024 20:48
* hasNewNotes implementation
* actually return notification component in ui
* delete reminder and job on item delete
* other goodies
…of post to satisfy prisma

* update wording in form toast for remindme bot usage

* update wording in the push notification sent
@SatsAllDay SatsAllDay marked this pull request as ready for review May 17, 2024 16:58
@SatsAllDay SatsAllDay changed the title DRAFT: @remindme bot support @remindme bot support May 17, 2024
Copy link
Contributor

coderabbitai bot commented May 17, 2024

Walkthrough

Walkthrough

The changes introduce a reminder functionality across various components and modules. Users can now set reminders on items, which will notify them at a scheduled time. This involves adding new resolver functions, type definitions, and database schema changes to support reminders. Additionally, notification handling is updated to include reminders, and UI components are modified to display reminder-related messages.

Changes

Files/Modules Change Summary
api/resolvers/item.js, lib/item.js Added functions to handle reminders and modified existing functions to integrate reminder logic.
api/resolvers/notifications.js, user.js Added queries and logic for fetching and handling reminders.
api/typeDefs/item.js, notifications.js Added new fields and types for reminders in GraphQL type definitions.
components/...-form.js, components/reply.js Updated GraphQL queries and mutations to include reminderScheduledAt and changed toast messages.
components/notifications.js Added a new function and updated component to handle reminder notifications.
fragments/notifications.js Added fields for reminders in the NOTIFICATIONS GraphQL query.
lib/form.js, lib/item.js, lib/webPush.js Added functions and patterns to handle reminders and renamed toast functions.
prisma/migrations/.../migration.sql, schema.prisma Added new Reminder table and updated schema for reminders.
worker/index.js, worker/reminder.js Added job processing logic for sending reminder notifications.

Assessment against linked issues

Objective (Issue #510) Addressed Explanation
Implement a reminder bot that attaches to posts or comments
Allow users to schedule reminders for various time intervals
Notify users via messages in their notifications
Attach the reminder as a reply to the original message
Implement a cost mechanism for setting reminders The cost mechanism for setting reminders is not addressed in the changes.

Overall, the changes effectively implement the core reminder functionality as requested in the linked issue, but the cost mechanism for setting reminders is not included and needs to be addressed separately.


Recent Review Details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between 1946cf4 and b884baa.
Files selected for processing (1)
  • api/resolvers/item.js (6 hunks)
Additional comments not posted (1)
api/resolvers/item.js (1)

1369-1370: Ensure reminders are not created for anonymous users.

Verification successful

The search results indicate that there are indeed checks for anonymous users (ANON_USER_ID) in various parts of the codebase, including the api/resolvers/item.js file. Specifically, there are lines that mention not supporting reminders for anonymous users and disallowing anonymous users from using reminders.

Here are the relevant lines from api/resolvers/item.js:

  • api/resolvers/item.js: // don't support reminders for ANON
  • api/resolvers/item.js: // disallow anon to use reminder
  • api/resolvers/item.js: if (!me || me.id === ANON_USER_ID) {

These lines suggest that the code does include logic to prevent reminders from being created for anonymous users.

  • : The codebase includes checks to ensure that reminders are not created for anonymous users.
Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that reminders are not created for anonymous users.

# Test: Search for checks against anonymous user IDs in reminder creation logic.
ast-grep --lang javascript --pattern $'function createReminderAndJob($_, $_) {
  $$$
  if ($_ === ANON_USER_ID) {
    $$$
  }
  $$$
}'

Length of output: 135



Script:

#!/bin/bash
# Description: Locate the implementation of the createReminderAndJob function and inspect its logic.

# Find the implementation of the createReminderAndJob function
ast-grep --lang javascript --pattern 'function createReminderAndJob($_, $_) { $$$ }'

# Search for any constants or variables that might represent anonymous users
rg -i 'anon' --type js

Length of output: 10465


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Out of diff range and nitpick comments (1)
api/resolvers/notifications.js (1)

307-314: Addition of the Reminder query looks good and aligns with the PR objectives.

Consider adding an index on the remindAt column in the Reminder table to optimize query performance, especially as the data grows.

api/resolvers/user.js Show resolved Hide resolved
api/resolvers/notifications.js Show resolved Hide resolved
worker/reminder.js Show resolved Hide resolved
worker/reminder.js Show resolved Hide resolved
Copy link
Member

@huumn huumn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks great. I had one request and a some very small nits but I'll QA now.

Comment on lines 1390 to 1399
await models.$queryRawUnsafe(`DELETE FROM pgboss.job WHERE name = 'reminder' AND data->>'itemId' = '${item.id}' AND data->>'userId' = '${me.id}' AND state <> 'completed';`)
await models.reminder.deleteMany({
where: {
itemId: Number(item.id),
userId: Number(me.id),
remindAt: {
gt: new Date()
}
}
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: making this transactional is probably a good idea

Comment on lines 1410 to 1416
await models.$queryRawUnsafe(`
INSERT INTO pgboss.job (name, data, startafter)
VALUES ('reminder', jsonb_build_object('itemId', ${item.id}, 'userId', ${me.id}), now() + interval '${reminderCommand.number} ${reminderCommand.unit}s');`)
// use a raw query instead of the model to reuse the built-in `now + interval` support instead of doing it via JS
await models.$queryRawUnsafe(`
INSERT INTO "Reminder" ("userId", "itemId", "remindAt")
VALUES (${me.id}, ${item.id}, now() + interval '${reminderCommand.number} ${reminderCommand.unit}s');`)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same nit as above

api/resolvers/notifications.js Show resolved Hide resolved
if (reminderCommand) {
await models.$queryRawUnsafe(`
INSERT INTO pgboss.job (name, data, startafter)
VALUES ('reminder', jsonb_build_object('itemId', ${item.id}, 'userId', ${me.id}), now() + interval '${reminderCommand.number} ${reminderCommand.unit}s');`)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll need to set an expiresin here. By default this job will be ignored after 15 minutes: https://github.com/timgit/pg-boss/blob/master/docs/readme.md#job-table

I noticed this when I was queuing territoryBilling jobs that startafter 1 month or 1 year.

Copy link
Member

@ekzyis ekzyis May 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked at the documentation now, especially since you also mentioned this to me. But afaict, expireIn is only relevant for active jobs so this shouldn't affect billing jobs that start (and then are active) in a month or year?

That would also match my expectations, why would pgboss drop jobs that haven't even started yet?

See documentation for job states:

All jobs start out in the created state and become active when picked up for work. If job processing completes successfully, jobs will go to completed. If a job fails, it will typically enter the failed state. However, if a job has retry options configured, it will enter the retry state on failure instead and have a chance to re-enter active state. It's also possible for active jobs to become expired, which happens when job processing takes too long. Jobs can also enter cancelled state via cancel(id) or cancel([ids]).

Expiration options

  • expireInSeconds, number
    How many seconds a job may be in active state before it is failed because of expiration. Must be >=1

[...]

Default: 15 minutes

So what you're describing sounds like a bug

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point. I think I borrowed this syntax from the delete bot job, which I don't think had it either? I could be wrong. But I agree, it does seem like a bug if you need to set expiration for a job that starts more than 15 minutes in the future. I don't know if I explicitly tested a reminder that far in the future so I can't say if it was an issue here.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Comment on lines 1392 to 1405
const deleteReminderAndJob = async ({ me, item, models }) => {
if (me?.id && me.id !== ANON_USER_ID) {
await models.$queryRawUnsafe(`DELETE FROM pgboss.job WHERE name = 'reminder' AND data->>'itemId' = '${item.id}' AND data->>'userId' = '${me.id}' AND state <> 'completed';`)
await models.reminder.deleteMany({
where: {
itemId: Number(item.id),
userId: Number(me.id),
remindAt: {
gt: new Date()
}
}
})
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor deletion of reminder jobs to handle non-completed jobs.

Consider adding a comment to clarify why only non-completed jobs are deleted. This could help future maintainers understand the decision-making process behind this logic.

+ // Delete non-completed reminder jobs to avoid interfering with already triggered reminders
  await models.$queryRawUnsafe(`DELETE FROM pgboss.job WHERE name = 'reminder' AND data->>'itemId' = '${item.id}' AND data->>'userId' = '${me.id}' AND state <> 'completed';`)

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
const deleteReminderAndJob = async ({ me, item, models }) => {
if (me?.id && me.id !== ANON_USER_ID) {
await models.$queryRawUnsafe(`DELETE FROM pgboss.job WHERE name = 'reminder' AND data->>'itemId' = '${item.id}' AND data->>'userId' = '${me.id}' AND state <> 'completed';`)
await models.reminder.deleteMany({
where: {
itemId: Number(item.id),
userId: Number(me.id),
remindAt: {
gt: new Date()
}
}
})
}
}
const deleteReminderAndJob = async ({ me, item, models }) => {
if (me?.id && me.id !== ANON_USER_ID) {
// Delete non-completed reminder jobs to avoid interfering with already triggered reminders
await models.$queryRawUnsafe(`DELETE FROM pgboss.job WHERE name = 'reminder' AND data->>'itemId' = '${item.id}' AND data->>'userId' = '${me.id}' AND state <> 'completed';`)
await models.reminder.deleteMany({
where: {
itemId: Number(item.id),
userId: Number(me.id),
remindAt: {
gt: new Date()
}
}
})
}
}

if (reminderCommand) {
await models.$queryRawUnsafe(`
INSERT INTO pgboss.job (name, data, startafter)
VALUES ('reminder', jsonb_build_object('itemId', ${item.id}, 'userId', ${me.id}), now() + interval '${reminderCommand.number} ${reminderCommand.unit}s');`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Set expiration for reminder jobs.

The reminder jobs need an expiresin setting to ensure they do not persist indefinitely in the queue if not executed within a reasonable timeframe.

+ // Set an expiration for the reminder job to ensure it does not persist indefinitely
  VALUES ('reminder', jsonb_build_object('itemId', ${item.id}, 'userId', ${me.id}), now() + interval '${reminderCommand.number} ${reminderCommand.unit}s', 'expiresin' '15 minutes');

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
VALUES ('reminder', jsonb_build_object('itemId', ${item.id}, 'userId', ${me.id}), now() + interval '${reminderCommand.number} ${reminderCommand.unit}s');`)
// Set an expiration for the reminder job to ensure it does not persist indefinitely
VALUES ('reminder', jsonb_build_object('itemId', ${item.id}, 'userId', ${me.id}), now() + interval '${reminderCommand.number} ${reminderCommand.unit}s', 'expiresin' '15 minutes');

@SatsAllDay
Copy link
Contributor Author

Awesome! Thank you for the feedback, I'll work on it as soon as I can

@huumn
Copy link
Member

huumn commented May 19, 2024

FYI I've made the changes myself which I just need to test now.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Out of diff range and nitpick comments (1)
api/resolvers/item.js (1)

1319-1324: Consider adding logging for reminder job deletions and creations.

Adding logging could help in debugging issues related to reminder job management. This could be particularly useful for monitoring the system and understanding the flow of reminder setting and deletion.

Comment on lines +757 to +760
// clean up any pending reminders, if triggered on this item and haven't been executed
if (hasReminderCommand(old.text)) {
await deleteReminderAndJob({ me, item: old, models })
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tip

Codebase Verification

Ensure proper error handling when deleting reminders.

The deleteReminderAndJob function does not currently include any error handling mechanisms. It is important to wrap the database operations in a try-catch block to handle potential errors gracefully.

  • Location: api/resolvers/item.js, lines where deleteReminderAndJob is defined and used.
Analysis chain

Ensure proper error handling when deleting reminders.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that `deleteReminderAndJob` handles errors gracefully.

# Test: Search for error handling in the function implementation.
ast-grep --lang javascript --pattern $'function deleteReminderAndJob($_, $_) {
  $$$
  try {
    $$$
  } catch ($_) {
    $$$
  }
  $$$
}'

Length of output: 139



Script:

#!/bin/bash
# Locate the definition of the `deleteReminderAndJob` function and display its implementation.
ast-grep --lang javascript --pattern 'function deleteReminderAndJob($_, $_) { $$$ }'

Length of output: 86



Script:

#!/bin/bash
# Search for the definition of the `deleteReminderAndJob` function across the codebase.
rg 'function deleteReminderAndJob'

Length of output: 36



Script:

#!/bin/bash
# Search for the definition of the `deleteReminderAndJob` function across the codebase.
rg 'deleteReminderAndJob'

Length of output: 269



Script:

#!/bin/bash
# Extract the implementation of the `deleteReminderAndJob` function from `api/resolvers/item.js`.
sed -n '/const deleteReminderAndJob = async ({ me, item, models }) => {/,/^}/p' api/resolvers/item.js

Length of output: 662

Comment on lines +1387 to +1392
INSERT INTO pgboss.job (name, data, startafter, expirein)
VALUES (
'deleteItem',
jsonb_build_object('id', ${item.id}),
now() + interval '${deleteCommand.number} ${deleteCommand.unit}s',
interval '${deleteCommand.number} ${deleteCommand.unit}s' + interval '1 minute')`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validate the deleteCommand structure before using it.

It's important to validate the deleteCommand object to ensure that it contains valid number and unit properties before constructing the SQL query. This can prevent potential SQL injection or logical errors.

+ if (!deleteCommand || typeof deleteCommand.number !== 'number' || typeof deleteCommand.unit !== 'string') {
+   throw new Error('Invalid delete command');
+ }

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
INSERT INTO pgboss.job (name, data, startafter, expirein)
VALUES (
'deleteItem',
jsonb_build_object('id', ${item.id}),
now() + interval '${deleteCommand.number} ${deleteCommand.unit}s',
interval '${deleteCommand.number} ${deleteCommand.unit}s' + interval '1 minute')`)
if (!deleteCommand || typeof deleteCommand.number !== 'number' || typeof deleteCommand.unit !== 'string') {
throw new Error('Invalid delete command');
}
INSERT INTO pgboss.job (name, data, startafter, expirein)
VALUES (
'deleteItem',
jsonb_build_object('id', ${item.id}),
now() + interval '${deleteCommand.number} ${deleteCommand.unit}s',
interval '${deleteCommand.number} ${deleteCommand.unit}s' + interval '1 minute')`)

Comment on lines +1417 to +1436
const createReminderAndJob = async ({ me, item, models }) => {
// disallow anon to use reminder
if (!me || me.id === ANON_USER_ID) {
return
}
const reminderCommand = getReminderCommand(item.text)
if (reminderCommand) {
await models.$transaction([
models.$queryRawUnsafe(`
INSERT INTO pgboss.job (name, data, startafter, expirein)
VALUES (
'reminder',
jsonb_build_object('itemId', ${item.id}, 'userId', ${me.id}),
now() + interval '${reminderCommand.number} ${reminderCommand.unit}s',
interval '${reminderCommand.number} ${reminderCommand.unit}s' + interval '1 minute')`),
// use a raw query instead of the model to reuse the built-in `now + interval` support instead of doing it via JS
models.$queryRawUnsafe(`
INSERT INTO "Reminder" ("userId", "itemId", "remindAt")
VALUES (${me.id}, ${item.id}, now() + interval '${reminderCommand.number} ${reminderCommand.unit}s')`)
])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor the reminder creation to handle potential exceptions.

The current implementation does not handle exceptions that might occur during the database operations. Wrapping these operations in a try-catch block could improve reliability and error handling.

+ try {
    await models.$transaction([
      models.$queryRawUnsafe(`
        INSERT INTO pgboss.job (name, data, startafter, expirein)
        VALUES (
          'reminder',
          jsonb_build_object('itemId', ${item.id}, 'userId', ${me.id}),
          now() + interval '${reminderCommand.number} ${reminderCommand.unit}s',
          interval '${reminderCommand.number} ${reminderCommand.unit}s' + interval '1 minute')`),
      models.$queryRawUnsafe(`
        INSERT INTO "Reminder" ("userId", "itemId", "remindAt")
        VALUES (${me.id}, ${item.id}, now() + interval '${reminderCommand.number} ${reminderCommand.unit}s')`)
    ])
+ } catch (error) {
+   console.error('Failed to create reminder', error);
+ }

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
const createReminderAndJob = async ({ me, item, models }) => {
// disallow anon to use reminder
if (!me || me.id === ANON_USER_ID) {
return
}
const reminderCommand = getReminderCommand(item.text)
if (reminderCommand) {
await models.$transaction([
models.$queryRawUnsafe(`
INSERT INTO pgboss.job (name, data, startafter, expirein)
VALUES (
'reminder',
jsonb_build_object('itemId', ${item.id}, 'userId', ${me.id}),
now() + interval '${reminderCommand.number} ${reminderCommand.unit}s',
interval '${reminderCommand.number} ${reminderCommand.unit}s' + interval '1 minute')`),
// use a raw query instead of the model to reuse the built-in `now + interval` support instead of doing it via JS
models.$queryRawUnsafe(`
INSERT INTO "Reminder" ("userId", "itemId", "remindAt")
VALUES (${me.id}, ${item.id}, now() + interval '${reminderCommand.number} ${reminderCommand.unit}s')`)
])
const createReminderAndJob = async ({ me, item, models }) => {
// disallow anon to use reminder
if (!me || me.id === ANON_USER_ID) {
return
}
const reminderCommand = getReminderCommand(item.text)
if (reminderCommand) {
try {
await models.$transaction([
models.$queryRawUnsafe(`
INSERT INTO pgboss.job (name, data, startafter, expirein)
VALUES (
'reminder',
jsonb_build_object('itemId', ${item.id}, 'userId', ${me.id}),
now() + interval '${reminderCommand.number} ${reminderCommand.unit}s',
interval '${reminderCommand.number} ${reminderCommand.unit}s' + interval '1 minute')`),
// use a raw query instead of the model to reuse the built-in `now + interval` support instead of doing it via JS
models.$queryRawUnsafe(`
INSERT INTO "Reminder" ("userId", "itemId", "remindAt")
VALUES (${me.id}, ${item.id}, now() + interval '${reminderCommand.number} ${reminderCommand.unit}s')`)
])
} catch (error) {
console.error('Failed to create reminder', error);
}
}
}

@huumn huumn merged commit 852d2cf into stackernews:master May 19, 2024
6 checks passed
@SatsAllDay SatsAllDay deleted the 510-remindme branch May 19, 2024 21:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

remindme bot
3 participants