Skip to content

Conversation

@Rohit3523
Copy link
Collaborator

@Rohit3523 Rohit3523 commented Oct 17, 2025

Proposed changes

This PR improve the private message behaviour

  1. Will not show action sheet when long hold
  2. Show a text message below the message content that says ‘Only you can see this message.’
  3. Added option to dismiss the message manually
  4. Message will automatically delete once user navigate back from room view to roomlist view

Pending thing:

  1. Align the footer
  2. Make a separate component for that info text
  3. Added pt-BR translation
  4. Add e2e test to make sure it's working 💀

Issue(s)

How to test or reproduce

Screenshots

Screenshot 2025-12-07 at 3 46 01 AM
Screenshot 2025-10-17 at 9 44 19 PM

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • Improvement (non-breaking change which improves a current function)
  • New feature (non-breaking change which adds functionality)
  • Documentation update (if none of the other choices apply)

Checklist

  • I have read the CONTRIBUTING doc
  • I have signed the CLA
  • Lint and unit tests pass locally with my changes
  • I have added tests that prove my fix is effective or that my feature works (if applicable)
  • I have added necessary documentation (if applicable)
  • Any dependent changes have been merged and published in downstream modules

Further comments

Summary by CodeRabbit

  • New Features

    • Introduced private message support with visual indicators displaying "Only you can see this message"
    • Users can dismiss private messages using a dedicated dismiss button
    • Private messages are excluded from long-press interaction options
  • Tests

    • Added comprehensive end-to-end test for private message creation, sending, dismissal, and state verification

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 17, 2025

Walkthrough

This pull request introduces a private messages feature for a chat application. The changes include a new test definition for private message workflows, UI components for displaying and dismissing private messages, database schema extensions with a new "private" field (schema version 28), a utility function to delete private messages, updated type definitions, and localization strings.

Changes

Cohort / File(s) Summary
Maestro Test
.maestro/tests/assorted/private-message.yaml
New test definition for private message feature covering setup, message composition, autocomplete, success verification, and dismissal workflows.
Message UI Components
app/containers/message/Message.tsx, index.tsx, interfaces.ts, styles.ts
Added private message indicator, dismiss button, and visual styling. Message component now renders private flag UI with testID for automation. New prop propagation and interface property.
Message Type Definitions
app/definitions/IMessage.ts
Added optional private boolean property to IMessage interface.
Database Schema & Model
app/lib/database/model/Message.js, migrations.js
app/lib/database/schema/app.js
Extended database schema from version 27 to 28. Added private field to Message model with database migration for adding optional boolean column. Updated asPlain() output.
Utility Function
app/lib/methods/deletePrivateMessages.ts
New function to delete private messages by ID or all private messages. Performs batched permanent destruction via WatermelonDB.
Localization
app/i18n/locales/en.json
Added two new i18n keys: "Dismiss_message" and "Only_you_can_see_this_message".
Room View Integration
app/views/RoomView/index.tsx
Added cleanup of private messages on component unmount and guard preventing long-press actions on private messages.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Areas requiring attention:

  • Database migration (schema version 28) correctness and compatibility with WatermelonDB API
  • Proper cleanup logic in RoomView unmount to prevent orphaned records
  • Private message deletion error handling with swallowed batch errors
  • Integration between UI dismiss action and database deletion
  • Test coverage for edge cases (cleanup on unmount, long-press guard)

Suggested reviewers

  • diegolmello

Poem

🐰 A secret whisper, just for you—
Dismiss it quick with a rabbit's clue!
Private messages now take flight,
Database clean, the schema's right. ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'improvement: private message' is vague and generic, using non-descriptive phrasing that doesn't convey the specific functionality being implemented. Make the title more specific by describing the actual feature, such as 'Add private message support with dismiss option and UI indicator' or similar.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch app-private-message

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

comment,
pinned
pinned,
private: isPrivate
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

we can't use private as variable name in class...

Copy link
Member

@diegolmello diegolmello left a comment

Choose a reason for hiding this comment

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

Nice improvement.
I'd like to have e2e tests for the review.
I think sending an unsupported app message (/unsupported) makes you receive a private message like that, so we don't have to configure an app on mobile ws specifically.

@Rohit3523
Copy link
Collaborator Author

I am using /status
Screenshot 2025-12-07 at 3 45 20 AM

@Rohit3523
Copy link
Collaborator Author

I have added E2E testing :)

@Rohit3523 Rohit3523 requested a deployment to approve_e2e_testing December 6, 2025 22:32 — with GitHub Actions Waiting
@Rohit3523 Rohit3523 requested a deployment to experimental_android_build December 6, 2025 22:34 — with GitHub Actions Waiting
@Rohit3523 Rohit3523 requested a deployment to experimental_ios_build December 6, 2025 22:34 — with GitHub Actions Waiting
@Rohit3523 Rohit3523 requested a deployment to official_android_build December 6, 2025 22:34 — with GitHub Actions Waiting
@Rohit3523 Rohit3523 marked this pull request as ready for review December 7, 2025 17:07
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

🧹 Nitpick comments (2)
app/lib/methods/deletePrivateMessages.ts (1)

6-26: Consider adding early return and validation for better efficiency.

The function could be optimized with these refinements:

  1. Return early if no messages are found
  2. Validate the id parameter when provided
  3. Consider returning a boolean or count to indicate success
 export async function deletePrivateMessages(id?: string): Promise<void> {
   try {
     const db = database.active;

     const messages = id
       ? await db.get('messages').query(Q.where('id', id)).fetch()
       : await db.get('messages').query(Q.where('private', true)).fetch();
+    
+    if (messages.length === 0) {
+      return; // No messages to delete
+    }
+    
     const messagesToBeDeleted = messages.map(message => message.prepareDestroyPermanently());

     await db.write(async () => {
       await db.batch(...messagesToBeDeleted);
     });
   } catch (e) {
     log(e);
   }
 }
.maestro/tests/assorted/private-message.yaml (1)

22-51: Consider adding assertion for the private indicator text.

The test verifies the dismiss button appears and functions correctly. Consider also asserting that the "Only you can see this message" text appears before dismissal to ensure the complete private message UI is rendered.

 - extendedWaitUntil:
     visible:
       text: 'Status message changed successfully.'
     timeout: 60000
+- extendedWaitUntil:
+    visible:
+      text: 'Only you can see this message'
+    timeout: 5000
 - extendedWaitUntil:
     visible:
       text: 'Dismiss message'
     timeout: 15000
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1bd62a2 and 220136b.

📒 Files selected for processing (12)
  • .maestro/tests/assorted/private-message.yaml (1 hunks)
  • app/containers/message/Message.tsx (3 hunks)
  • app/containers/message/index.tsx (2 hunks)
  • app/containers/message/interfaces.ts (1 hunks)
  • app/containers/message/styles.ts (1 hunks)
  • app/definitions/IMessage.ts (1 hunks)
  • app/i18n/locales/en.json (2 hunks)
  • app/lib/database/model/Message.js (2 hunks)
  • app/lib/database/model/migrations.js (1 hunks)
  • app/lib/database/schema/app.js (2 hunks)
  • app/lib/methods/deletePrivateMessages.ts (1 hunks)
  • app/views/RoomView/index.tsx (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
app/views/RoomView/index.tsx (1)
app/lib/methods/deletePrivateMessages.ts (1)
  • deletePrivateMessages (6-26)
app/containers/message/Message.tsx (1)
app/lib/methods/deletePrivateMessages.ts (1)
  • deletePrivateMessages (6-26)
🔇 Additional comments (16)
app/containers/message/styles.ts (1)

166-177: LGTM!

The new style definitions for private message indicators are straightforward and well-structured.

app/definitions/IMessage.ts (1)

177-177: LGTM!

The addition of the optional private field to the IMessage interface is consistent with the interface structure and properly typed.

app/containers/message/index.tsx (1)

413-414: LGTM!

The aliasing of private to isPrivate correctly avoids JavaScript reserved keyword issues, and the prop is properly passed to the Message component.

Also applies to: 509-509

app/lib/database/model/Message.js (2)

91-91: LGTM!

The WatermelonDB field decorator is correctly applied for the new private field.


133-134: LGTM!

The private field is correctly included in the asPlain() serialization output, consistent with other fields in the model.

app/containers/message/interfaces.ts (1)

120-120: LGTM!

The addition of the optional private field to IMessageInner is correctly typed and positioned within the interface.

app/i18n/locales/en.json (1)

257-257: Note: Portuguese (pt-BR) translation pending.

The English i18n strings for private messages have been added. As noted in the PR objectives (pending item #3), the pt-BR translations are still pending.

Also applies to: 616-616

app/views/RoomView/index.tsx (2)

867-869: LGTM!

The guard correctly prevents long-press actions on private messages, maintaining the intended UX where private messages cannot trigger action sheets.


375-375: Verify fire-and-forget cleanup pattern for private messages.

The deletePrivateMessages() function is called without await in the async componentWillUnmount lifecycle method. While the function has internal error handling and won't throw, the deletion may not complete before the component fully unmounts.

If this fire-and-forget pattern is intentional (for performance), consider adding a comment. Otherwise, consider awaiting the cleanup:

-		deletePrivateMessages();
+		await deletePrivateMessages();
app/lib/database/schema/app.js (1)

4-4: Migration for schema version 28 is correctly in place.

The database migration exists in app/lib/database/model/migrations.js (lines 336–343) with the proper addColumns for the messages.private field matching the schema update.

app/containers/message/Message.tsx (1)

2-2: LGTM!

The new imports are appropriate for the private message feature implementation.

Also applies to: 29-29

app/lib/database/model/migrations.js (1)

334-343: LGTM!

The migration correctly adds the private column to the messages table. The structure follows the existing pattern, version increments properly, and the boolean type with isOptional: true is appropriate for this feature.

.maestro/tests/assorted/private-message.yaml (3)

1-18: LGTM!

The test setup and lifecycle hooks follow best practices. User creation, room creation, login flow, and cleanup are properly configured.


53-84: LGTM!

This test scenario correctly verifies the automatic cleanup behavior when navigating away from the room. The test steps are clear and the assertions properly validate that private messages are removed when returning to the room.


1-84: Excellent E2E test coverage for the private message feature.

The test comprehensively covers:

  1. Manual dismissal of private messages
  2. Automatic cleanup when navigating away and returning
  3. Proper use of testIDs matching the implementation
  4. Clear scenario separation with comments

This aligns well with the PR objective: "Added e2e test to make sure it's working" ✓

app/lib/methods/deletePrivateMessages.ts (1)

10-12: The query column names are correct.

Q.where('id', id) properly uses the WatermelonDB-inherited primary key, and Q.where('private', true) correctly references the explicitly defined private field in the Message model. No changes needed.

Comment on lines +197 to +204
{props.private && (
<View style={styles.privateIndicator}>
<Text style={styles.privateIndicatorText}>{i18n.t('Only_you_can_see_this_message')}</Text>
<Pressable testID='dismiss-private-message' onPress={() => deletePrivateMessages(props.id)}>
<Text style={styles.privateMessageDismiss}>{i18n.t('Dismiss_message')}</Text>
</Pressable>
</View>
)}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add user feedback and error handling for dismiss action.

The dismiss action calls deletePrivateMessages without awaiting or providing feedback. Users won't know if the dismissal succeeded or failed.

Consider these improvements:

  1. Add a loading state while deleting
  2. Show success/error feedback (toast/snackbar)
  3. Disable the button during deletion to prevent multiple taps
  4. Handle potential errors gracefully

Example implementation:

+const [isDismissing, setIsDismissing] = React.useState(false);
+
+const handleDismiss = async () => {
+  if (isDismissing) return;
+  setIsDismissing(true);
+  try {
+    await deletePrivateMessages(props.id);
+    // Show success feedback (e.g., toast)
+  } catch (error) {
+    // Show error feedback
+  } finally {
+    setIsDismissing(false);
+  }
+};

 {props.private && (
   <View style={styles.privateIndicator}>
     <Text style={styles.privateIndicatorText}>{i18n.t('Only_you_can_see_this_message')} • </Text>
-    <Pressable testID='dismiss-private-message' onPress={() => deletePrivateMessages(props.id)}>
-      <Text style={styles.privateMessageDismiss}>{i18n.t('Dismiss_message')}</Text>
+    <Pressable testID='dismiss-private-message' onPress={handleDismiss} disabled={isDismissing}>
+      <Text style={styles.privateMessageDismiss}>
+        {isDismissing ? i18n.t('Dismissing...') : i18n.t('Dismiss_message')}
+      </Text>
     </Pressable>
   </View>
 )}

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +15 to +22
await db.write(async () => {
try {
await db.batch(...messagesToBeDeleted);
} catch (e) {
console.log('e', e);
// Do nothing
}
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Improve error handling - don't silently swallow batch errors.

The inner try-catch at lines 16-21 swallows errors with only a console.log, making debugging difficult and preventing callers from knowing if deletion failed.

Consider these improvements:

 await db.write(async () => {
-  try {
-    await db.batch(...messagesToBeDeleted);
-  } catch (e) {
-    console.log('e', e);
-    // Do nothing
-  }
+  await db.batch(...messagesToBeDeleted);
 });

If you need to handle specific batch errors, re-throw them so the outer catch can log properly:

 await db.write(async () => {
   try {
     await db.batch(...messagesToBeDeleted);
   } catch (e) {
-    console.log('e', e);
-    // Do nothing
+    log(e);
+    throw e; // Re-throw so caller knows deletion failed
   }
 });
📝 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. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await db.write(async () => {
try {
await db.batch(...messagesToBeDeleted);
} catch (e) {
console.log('e', e);
// Do nothing
}
});
await db.write(async () => {
await db.batch(...messagesToBeDeleted);
});
Suggested change
await db.write(async () => {
try {
await db.batch(...messagesToBeDeleted);
} catch (e) {
console.log('e', e);
// Do nothing
}
});
await db.write(async () => {
try {
await db.batch(...messagesToBeDeleted);
} catch (e) {
log(e);
throw e; // Re-throw so caller knows deletion failed
}
});
🤖 Prompt for AI Agents
In app/lib/methods/deletePrivateMessages.ts around lines 15 to 22 the inner
try-catch is swallowing batch errors (only doing console.log and continuing), so
replace that silent catch with proper error propagation: either remove the inner
try-catch entirely so a thrown error bubbles to the outer catch, or if you must
log here, log using the application's logger with a descriptive message and then
re-throw the error so the outer catch can handle/report it; ensure the logged
message includes the error object and context (which messages failed) instead of
console.log.

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.

3 participants