Skip to content

Conversation

abhisek7154
Copy link

@abhisek7154 abhisek7154 commented Sep 12, 2025

… (#6535)

Proposed changes

Refactored emoji rendering by introducing a shared component at
app/components/Emoji/Emoji.tsx.

  • app/containers/EmojiPicker/Emoji.tsx updated to use the shared component.
  • app/containers/markdown/components/emoji/Emoji.tsx updated to use the shared component.
  • Keeps existing exports for compatibility.

This removes duplicate logic and ensures consistent rendering of Unicode, shortcode, and custom emojis.

Issue(s)

Closes #6535

How to test or reproduce

  • Send messages with:
    • A standard Unicode emoji 😀
    • A shortcode :smile:
    • ASCII text emoji :)
    • A custom emoji (if configured)
  • Open EmojiPicker and check rendering
  • Verify big emojis render correctly in chat

Screenshots

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

This refactor consolidates emoji handling into a single reusable component.
Markdown and EmojiPicker previously duplicated similar logic, which made updates harder and risked inconsistent behavior.

Centralizing this logic ensures:

  • Consistent rendering for all emoji types (Unicode, shortcode, ASCII, custom).
  • Easier maintenance since future changes only need to be made in one place.
  • Cleaner and more readable code with less duplication.

No new functionality is added — the focus is purely on improving structure and maintainability.

Summary by CodeRabbit

  • New Features
    • Introduced a unified Emoji component for rendering standard and custom emojis with adjustable sizing.
    • Added an EmojiPicker wrapper to apply consistent styling in the picker.
  • Bug Fixes
    • Fixed inconsistent emoji rendering across markdown, picker, and other UI areas.
  • Refactor
    • Consolidated emoji rendering paths to use the shared renderer for visual consistency.

@CLAassistant
Copy link

CLAassistant commented Sep 12, 2025

CLA assistant check
All committers have signed the CLA.

Copy link
Contributor

coderabbitai bot commented Sep 12, 2025

Walkthrough

Adds a reusable Emoji component at app/components/Emoji/Emoji.tsx, updates EmojiPicker to wrap and re-export that component, and refactors the Markdown emoji renderer to use a centralized EmojiRenderer for all emoji rendering paths.

Changes

Cohort / File(s) Summary
Reusable Emoji component
app/components/Emoji/Emoji.tsx
Adds default Emoji component accepting `emoji: string
EmojiPicker integration
app/containers/EmojiPicker/Emoji.tsx
Replaces local emoji rendering with EmojiPickerEmoji wrapper that delegates to the shared Emoji component and applies conditional styles for category (string) vs custom emojis; re-exports Emoji from app/components/Emoji/Emoji.tsx.
Markdown integration
app/containers/markdown/components/emoji/Emoji.tsx
Replaces previous Text/Plain/CustomEmoji branches with a unified EmojiRenderer usage for unicode, ascii and custom emoji paths; adjusts sizing, spacing and color application to match centralized rendering.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor UI as UI
  participant Picker as EmojiPickerEmoji
  participant Shared as app/components/Emoji/Emoji
  participant Shortname as useShortnameToUnicode
  participant Custom as CustomEmoji
  Note right of Picker: wrapper selects style then delegates

  UI->>Picker: render({ emoji, size, style })
  Picker->>Shared: render({ emoji, size, style })
  alt emoji is string
    Shared->>Shortname: formatShortnameToUnicode(trimmed or :shortname:)
    Shortname-->>Shared: unicodeChar
    Shared-->>UI: <Text style(fontSize=size)>{leading + unicodeChar + trailing}</Text>
  else emoji is ICustomEmoji
    Shared->>Custom: render({ emoji, width:size, height:size, style })
    Custom-->>Shared: <Image />
    Shared-->>UI: <CustomEmoji />
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Pre-merge checks (5 passed)

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "refactor(emoji): centralize emoji rendering into app/components/Emoji…" succinctly and accurately describes the primary change (centralizing emoji rendering into a shared component) and aligns with the PR objectives and modified files, so it is specific and readable despite the trailing ellipsis.
Linked Issues Check ✅ Passed The PR adds a reusable component at app/components/Emoji/Emoji.tsx, updates app/containers/EmojiPicker/Emoji.tsx to re-export and wrap that component, and modifies markdown emoji rendering to use the shared renderer while preserving handling for Unicode, shortcodes, ASCII, and custom emojis, thereby meeting the objectives of issue [#6535].
Out of Scope Changes Check ✅ Passed All changes summarized are confined to emoji rendering (the new shared component and updates to EmojiPicker and markdown renderers) and related imports/exports; I found no unrelated feature or file modifications in the provided summaries, so there are no apparent out-of-scope changes.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

Poem

Hop hop — a glyph I share,
Shortnames turn to faces fair.
Picker points; Markdown beams bright,
One renderer hops into the light.
Reuse stitched up, neat and snappy 🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b3daed3 and fe650eb.

⛔ Files ignored due to path filters (3)
  • app/containers/UIKit/__snapshots__/UiKitModal.test.tsx.snap is excluded by !**/*.snap
  • app/containers/markdown/__snapshots__/Markdown.test.tsx.snap is excluded by !**/*.snap
  • app/containers/message/__snapshots__/Message.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (3)
  • app/components/Emoji/Emoji.tsx (1 hunks)
  • app/containers/EmojiPicker/Emoji.tsx (1 hunks)
  • app/containers/markdown/components/emoji/Emoji.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • app/containers/EmojiPicker/Emoji.tsx
  • app/components/Emoji/Emoji.tsx
  • app/containers/markdown/components/emoji/Emoji.tsx
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

@abhisek7154
Copy link
Author

Hi 👋, this PR is ready for review.
It centralizes emoji rendering into a single shared component as described in #6535.
Please let me know if anything needs to be adjusted. Thanks!

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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/containers/markdown/components/emoji/Emoji.tsx (1)

55-56: Fix potential undefined access when sanitizing custom emoji name

block.value?.value.replace(...) can throw when block.value or block.value.value is undefined. Guard before calling replace.

-const emoji = getCustomEmoji?.(block.value?.value.replace(/\:/g, ''));
+const raw = block.value?.value;
+const emoji = raw ? getCustomEmoji?.(raw.replace(/:/g, '')) : undefined;
🧹 Nitpick comments (6)
app/containers/markdown/components/emoji/Emoji.tsx (2)

41-41: Minor: simplify space prefix logic

More explicit and avoids falsy truthiness quirks.

-const spaceLeft = index && index > 0 ? ' ' : '';
+const spaceLeft = (index ?? 0) > 0 ? ' ' : '';

80-90: Avoid redundant fontSize sources

You pass both size={...} and text styles (styles.text/styles.textBig) that likely set fontSize. Prefer one source to prevent surprises.

app/components/Emoji/Emoji.tsx (3)

5-5: Layering concern: components → containers dependency

components/Emoji imports containers/EmojiPicker/CustomEmoji, creating an upward dependency. Consider moving CustomEmoji to app/components or a shared module to keep layers clean.


8-12: Doc the accepted string forms

Clarify via a short JSDoc that emoji (string) may be Unicode, :shortname: or bare shortname, and that ASCII emoticons are rendered verbatim. Helps future call-sites.


1-30: Add focused tests for the centralized component

Please add unit tests covering:

  • '😄' (Unicode) → renders unchanged
  • ':smile:' and 'smile' → render 😄
  • ':)' (ASCII) → rendered verbatim
  • Custom emoji object → uses CustomEmoji with size applied

I can scaffold Jest tests for these cases if helpful.

app/containers/EmojiPicker/Emoji.tsx (1)

3-11: Optional: memoize to reduce list re-renders

Emoji picker lists can be large; wrapping with React.memo (or memo with a shallow props check) can help.

-import React from 'react';
+import React, { memo } from 'react';
 ...
-export const EmojiPickerEmoji = ({ emoji }: IEmojiProps): React.ReactElement => (
+export const EmojiPickerEmoji = memo(({ emoji }: IEmojiProps): React.ReactElement => (
   <Emoji
     emoji={emoji}
     style={typeof emoji === 'string' ? styles.categoryEmoji : styles.customCategoryEmoji}
   />
-);
+));
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 056056c and 0daf449.

📒 Files selected for processing (3)
  • app/components/Emoji/Emoji.tsx (1 hunks)
  • app/containers/EmojiPicker/Emoji.tsx (1 hunks)
  • app/containers/markdown/components/emoji/Emoji.tsx (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
app/containers/EmojiPicker/Emoji.tsx (1)
app/containers/EmojiPicker/interfaces.ts (1)
  • IEmojiProps (44-46)
app/components/Emoji/Emoji.tsx (1)
app/definitions/IEmoji.ts (1)
  • ICustomEmoji (12-15)
🔇 Additional comments (1)
app/containers/EmojiPicker/Emoji.tsx (1)

7-12: LGTM: wrapper cleanly delegates to the shared Emoji component

Keeps styling concerns local and centralizes rendering. Nice.

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: 0

🧹 Nitpick comments (5)
app/components/Emoji/Emoji.tsx (5)

5-5: Avoid component → container dependency; relocate CustomEmoji to a shared layer

Importing from a container path in a shared component can create layering cycles. Consider moving CustomEmoji under app/components/Emoji/CustomEmoji (or a shared module) and updating imports. Keep a re-export at the old path for compatibility if needed.


10-11: Clarify styling API to prevent misuse and casts (add textStyle/imageStyle)

Today a single style prop is cast to both TextStyle and ImageStyle, which is easy to misuse. Keep style for BC, but add explicit textStyle/imageStyle props and apply them in the respective branches.

 interface IEmojiProps {
   emoji: string | ICustomEmoji;
-  style?: StyleProp<TextStyle | ImageStyle>;
+  style?: StyleProp<TextStyle | ImageStyle>; // backward compatible
+  textStyle?: StyleProp<TextStyle>;
+  imageStyle?: StyleProp<ImageStyle>;
   size?: number;
 }
 
-const Emoji = ({ emoji, style, size = 16 }: IEmojiProps) => {
+const Emoji = ({ emoji, style, textStyle, imageStyle, size = 16 }: IEmojiProps) => {
   const { formatShortnameToUnicode } = useShortnameToUnicode(true);
@@
-      <Text style={[{ fontSize: size }, style as StyleProp<TextStyle>]}>
+      <Text style={[{ fontSize: size }, style as StyleProp<TextStyle>, textStyle]}>
         {`${leading}${converted}${trailing}`}
       </Text>
@@
     <CustomEmoji
-      style={[{ width: size, height: size }, style as StyleProp<ImageStyle>]}
+      style={[{ width: size, height: size }, style as StyleProp<ImageStyle>, imageStyle]}
       emoji={emoji}
     />

Also applies to: 37-37, 45-45


1-1: Wrap with React.memo to reduce unnecessary re-renders in emoji-heavy lists

This component is frequently used in FlatLists. Memoization gives a cheap win.

-import React from 'react';
+import React, { memo } from 'react';
@@
-export default Emoji;
+export default memo(Emoji);

Also applies to: 51-51


37-37: Consider disabling font scaling for predictable emoji sizing

If you want consistent emoji sizes independent of system font scaling, set allowFontScaling={false} (or expose it as a prop defaulting to false).

-      <Text style={[{ fontSize: size }, style as StyleProp<TextStyle>]}>
+      <Text allowFontScaling={false} style={[{ fontSize: size }, style as StyleProp<TextStyle>]}>

17-33: Add minimal tests for the conversion matrix

Recommend unit tests covering: Unicode (“😄”), colon shortname (:smile:), bare shortname (smile), ASCII (":)"), unknown shortname (":notfound:"), and custom emoji object. I can draft Jest + @testing-library/react-native tests if you want.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0daf449 and b3daed3.

📒 Files selected for processing (1)
  • app/components/Emoji/Emoji.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
app/components/Emoji/Emoji.tsx (2)
app/definitions/IEmoji.ts (1)
  • ICustomEmoji (12-15)
app/lib/database/model/CustomEmoji.js (1)
  • CustomEmoji (8-18)
🔇 Additional comments (1)
app/components/Emoji/Emoji.tsx (1)

17-33: Shortname handling looks correct and resolves the earlier colon-wrapping issue — LGTM

Colon-delimited and bare shortnames are converted; Unicode and ASCII are preserved; leading/trailing whitespace retained. This addresses the prior review concern.

Also applies to: 36-40

@abhisek7154
Copy link
Author

✅ All tests have passed locally and the lint-testunit job has completed successfully on CircleCI.
When you have a moment, could a maintainer please approve the workflow so the remaining e2e and build jobs can proceed?

@diegolmello
Copy link
Member

@abhisek7154 we'll get here eventually. Just finishing internal priorities. Thanks for your contribution!

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.

improvement: create reusable emoji component
3 participants