feat: add icons to ComponentContext#3246
Conversation
📝 WalkthroughWalkthroughThe PR adds typed icon override slots to ChangesContext-driven icon customization
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
|
Size Change: +5.54 kB (+0.63%) Total Size: 886 kB 📦 View Changed
ℹ️ View Unchanged
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
src/plugins/ChannelDetail/Views/ChannelManagementView/ChannelManagementView.tsx (1)
62-67: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsolidate redundant
useComponentContextcalls.Both components fetch from
useComponentContext()multiple times in a row. You can consolidate these calls into a single destructuring statement to clean up the component bodies.
src/plugins/ChannelDetail/Views/ChannelManagementView/ChannelManagementView.tsx#L62-L67: combine theiconsandAvatarlookups into one hook call inChannelManagementInfoBody.src/plugins/ChannelDetail/Views/ChannelManagementView/ChannelManagementView.tsx#L323-L324: combine theiconsandAvatarlookups into one hook call inChannelManagementEditBody.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/plugins/ChannelDetail/Views/ChannelManagementView/ChannelManagementView.tsx` around lines 62 - 67, Consolidate the adjacent useComponentContext calls in ChannelManagementInfoBody at src/plugins/ChannelDetail/Views/ChannelManagementView/ChannelManagementView.tsx lines 62-67 into one destructuring statement containing both icons and Avatar, preserving their existing defaults. Apply the same consolidation in ChannelManagementEditBody at lines 323-324; both sites require direct changes.src/plugins/ChannelDetail/Views/ChannelMembersView/ChannelMembersBrowseView.tsx (1)
82-100: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueHoist the hook call to the parent component.
useComponentContextis currently called inside a dynamically created function (MemberTrailingSlot). Depending on how the layout component invokesTrailingSlot(as a render propTrailingSlot()vs a component<TrailingSlot />), this might violate the Rules of Hooks. Hoisting it to the top level ofChannelMembersBrowseViewItemis safer and more idiomatic.♻️ Proposed refactor
- const TrailingSlot = useMemo( - () => - function MemberTrailingSlot() { - const { icons: { IconMute = DefaultIconMute } = {} } = useComponentContext(); - return ( - <div className='str-chat__channel-detail__channel-members-view__list-item__trailing-slot'> - {roleTranslation ? ( - <span className='str-chat__channel-detail__channel-members-view__role-label'> - {roleTranslation} - </span> - ) : null} - {isMuted ? ( - <IconMute className='str-chat__channel-detail__channel-members-view__list-item__indicator-icon str-chat__channel-detail__channel-members-view__list-item__indicator-icon--mute' /> - ) : null} - </div> - ); - }, - [isMuted, roleTranslation], - ); + const { icons: { IconMute = DefaultIconMute } = {} } = useComponentContext(); + + const TrailingSlot = useMemo( + () => + function MemberTrailingSlot() { + return ( + <div className='str-chat__channel-detail__channel-members-view__list-item__trailing-slot'> + {roleTranslation ? ( + <span className='str-chat__channel-detail__channel-members-view__role-label'> + {roleTranslation} + </span> + ) : null} + {isMuted ? ( + <IconMute className='str-chat__channel-detail__channel-members-view__list-item__indicator-icon str-chat__channel-detail__channel-members-view__list-item__indicator-icon--mute' /> + ) : null} + </div> + ); + }, + [isMuted, roleTranslation, IconMute], + );🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/plugins/ChannelDetail/Views/ChannelMembersView/ChannelMembersBrowseView.tsx` around lines 82 - 100, Move the useComponentContext call out of the dynamically created MemberTrailingSlot function and invoke it at the top level of ChannelMembersBrowseViewItem. Derive the IconMute fallback there, then have MemberTrailingSlot reuse that value while preserving the existing mute and role-label rendering.src/components/MessageComposer/AttachmentPreviewList/GeolocationPreview.tsx (1)
12-15: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove the deprecated string argument.
The
_componentNameparameter inuseComponentContextis marked as@deprecated. It is recommended to call the hook without any arguments.♻️ Proposed refactor
const GeolocationPreviewImage = () => { - const { icons: { IconLocation = DefaultIconLocation } = {} } = useComponentContext( - 'GeolocationPreviewImage', - ); + const { icons: { IconLocation = DefaultIconLocation } = {} } = useComponentContext(); return (🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/MessageComposer/AttachmentPreviewList/GeolocationPreview.tsx` around lines 12 - 15, Update GeolocationPreviewImage to call useComponentContext without the deprecated string argument, while preserving the existing icons destructuring and DefaultIconLocation fallback.src/components/MessageActions/MessageActions.defaults.tsx (1)
94-94: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueMerge duplicate
useComponentContextinvocations.
useComponentContext()is already invoked earlier in this action function (around line 80) to retrieveReactionSelector. You can consolidate these calls into a single destructuring statement.♻️ Proposed refactor
React() { - const { ReactionSelector = DefaultReactionSelector } = useComponentContext(); const { anchorReferenceElement } = useContextMenuContext(); const { isMyMessage, message, threadList } = useMessageContext(); @@ -91,3 +90,5 @@ const dialogIsOpen = useDialogIsOpen(dialogId, dialogManager?.id); - const { icons: { IconEmoji = DefaultIconEmoji } = {} } = useComponentContext(); + const { + icons: { IconEmoji = DefaultIconEmoji } = {}, + ReactionSelector = DefaultReactionSelector, + } = useComponentContext(); return (🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/MessageActions/MessageActions.defaults.tsx` at line 94, Merge the later useComponentContext() call used to obtain IconEmoji into the earlier invocation that retrieves ReactionSelector in the same action function. Extend the existing destructuring to read both values, preserving DefaultIconEmoji as the fallback when icons or IconEmoji are unavailable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/Badge/MediaBadge.tsx`:
- Around line 17-30: Update the voice entry in MediaBadge’s
mediaBadgeVariantToIcon mapping to use the exact MediaBadgeVariant value
"voice-recording" instead of "voiceRecording", while preserving the existing
microphone icon mapping.
---
Nitpick comments:
In `@src/components/MessageActions/MessageActions.defaults.tsx`:
- Line 94: Merge the later useComponentContext() call used to obtain IconEmoji
into the earlier invocation that retrieves ReactionSelector in the same action
function. Extend the existing destructuring to read both values, preserving
DefaultIconEmoji as the fallback when icons or IconEmoji are unavailable.
In `@src/components/MessageComposer/AttachmentPreviewList/GeolocationPreview.tsx`:
- Around line 12-15: Update GeolocationPreviewImage to call useComponentContext
without the deprecated string argument, while preserving the existing icons
destructuring and DefaultIconLocation fallback.
In
`@src/plugins/ChannelDetail/Views/ChannelManagementView/ChannelManagementView.tsx`:
- Around line 62-67: Consolidate the adjacent useComponentContext calls in
ChannelManagementInfoBody at
src/plugins/ChannelDetail/Views/ChannelManagementView/ChannelManagementView.tsx
lines 62-67 into one destructuring statement containing both icons and Avatar,
preserving their existing defaults. Apply the same consolidation in
ChannelManagementEditBody at lines 323-324; both sites require direct changes.
In
`@src/plugins/ChannelDetail/Views/ChannelMembersView/ChannelMembersBrowseView.tsx`:
- Around line 82-100: Move the useComponentContext call out of the dynamically
created MemberTrailingSlot function and invoke it at the top level of
ChannelMembersBrowseViewItem. Derive the IconMute fallback there, then have
MemberTrailingSlot reuse that value while preserving the existing mute and
role-label rendering.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 954d8a21-faf0-4bb8-bc63-0199423c5c8d
📒 Files selected for processing (103)
src/components/Attachment/Geolocation.tsxsrc/components/Attachment/Giphy.tsxsrc/components/Attachment/LinkPreview/Card.tsxsrc/components/Attachment/LinkPreview/CardAudio.tsxsrc/components/Attachment/ModalGallery.tsxsrc/components/Attachment/UnsupportedAttachment.tsxsrc/components/Attachment/VisibilityDisclaimer.tsxsrc/components/Attachment/components/DownloadButton.tsxsrc/components/Avatar/Avatar.tsxsrc/components/Badge/Badge.tsxsrc/components/Badge/MediaBadge.tsxsrc/components/BaseImage/ImagePlaceholder.tsxsrc/components/Button/PlayButton.tsxsrc/components/ChannelListItem/ChannelListItemActionButtons.defaults.tsxsrc/components/ChannelListItem/ChannelListItemUI.tsxsrc/components/ChatView/ChatView.tsxsrc/components/Dialog/components/Callout.tsxsrc/components/Dialog/components/ContextMenu.tsxsrc/components/Dialog/components/Prompt.tsxsrc/components/Dialog/components/Viewer.tsxsrc/components/EmptyStateIndicator/EmptyStateIndicator.tsxsrc/components/Form/NumericInput.tsxsrc/components/Form/TextInput.tsxsrc/components/Gallery/GalleryHeader.tsxsrc/components/Gallery/GalleryUI.tsxsrc/components/Icons/index.tssrc/components/Icons/slots.tssrc/components/Loading/LoadingIndicator.tsxsrc/components/Location/ShareLocationDialog.tsxsrc/components/MediaRecorder/AudioRecorder/AudioRecorderRecordingControls.tsxsrc/components/MediaRecorder/AudioRecorder/AudioRecordingButtonWithNotification.tsxsrc/components/MediaRecorder/AudioRecorder/AudioRecordingPlayback.tsxsrc/components/MediaRecorder/AudioRecorder/AudioRecordingPreview.tsxsrc/components/MediaRecorder/AudioRecorder/__tests__/AudioRecordingPreview.test.tsxsrc/components/Message/MessageAlsoSentInChannelIndicator.tsxsrc/components/Message/MessageDeletedBubble.tsxsrc/components/Message/MessageStatus.tsxsrc/components/Message/MessageTranslationIndicator.tsxsrc/components/Message/PinIndicator.tsxsrc/components/Message/ReminderNotification.tsxsrc/components/MessageActions/DownloadSubmenu.tsxsrc/components/MessageActions/MessageActions.defaults.tsxsrc/components/MessageActions/RemindMeSubmenu.tsxsrc/components/MessageBounce/MessageBouncePrompt.tsxsrc/components/MessageComposer/AttachmentPreviewList/AudioAttachmentPreview.tsxsrc/components/MessageComposer/AttachmentPreviewList/FileAttachmentPreview.tsxsrc/components/MessageComposer/AttachmentPreviewList/GeolocationPreview.tsxsrc/components/MessageComposer/AttachmentPreviewList/MediaAttachmentPreview.tsxsrc/components/MessageComposer/AttachmentPreviewList/UnsupportedAttachmentPreview.tsxsrc/components/MessageComposer/AttachmentSelector/AttachmentSelector.tsxsrc/components/MessageComposer/AttachmentSelector/CommandsMenu.tsxsrc/components/MessageComposer/AttachmentSelector/__tests__/CommandsMenu.test.tsxsrc/components/MessageComposer/CommandChip.tsxsrc/components/MessageComposer/LinkPreviewList.tsxsrc/components/MessageComposer/MessageComposerActions.tsxsrc/components/MessageComposer/QuotedMessagePreview.tsxsrc/components/MessageComposer/RemoveAttachmentPreviewButton.tsxsrc/components/MessageComposer/SendButton.tsxsrc/components/MessageComposer/SendToChannelCheckbox.tsxsrc/components/MessageComposer/WithDragAndDropUpload.tsxsrc/components/MessageComposer/__tests__/CommandChip.test.tsxsrc/components/MessageList/ScrollToLatestMessageButton.tsxsrc/components/MessageList/UnreadMessagesNotification.tsxsrc/components/MessageList/UnreadMessagesSeparator.tsxsrc/components/Modal/CloseButtonOnModalOverlay.tsxsrc/components/Notifications/Notification.tsxsrc/components/Poll/PollActions/PollResults/PollOptionWithVotesHeader.tsxsrc/components/Poll/PollCreationDialog/OptionFieldSet.tsxsrc/components/Poll/PollCreationDialog/PollCreationDialogControls.tsxsrc/components/Poll/PollCreationDialog/PollOptionReorderHandle.tsxsrc/components/Reactions/MessageReactionsDetail.tsxsrc/components/Reactions/ReactionSelector.tsxsrc/components/Reactions/ReactionSelectorWithButton.tsxsrc/components/SummarizedMessagePreview/SummarizedMessagePreview.tsxsrc/components/TextareaComposer/SuggestionList/MentionItem/BroadcastMentionItem.tsxsrc/components/TextareaComposer/SuggestionList/MentionItem/RoleItem.tsxsrc/components/TextareaComposer/SuggestionList/MentionItem/UserGroupItem.tsxsrc/components/TextareaComposer/__tests__/CommandItem.test.tsxsrc/components/Thread/ThreadHeader.tsxsrc/components/Threads/ThreadList/ThreadListEmptyPlaceholder.tsxsrc/components/Threads/ThreadList/ThreadListUnseenThreadsBanner.tsxsrc/components/VideoPlayer/VideoThumbnail.tsxsrc/context/ComponentContext.tsxsrc/context/WithComponents.tsxsrc/plugins/ChannelDetail/ChannelDetail.tsxsrc/plugins/ChannelDetail/ChannelDetailEmptyList.tsxsrc/plugins/ChannelDetail/ChannelDetailSearchInput.tsxsrc/plugins/ChannelDetail/SectionNavigator/SectionNavigatorHeader.tsxsrc/plugins/ChannelDetail/Views/ChannelFilesView/ChannelFilesEmptyList.tsxsrc/plugins/ChannelDetail/Views/ChannelFilesView/__tests__/ChannelFilesView.test.tsxsrc/plugins/ChannelDetail/Views/ChannelManagementView/ChannelManagementActions.defaults.tsxsrc/plugins/ChannelDetail/Views/ChannelManagementView/ChannelManagementView.tsxsrc/plugins/ChannelDetail/Views/ChannelMediaView/ChannelMediaEmptyList.tsxsrc/plugins/ChannelDetail/Views/ChannelMediaView/ChannelMediaView.tsxsrc/plugins/ChannelDetail/Views/ChannelMemberDetailView/ChannelMemberActions.defaults.tsxsrc/plugins/ChannelDetail/Views/ChannelMembersView/ChannelMembersAddView.tsxsrc/plugins/ChannelDetail/Views/ChannelMembersView/ChannelMembersBrowseView.tsxsrc/plugins/ChannelDetail/Views/ChannelMembersView/ChannelMembersHeaderActions.defaults.tsxsrc/plugins/ChannelDetail/Views/ChannelMembersView/__tests__/ChannelMembersAddView.test.tsxsrc/plugins/ChannelDetail/Views/ChannelMembersView/__tests__/ChannelMembersBrowseView.test.tsxsrc/plugins/ChannelDetail/Views/PinnedMessagesView/PinnedMessagesEmptyList.tsxsrc/plugins/ChannelDetail/Views/PinnedMessagesView/__tests__/PinnedMessagesView.test.tsxsrc/plugins/Emojis/EmojiPicker.tsx
| export const MediaBadge = ({ attachment, variant }: MediaBadgeProps) => { | ||
| const Icon = MediaBadgeVariantToIcon[variant]; | ||
| const { | ||
| icons: { | ||
| IconMicrophoneSolid = DefaultIconMicrophoneSolid, | ||
| IconVideoFill = DefaultIconVideoFill, | ||
| } = {}, | ||
| } = useComponentContext(); | ||
|
|
||
| const mediaBadgeVariantToIcon: Record<MediaBadgeVariant, ComponentType> = { | ||
| video: IconVideoFill, | ||
| voiceRecording: IconMicrophoneSolid, | ||
| }; | ||
|
|
||
| const Icon = mediaBadgeVariantToIcon[variant]; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
Fix variant mapping key to match the exact string type.
The MediaBadgeVariant type defines the voice variant as 'voice-recording', but the mediaBadgeVariantToIcon dictionary uses the camelCased key voiceRecording. Consequently, when variant='voice-recording' is passed, the icon will fail to resolve and render.
🐛 Proposed fix for the dictionary key
const mediaBadgeVariantToIcon: Record<MediaBadgeVariant, ComponentType> = {
video: IconVideoFill,
- voiceRecording: IconMicrophoneSolid,
+ 'voice-recording': IconMicrophoneSolid,
};📝 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.
| export const MediaBadge = ({ attachment, variant }: MediaBadgeProps) => { | |
| const Icon = MediaBadgeVariantToIcon[variant]; | |
| const { | |
| icons: { | |
| IconMicrophoneSolid = DefaultIconMicrophoneSolid, | |
| IconVideoFill = DefaultIconVideoFill, | |
| } = {}, | |
| } = useComponentContext(); | |
| const mediaBadgeVariantToIcon: Record<MediaBadgeVariant, ComponentType> = { | |
| video: IconVideoFill, | |
| voiceRecording: IconMicrophoneSolid, | |
| }; | |
| const Icon = mediaBadgeVariantToIcon[variant]; | |
| export const MediaBadge = ({ attachment, variant }: MediaBadgeProps) => { | |
| const { | |
| icons: { | |
| IconMicrophoneSolid = DefaultIconMicrophoneSolid, | |
| IconVideoFill = DefaultIconVideoFill, | |
| } = {}, | |
| } = useComponentContext(); | |
| const mediaBadgeVariantToIcon: Record<MediaBadgeVariant, ComponentType> = { | |
| video: IconVideoFill, | |
| 'voice-recording': IconMicrophoneSolid, | |
| }; | |
| const Icon = mediaBadgeVariantToIcon[variant]; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/Badge/MediaBadge.tsx` around lines 17 - 30, Update the voice
entry in MediaBadge’s mediaBadgeVariantToIcon mapping to use the exact
MediaBadgeVariant value "voice-recording" instead of "voiceRecording", while
preserving the existing microphone icon mapping.
🎯 Goal
Adds ability to override icons through the component context.
Ref: GetStream/stream-chat-react-native#3731
Summary by CodeRabbit
New Features
Bug Fixes