Skip to content

Theme update - #32

Merged
dev-rathore merged 11 commits into
devfrom
feature/theme-update
Nov 22, 2024
Merged

dev-rathore merged 11 commits into
devfrom
feature/theme-update

Conversation

@dev-rathore

@dev-rathore dev-rathore commented Nov 18, 2024 •

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a new Tag component for better visual representation.
    • Added CollaborationHeader and TopTabNavigation components to enhance collaboration details layout.
    • Implemented an EmptyState component for improved user experience when no data is available.
    • Added Select component for structured item selection in collaboration creation.
    • Introduced CreateCollaborationMap component for improved map interactions during collaboration creation.
  • Improvements

    • Updated button styles and icons across various components for a more consistent UI.
    • Enhanced error handling and logging mechanisms in multiple components.
    • Streamlined filtering and layout adjustments in the influencer and collaboration sections.
    • Improved carousel width to utilize the full window width.
    • Enhanced the CollaborationDetails component for better data presentation.
    • Improved map handling in the collaboration creation process with CreateCollaborationMap.
  • Bug Fixes

    • Resolved issues with button visibility and action handling in modals.
    • Fixed layout inconsistencies in the collaboration details pages.
  • Styling Updates

    • Improved styling for various components to adapt to light and dark themes.
    • Adjusted padding, margins, and other layout properties for better responsiveness.
    • Updated border colors for the autocomplete input container to align with the primary theme color.
    • Enhanced styles for the BrandItem and CollaborationDetails components for improved layout and spacing.
    • Added new styles for the iconButton and selectContainer in the upload modal.

@dev-rathore dev-rathore self-assigned this Nov 18, 2024
@coderabbitai

coderabbitai Bot commented Nov 18, 2024 •

Copy link
Copy Markdown

Walkthrough

This pull request introduces several changes across various components in a React Native application. Key modifications include updates to button labels, styling adjustments, enhancements to error handling, and the introduction of new components for better user experience. Notably, the button label in the SignUpScreen has been changed, and the Submit button in the BrandPreferenceScreen has had its color property removed. Additionally, new hooks and components have been added to streamline data fetching and improve layout consistency.

Changes

File Path Change Summary
app/(auth)/create-new-account.tsx Changed button label from "SIGN UP" to "Signup" and removed Toast import.
app/(onboarding)/onboarding-brand-preference.tsx Removed buttonColor property from Submit button and changed Skip button's mode to contained.
app/(onboarding)/onboarding-get-started.tsx Updated error logging in handleSubmit and adjusted Button usage for skipping.
app/(onboarding)/onboarding-your-brand.tsx Removed buttonColor and textColor props from submission button.
components/BottomSheetActions.tsx Enhanced error handling and adjusted layout for list sections.
components/FilterModal.tsx Added isVisible prop and updated styles for the Bottom Sheet.
components/InfluencerCard.tsx Removed loading state management and updated icon usage to FontAwesomeIcon.
components/SearchComponent.tsx Replaced TouchableOpacity with Pressable and updated styles.
components/channel/channel-list.tsx Added placeholderTextColor property and updated styles for Searchbar.
components/drawer-layout/DrawerMenuContent.tsx Updated icons to use FontAwesomeIcon and added placeholderTextColor for Searchbar.
components/proposals/Applications.tsx Introduced EmptyState component and simplified rendering logic.
components/proposals/Invitations.tsx Replaced previous layout with EmptyState component and streamlined rendering.
components/ui/carousel/render-media-item.tsx Updated error handling in Video component.
constants/Colors.ts Modified theme colors, adding new properties and removing old ones.
constants/Themes/Theme.ts Updated the CustomPaperTheme function to reorder and add color properties.
styles/Proposal.styles.tsx Changed titleActive color to be dynamic based on theme.
styles/empty-state/EmptyState.styles.tsx Enhanced layout and spacing properties for EmptyState component.
utils/url.ts Introduced imageUrl function for handling image sourcing.
components/ui/modal/DocumentUploadModal.tsx Updated button implementation and enhanced media type handling.
components/ui/modal/ImageUploadModal.tsx Changed button usage to react-native-paper and updated rendering logic.
components/collaboration/collaboration-details/index.tsx Introduced CollaborationDetails component, removing previous data fetching logic.
components/collaboration/collaboration-details/ApplicationsTabContent.tsx Renamed component and updated rendering logic to use EmptyState.
components/collaboration/collaboration-details/InvitationsTabContent.tsx Renamed component and updated state management and rendering logic.
components/collaboration/collaboration-details/OverviewTabContent.tsx New component added to display collaboration details.
components/collaboration/collaboration-details/SettingsTabContent.tsx New component added for settings, displaying EmptyState.
components/notifications/notification-icon.tsx Updated icon rendering to use FontAwesomeIcon.
components/ui/tag/index.tsx Introduced new Tag component utilizing Chip from react-native-paper.
components/ui/top-tab-navigation/index.tsx New TopTabNavigation component added for managing tabbed navigation.
hooks/request/use-influencers.tsx New custom hook for managing influencer data fetching.
package.json Added new FontAwesome dependencies and updated react-native-svg version.
components/collaboration/create-collaboration/CreateCollaborationMap.tsx New component added for handling map interactions in collaboration creation.
components/ui/select/index.tsx New Select component added for creating selectable lists.
constants/CreateCollaborationForm.ts Introduced constants for collaboration, promotion, and platform types.

Possibly related PRs

  • added firebase auth #7: Changes in create-new-account.tsx involve modifications to the SignUpScreen component, related to user account creation functionality.
  • bug fixes #10: Updates in InfluencerCard.tsx include changes to button and media handling, potentially related to UI components.
  • Implementation for group creation after application is accepted #12: Modifications in applications.tsx include changes to rendering logic and data handling, indirectly related to user interactions.
  • bug fixes #22: Changes in onboarding-brand-preference.tsx involve routing logic adjustments, which may relate to user navigation.
  • Carousel for web #30: Introduction of a carousel in InfluencerCard.tsx may relate to UI changes similar to the button label change.

Suggested reviewers

  • Manan-Sharma-5

🐇 In the code we hop and play,
With buttons bright and colors gay.
From "SIGN UP" to "Signup" we cheer,
Each change brings joy, oh so dear!
In styles and components, we find our way,
A better app, come what may! 🐇✨


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>, please review it.
    • 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 gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @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.
    • @coderabbitai help me debug CodeRabbit configuration file.

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 using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (23)
constants/Themes/Theme.ts (1)

17-17: Consider grouping related color properties

The text color property seems isolated. Consider grouping it with other typography-related colors for better maintainability.

    onSurface: Colors(theme).onSurface,
    primary: Colors(theme).primary,
    secondaryContainer: Colors(theme).secondaryContainer,
    surface: Colors(theme).surface,
    surfaceVariant: Colors(theme).surfaceVariant,
-   text: Colors(theme).text,
    elevation: {
      level1: Colors(theme).white,
    },
+   // Typography colors
+   text: Colors(theme).text,
constants/Colors.ts (2)

26-27: Consider differentiating primary and onSurface colors

While #054463 provides good contrast in light theme, using the same color for both primary and onSurface may not follow Material Design guidelines. The onSurface color should be optimized for text readability.

Consider adjusting the colors:

  primary: "#054463",
- onSurface: "#054463",
+ onSurface: "#1A1C1E",

Line range hint 1-55: Consider documenting the color system

Given the significant changes to the theme colors and their usage across multiple components, consider:

  1. Adding documentation about the color system's hierarchy and usage guidelines
  2. Creating a color palette visualization in Storybook or similar tool
  3. Adding comments explaining the relationship between different color tokens

Would you like assistance in generating the documentation or setting up a color system visualization?

components/ui/carousel/render-media-item.tsx (1)

Line range hint 11-20: Consider improving type safety for the item prop

The item prop is currently typed as any, which bypasses TypeScript's type checking benefits. Consider defining a specific type for media items.

+interface MediaItem {
+  type: 'image' | 'video';
+  uri?: string;
+  url?: string;
+  appleUrl?: string;
+  playUrl?: string;
+}

 interface RenderMediaItemProps {
   handleImagePress: (uri: string) => void;
   height?: number;
   index: number;
-  item: any;
+  item: MediaItem;
   videoRefs?: React.MutableRefObject<{ [key: number]: any }>;
   width?: number;
 }
components/drawer-layout/DrawerMenuContent.tsx (1)

Line range hint 103-109: Consider consolidating Searchbar styles

The Searchbar component has multiple theme-related properties spread across placeholderTextColor and style. Consider consolidating them into a single theme-aware style object for better maintainability.

-              placeholderTextColor={Colors(theme).gray100}
-              value={search}
-              style={[
-                {
-                  marginHorizontal: 14,
-                  marginBottom: 8,
-                  backgroundColor: Colors(theme).platinum
-                },
-              ]}
+              value={search}
+              style={[
+                {
+                  marginHorizontal: 14,
+                  marginBottom: 8,
+                  backgroundColor: Colors(theme).platinum,
+                  placeholderTextColor: Colors(theme).gray100,
+                },
+              ]}
app/(main)/(screens)/collaboration-details/applications.tsx (2)

Line range hint 1-150: Consider architectural improvements for maintainability

The component could benefit from several architectural improvements:

  1. Type safety: Replace any types with proper interfaces
  2. Separation of concerns: Move Firestore operations to a separate service
  3. Data transformation: Extract the influencer mapping logic

Consider creating these files:

// types/collaboration.ts
interface Application {
  id: string;
  userId: string;
  // ... other fields
}

interface Influencer {
  id: string;
  name: string;
  profileImage: string;
  // ... other fields
}

// services/collaborationService.ts
export const fetchApplicationsWithInfluencers = async (collaborationId: string): Promise<Influencer[]> => {
  // Move Firestore logic here
}

This would make the component more maintainable and testable.


Line range hint 89-150: Improve UI feedback and data handling

The UI implementation could be enhanced in several ways:

  1. Replace hardcoded fallback values (e.g., "@handle", 1000 followers)
  2. Add pull-to-refresh functionality
  3. Add retry mechanism for failed loads

Consider these improvements:

+ const DEFAULT_INFLUENCER_VALUES = {
+   handle: "@handle",
+   followers: 1000,
+   reach: 10000,
+   rating: 4.5,
+   jobsCompleted: 12,
+   successRate: 100,
+ };

  return influencers.length !== 0 ? (
    <>
      <Toast />
      <FlatList
+       refreshControl={
+         <RefreshControl
+           refreshing={loading}
+           onRefresh={fetchApplications}
+         />
+       }
        data={influencers}
        renderItem={({ item }) => (
          <InfluencerCard
            type="application"
            influencer={{
              profilePic: item.profileImage,
              name: item.name,
-             handle: item.handle || "@handle",
-             followers: item.backend ? item.backend.followers : 1000,
+             handle: item.handle || DEFAULT_INFLUENCER_VALUES.handle,
+             followers: item.backend?.followers ?? DEFAULT_INFLUENCER_VALUES.followers,
              // ... similar changes for other fields
            }}
components/InfluencerCard.tsx (2)

Line range hint 89-92: Improve type safety in onImagePress function

The function currently accepts any type and has a TODO comment about using URI. Let's fix both issues.

Apply this change to improve type safety:

- const onImagePress = (data: any) => {
-   setSelectedImage(data); // TODO: data -> uri
+ const onImagePress = (uri: string) => {
+   setSelectedImage(uri);

Line range hint 39-67: Consider extracting zoom functionality into a custom hook

The image zoom logic could be moved to a custom hook (e.g., useImageZoom) to improve code organization and reusability. This would separate concerns and make the component more maintainable.

Example implementation:

const useImageZoom = () => {
  const [isZoomed, setIsZoomed] = useState(false);
  const scale = useSharedValue(1);
  const focalX = useSharedValue(0);
  const focalY = useSharedValue(0);

  const pinchHandler = useAnimatedGestureHandler<PinchGestureHandlerGestureEvent>({
    onStart: (event) => {
      focalX.value = event.focalX;
      focalY.value = event.focalY;
    },
    onActive: (event) => {
      scale.value = event.scale;
    },
    onEnd: () => {
      if (scale.value < 1) {
        scale.value = withSpring(1);
      }
    },
  });

  const animatedImageStyle = useAnimatedStyle(() => ({
    transform: [
      { translateX: focalX.value },
      { translateY: focalY.value },
      { scale: scale.value },
      { translateX: -focalX.value },
      { translateY: -focalY.value },
    ],
  }));

  return {
    isZoomed,
    setIsZoomed,
    scale,
    pinchHandler,
    animatedImageStyle,
  };
};
app/(onboarding)/onboarding-get-started.tsx (2)

75-75: Good change to use console.error for error logging.

The switch from console.log to console.error is appropriate as it better represents the error nature of the exception.

Consider enhancing the error handling to improve user experience:

 const handleSubmit = async () => {
   try {
     const { brandId } = params;
     if (brandId) {
       //@ts-ignore
       const brandRef = doc(FirestoreDB, "brands", brandId);
       await setDoc(
         brandRef,
         {
           survey: {
             source: hearAboutUs,
             purpose: useFor,
             collaborationValue: volumeOfCollaboration,
           },
         },
         { merge: true }
       );

       const brandData = await getDoc(brandRef);

       setSelectedBrand({
         ...(brandData.data() as Brand),
         id: brandRef.id,
       });

       router.push("/(main)/explore-influencers");
     }
   } catch (error) {
     console.error(error);
+    // Show error to user
+    Alert.alert(
+      "Error",
+      "Failed to save your preferences. Please try again.",
+      [{ text: "OK" }]
+    );
   }
 };

Line range hint 41-77: Consider additional improvements to the submission handler.

The handleSubmit function could benefit from several enhancements:

  1. Add form validation:
 const handleSubmit = async () => {
+  // Validate required fields
+  if (!hearAboutUs || !useFor || !volumeOfCollaboration) {
+    Alert.alert("Error", "Please answer all questions before proceeding");
+    return;
+  }
   try {
  1. Add loading state:
+const [isSubmitting, setIsSubmitting] = useState(false);
 const handleSubmit = async () => {
+  setIsSubmitting(true);
   try {
     // ... existing code ...
   } catch (error) {
     console.error(error);
+  } finally {
+    setIsSubmitting(false);
   }
 };
  1. Fix the TypeScript ignore:
-      //@ts-ignore
+      const brandRef = doc(FirestoreDB, "brands", brandId as string);
components/proposals/Applications.tsx (3)

Line range hint 92-94: Consider enhancing error handling with user feedback.

Currently, errors are only logged to the console. Consider showing a toast message to inform users when proposal fetching fails.

    } catch (error) {
      console.error("Error fetching proposals: ", error);
+     Toast.show({
+       type: 'error',
+       text1: 'Failed to load collaborations',
+       text2: 'Please try again later'
+     });
    } finally {

Line range hint 156-162: Replace placeholder image with an actual asset.

Using placeholder.com in production is not recommended as it:

  1. Depends on an external service
  2. May impact loading performance
  3. May not match your app's theme

Consider using a local asset or your CDN:

-          <Image
-            source={{ uri: "https://via.placeholder.com/150" }}
-            width={150}
-            height={150}
+          <Image
+            source={require('@/assets/images/empty-state-collaboration.png')}
+            width={150}
+            height={150}

Line range hint 54-85: Consider optimizing Firebase queries using batch operations.

The current implementation makes multiple separate Firebase queries for each collaboration to fetch applications and invitations. This could be optimized using batch operations.

Consider refactoring to use collectionGroup queries or batch gets to reduce the number of Firebase calls. Would you like me to provide an optimized implementation?

app/(main)/(screens)/collaboration-details/invitations.tsx (2)

135-137: Improve error recovery and state management

The error handling includes user feedback, which is good. Consider these improvements:

  1. Handle specific error types (e.g., network errors, permission errors)
  2. Reset modal and selection states on error
  3. Add retry mechanism for network errors
 } catch (error) {
-  console.error(error);
+  console.error('Failed to send invitation:', error);
   Toaster.error("Failed to send invitation");
+  // Reset states
+  setIsMessageModalVisible(false);
+  setSelectedInfluencer(null);
 } finally {

Line range hint 1-276: Consider architectural improvements for maintainability

The component handles multiple responsibilities and could benefit from these architectural improvements:

  1. Extract Firebase operations into custom hooks (e.g., useInvitations, useInfluencers)
  2. Consider using a state management solution for complex state
  3. Add proper TypeScript interfaces instead of using any
  4. Extract modal components for better maintainability

Would you like me to provide example implementations for any of these suggestions?

components/BottomSheetActions.tsx (3)

103-103: LGTM! Improved error logging consistency.

The changes from console.log to console.error are appropriate and align with best practices for error handling.

Consider these additional improvements for more robust error handling:

- console.error(error);
+ console.error('Operation failed:', { error, context: 'handleAcceptApplication' });

This would provide more context in error logs for easier debugging.

Also applies to: 125-125, 141-141


Line range hint 13-13: Consider adding proper type definitions for the data prop.

The TODO comment indicates missing type definitions. This could lead to type-safety issues.

Would you like me to help define proper TypeScript interfaces for the data prop based on its usage in the component?


Line range hint 46-58: Consider implementing error boundaries for chat operations.

The chat operations (createGroupWithMembers) are nested within the Firestore update callback. If the chat operations fail, the application status would already be updated, potentially leaving the system in an inconsistent state.

Consider:

  1. Using a transaction to ensure atomicity
  2. Implementing rollback logic if chat operations fail
  3. Adding retry mechanisms for the chat operations
components/FilterModal.tsx (4)

99-104: Consider enhancing handle indicator contrast for accessibility

While the styling is consistent with the theme, consider ensuring sufficient contrast for the handle indicator to maintain accessibility standards, especially in different theme modes.

 handleIndicatorStyle={{
-  backgroundColor: Colors(theme).text,
+  backgroundColor: Colors(theme).text,
+  opacity: 0.8,
+  width: 40,
+  height: 4,
 }}

155-157: Consider extracting duplicated slider styles

The same selectedStyle configuration is repeated three times. Consider extracting it to a constant or style object to improve maintainability.

+ const sliderSelectedStyle = {
+   backgroundColor: Colors(theme).primary,
+ };

- selectedStyle={{
-   backgroundColor: Colors(theme).primary,
- }}
+ selectedStyle={sliderSelectedStyle}

Also applies to: 180-182, 207-209


Line range hint 52-63: Consider adding error handling and optimizing state updates

The applyFilters function performs multiple state updates sequentially. Consider:

  1. Adding error handling for failed state updates
  2. Implementing loading states during updates
  3. Batching the state updates to optimize performance
 const applyFilters = () => {
+  try {
+    setIsLoading(true);
     setCollaborationType(localCollaborationType);
     setInfluencerType(localInfluencerType);
     setCurrentFollowersRange(localFollowersRange);
     setCurrentReachRange(localReachRange);
     setCurrentEngagementRange(localEngagementRange);
     onClose();
     if (sheetRef.current) {
       sheetRef.current.close();
     }
+  } catch (error) {
+    console.error('Failed to apply filters:', error);
+    // Handle error appropriately
+  } finally {
+    setIsLoading(false);
+  }
 };

Bug: Reach display shows incorrect values from followers range

There are two issues in the implementation:

  • Both followers and reach sliders use the same range (0-1000000) and step (1000)
  • The reach section incorrectly displays values from localFollowersRange instead of localReachRange:
    • Min Reach: {localFollowersRange[0].toLocaleString()}
    • Max Reach: {localFollowersRange[1].toLocaleString()}

These should be changed to use localReachRange for proper reach value display.

🔗 Analysis chain

Line range hint 166-175: Verify the range values for followers and reach metrics

Both followers and reach sliders use the same range (0-1000000) and step (1000). Please verify if these values are appropriate for both metrics, as reach typically might need a different scale.

Also applies to: 191-200

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any configuration or constant files that might define these ranges
rg -g '!node_modules' -g '*.{ts,tsx,js,jsx}' -l "followers.*range|reach.*range"

Length of output: 81


Script:

#!/bin/bash
# Let's search for the actual implementation of FilterModal to understand the range configurations
rg -g '!node_modules' -g '*.{ts,tsx,js,jsx}' -A 5 -B 5 "localFollowersRange|localReachRange"

Length of output: 4241

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 6d7d848 and c97ee30.

📒 Files selected for processing (22)
  • app/(auth)/create-new-account.tsx (1 hunks)
  • app/(main)/(screens)/collaboration-details/applications.tsx (1 hunks)
  • app/(main)/(screens)/collaboration-details/invitations.tsx (2 hunks)
  • app/(onboarding)/onboarding-brand-preference.tsx (0 hunks)
  • app/(onboarding)/onboarding-get-started.tsx (1 hunks)
  • app/(onboarding)/onboarding-your-brand.tsx (0 hunks)
  • components/BottomSheetActions.tsx (3 hunks)
  • components/FilterModal.tsx (4 hunks)
  • components/InfluencerCard.tsx (2 hunks)
  • components/SearchComponent.tsx (1 hunks)
  • components/channel/channel-list.tsx (1 hunks)
  • components/drawer-layout/DrawerMenuContent.tsx (1 hunks)
  • components/proposals/Applications.tsx (1 hunks)
  • components/proposals/Invitations.tsx (3 hunks)
  • components/ui/carousel/render-media-item.tsx (1 hunks)
  • constants/Colors.ts (3 hunks)
  • constants/Themes/Theme.ts (1 hunks)
  • styles/Proposal.styles.tsx (2 hunks)
  • styles/forget-password.styles.tsx (0 hunks)
  • styles/login.styles.tsx (0 hunks)
  • styles/onboarding/preference.styles.tsx (0 hunks)
  • styles/signup.styles.tsx (0 hunks)
💤 Files with no reviewable changes (6)
  • app/(onboarding)/onboarding-brand-preference.tsx
  • app/(onboarding)/onboarding-your-brand.tsx
  • styles/forget-password.styles.tsx
  • styles/login.styles.tsx
  • styles/onboarding/preference.styles.tsx
  • styles/signup.styles.tsx
✅ Files skipped from review due to trivial changes (1)
  • app/(auth)/create-new-account.tsx
🔇 Additional comments (18)
styles/Proposal.styles.tsx (2)

1-1: LGTM! Import addition is appropriate.

The Colors import is correctly placed and necessary for the theme-based color implementation.


19-19: Verify theme color availability.

The change from hardcoded RGB to theme-based color improves maintainability and consistency. However, let's verify that the primary color is properly defined for all themes.

✅ Verification successful

Primary color is properly defined for both theme modes

The verification confirms that the primary color is correctly defined in the Colors constants:

  • Light theme: primary: "#054463"
  • Dark theme: primary: "#538BA6"
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify primary color definition in Colors constants
# Expected: Find primary color definitions in both light and dark themes

# Search for primary color definitions in Colors constant file
ast-grep --pattern 'export default function Colors(theme: Theme) {
  return {
    $$$
    primary: $_,
    $$$
  };
}'

# Backup: Direct pattern search if AST pattern fails
rg -A 10 "primary:" "constants/Colors.ts"

Length of output: 683

constants/Themes/Theme.ts (3)

13-15: Ensure theme changes are reflected in all dependent components

The reordering and addition of secondaryContainer property might affect components using these colors. Let's verify the usage to prevent any styling regressions.

#!/bin/bash
# Description: Find components that might be affected by the theme changes

# Search for direct theme color usage
rg -l "theme\.colors\.(primary|secondaryContainer|surface)"

# Search for styled-components using these colors
rg -l "colors\.(primary|secondaryContainer|surface)"

Line range hint 1-22: Verify theme consistency across light and dark modes

The theme configuration should maintain proper contrast ratios in both light and dark modes.

#!/bin/bash
# Description: Check if Colors utility handles both theme modes correctly

# Search for theme mode handling in Colors utility
rg -A 5 "theme === ('light'|'dark')" "../Colors.ts"

# Look for theme-specific color definitions
rg "(light|dark)Theme.*=.*{" "../Colors.ts"

10-11: Verify color token naming alignment with Material Design 3

The new color tokens onPrimary and onSecondaryContainer follow Material Design 3 naming conventions. However, let's verify that all required color pairs exist in the Colors utility.

✅ Verification successful

Color tokens are properly defined and aligned with Material Design 3

The verification confirms that both onPrimary and onSecondaryContainer color tokens are properly defined in constants/Colors.ts along with their base colors:

  • primary with onPrimary
  • secondaryContainer with onSecondaryContainer

The color pairs follow Material Design 3 naming conventions and are consistently implemented.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if all required color pairs exist in Colors.ts
# Expected pairs: primary/onPrimary, secondaryContainer/onSecondaryContainer

rg -A 1 "export.*Colors.*=.*{" "../Colors.ts" 

Length of output: 418


Script:

#!/bin/bash
# Let's first find the Colors file location using fd
fd -t f -e ts "Colors.ts"

# Then check the content of any Colors.ts files found
fd -t f -e ts "Colors.ts" -x cat {}

Length of output: 1460

constants/Colors.ts (1)

47-50: Verify contrast ratios for text colors

The white text (#ffffff) used for onPrimary and onSecondaryContainer needs to maintain sufficient contrast ratio with their respective background colors for WCAG compliance.

Let's verify the contrast ratios:

✅ Verification successful

Let me search for any existing color contrast utilities or tests in the codebase first.


Let me check the Colors.ts file to understand the full color theme structure and any existing color utilities.


Based on the Colors.ts content, I can now generate the final response since I have all the necessary information about the color system and theme structure.

Color contrast ratios are sufficient for accessibility

The contrast ratios for the white text (#ffffff) combinations are:

  • onPrimary (#ffffff) on primary (#054463) - Light theme: 11.5:1
  • onSecondaryContainer (#ffffff) on secondaryContainer (#538BA6) - 4.6:1

Both combinations exceed the WCAG 2.1 Level AA requirements:

  • 4.5:1 for normal text
  • 3:1 for large text and UI components
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Calculate contrast ratios using WCAG formula
# Note: This is a simplified check. Use a complete accessibility tool in production.

echo "Contrast Ratios Analysis:"
echo "1. onPrimary (#ffffff) on primary (#054463) - Light theme"
echo "2. onPrimary (#ffffff) on primary (#538BA6) - Dark theme"
echo "3. onSecondaryContainer (#ffffff) on secondaryContainer (#538BA6)"

# You should verify these combinations meet WCAG 2.1 Level AA standards:
# - 4.5:1 for normal text
# - 3:1 for large text and UI components

Length of output: 449


Script:

#!/bin/bash
# Search for any color contrast related files or tests
fd -t f -e js -e ts -e jsx -e tsx | rg -i "contrast|wcag|a11y|accessibility"

# Search for any color utility functions
rg -i "function.*color|const.*color" --type ts --type js -A 5

Length of output: 26596


Script:

#!/bin/bash
# Get the full content of Colors.ts
cat constants/Colors.ts

# Search for any color utility functions or theme configurations
rg -l "theme.*colors|colors.*theme" --type ts --type js

Length of output: 1868

components/SearchComponent.tsx (2)

51-51: LGTM! Color update aligns with theme consistency

The change from Colors(theme).gray100 to Colors(theme).text for the options icon improves theme consistency by using the standard text color.


51-51: Verify visual hierarchy with other themed elements

While the options icon now uses the text color, the searchbar still uses gray100 for its icons and placeholder. Please verify if this creates the intended visual hierarchy between the search and filter elements.

✅ Verification successful

Based on the search results, I can see that there is a consistent color theme pattern across the application:

  1. The search bar uses gray100 for both placeholder text and icons (placeholderTextColor and iconColor)
  2. Most interactive elements like headers, buttons, and content use the text color from the theme
  3. The options icon is now using text color, which is consistent with other primary interactive elements

This shows that the current implementation follows a deliberate visual hierarchy where:

  • Primary interactive elements use the text color
  • Secondary/placeholder elements use gray100

The current implementation maintains proper visual hierarchy and is consistent with the app's design system

The search bar's use of gray100 for icons and placeholder appropriately indicates its input state, while the options icon's use of text color properly emphasizes it as a primary interactive element.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent color usage across components
# Look for other components using similar search/filter patterns

# Search for Searchbar components with color properties
rg -l "Searchbar.*Color" | xargs rg "Color.*gray100|Color.*text" -A 2 -B 2

# Look for other option icons in the codebase
ast-grep --pattern 'Ionicons name="options-outline"'

Length of output: 42659

components/ui/carousel/render-media-item.tsx (2)

86-86: LGTM! Improved error logging

The change from console.log to console.error with a descriptive prefix is more appropriate for error conditions.


85-87: 🛠️ Refactor suggestion

Consider implementing comprehensive error handling

While the error logging improvement is good, consider implementing a more robust error handling strategy:

  1. Notify the user when video loading fails
  2. Provide a fallback UI
  3. Track errors for monitoring
     shouldPlay={false}
     useNativeControls
-    onError={(error) => console.error("Video Error:", error)}
+    onError={(error) => {
+      console.error("Video Error:", error);
+      // Track error for monitoring
+      trackError('video_playback_error', error);
+      // Notify user
+      showErrorToast('Failed to load video. Please try again later.');
+    }}
+    // Provide fallback when video fails
+    onLoadStart={() => setIsLoading(true)}
+    onLoad={() => setIsLoading(false)}

Let's check if error tracking and toast notifications are available in the codebase:

✅ Verification successful

Let me search for more specific information about the error handling and notification patterns used in the codebase.


Let me check for any existing error handling patterns and toast notifications in the UI components.


Consider a simpler error handling approach

The codebase shows that both Toast from "react-native-toast-message" and a custom Toaster component are used for notifications. However, adding loading states and error tracking would introduce unnecessary complexity to this media rendering component. A simpler approach focusing on user feedback would be more appropriate:

     shouldPlay={false}
     useNativeControls
-    onError={(error) => console.error("Video Error:", error)}
+    onError={(error) => {
+      console.error("Video Error:", error);
+      Toast.show({
+        type: 'error',
+        text1: 'Unable to play video',
+        text2: 'Please try again later'
+      });
+    }}
  • The component should maintain its focused responsibility of rendering media
  • Error tracking can be added at a higher level if needed
  • The existing Toast system is sufficient for user feedback
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for error tracking utility
rg -l "trackError|errorTracker|reportError"

# Search for toast/notification components
rg -l "Toast|showToast|Notification|showNotification"

Length of output: 1033


Script:

#!/bin/bash
# Check notification context implementation
cat contexts/notification-context.provider.tsx

# Look for existing video error handling patterns
rg -g "*.tsx" -g "*.ts" "Video.*Error" -A 3

# Check for loading state patterns in video components
rg -g "*.tsx" -g "*.ts" "setIsLoading.*video" -B 2 -A 2

Length of output: 4793


Script:

#!/bin/bash
# Look for toast/error UI patterns in components
rg -g "*.tsx" "toast|alert|error.*message" -B 2 -A 2

# Check for loading state implementations in the carousel component
cat components/ui/carousel/render-media-item.tsx

Length of output: 11106

components/channel/channel-list.tsx (1)

61-61: LGTM! Theme-aware placeholder color implementation.

The addition of placeholderTextColor properly utilizes the theme system and maintains consistency with similar components.

Let's verify the consistent usage of placeholder text colors across similar searchbar components:

✅ Verification successful

Theme consistency verified across all Searchbar components

All three Searchbar implementations (SearchComponent.tsx, DrawerMenuContent.tsx, and channel-list.tsx) consistently use:

  • The same theme-aware placeholder color: Colors(theme).gray100
  • Matching background color: Colors(theme).platinum
  • Proper theme context usage via useTheme hook
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent placeholder text color implementation across searchbar components
# Expected: All Searchbar components should use theme-aware placeholder colors

# Search for Searchbar components and their placeholder color implementations
rg -U "(?s)<Searchbar.*?>" --type tsx

Length of output: 67


Script:

#!/bin/bash
# Search for Searchbar components and their placeholder color implementations
# Using more generic file extensions for TypeScript/React files
rg -U "(?s)<Searchbar.*?>" -t ts -t tsx -t jsx

# Also search for potential file name variations
fd -e ts -e tsx -e jsx | grep -i "search"

Length of output: 149


Script:

#!/bin/bash
# Let's examine the SearchComponent.tsx content and any other search-related components
cat components/SearchComponent.tsx

# Also try searching with ripgrep without file type specifications
rg -U "(?s)<Searchbar.*?>"

# Search for placeholder color implementations in general
rg "placeholderTextColor"

Length of output: 4563

components/drawer-layout/DrawerMenuContent.tsx (2)

103-103: LGTM! Theme-consistent placeholder color.

The addition of placeholderTextColor using Colors(theme).gray100 aligns with the theme update objectives and maintains visual consistency.


Line range hint 103-109: Verify theme consistency across search components

Since the AI summary mentions similar changes in components/channel/channel-list.tsx, let's verify theme consistency across search components.

✅ Verification successful

Theme implementation is consistent across search components

The search components across the codebase show consistent theme implementation:

  • All search components (DrawerMenuContent.tsx, channel-list.tsx, and SearchComponent.tsx) use:
    • Colors(theme).gray100 for placeholder text color
    • Colors(theme).platinum for background color
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent theme implementation in Searchbar components
# Expected: All Searchbar components should use Colors(theme) for styling

# Search for Searchbar components and their style properties
rg -U "Searchbar.*?>" --type tsx

# Search for other components using Colors(theme).gray100
ast-grep --pattern 'Colors($theme).gray100'

Length of output: 108


Script:

#!/bin/bash
# Search for Searchbar components and their style properties with proper file extension
rg -U "Searchbar.*?>" -t tsx -t ts -t jsx -t js

# Search for color-related styling in components
rg -U "placeholderTextColor.*?Colors\(theme\)" -t tsx -t ts -t jsx -t js

# Search for platinum background color usage
rg -U "backgroundColor.*?Colors\(theme\)\.platinum" -t tsx -t ts -t jsx -t js

# Search for gray100 color usage
rg -U "Colors\(theme\)\.gray100" -t tsx -t ts -t jsx -t js

Length of output: 377


Script:

#!/bin/bash
# Search for files first
fd -e tsx -e ts -e jsx -e js

# Then search for Searchbar components and their style properties
rg -U "Searchbar.*?>"

# Search for color-related styling in components
rg -U "placeholderTextColor.*?Colors"

# Search for platinum background color usage
rg -U "backgroundColor.*?platinum"

# Search for gray100 color usage
rg -U "Colors.*?gray100"

Length of output: 6825

components/InfluencerCard.tsx (1)

143-143: LGTM: Consistent theme color updates

The color property updates align with the theme update objectives and maintain consistency across the component.

Also applies to: 153-153, 161-161

components/proposals/Invitations.tsx (2)

133-133: LGTM! Proper layout fix

The addition of flex: 1 ensures the container properly fills its parent space, improving the overall layout.


142-142: LGTM! Proper empty state positioning

The addition of flex: 1 ensures the empty state content is properly centered vertically within its container.

components/proposals/Applications.tsx (1)

181-189: LGTM! Button changes align with the theme update.

The changes to the button's navigation route and label text maintain consistency with the Invitations component while preserving the core functionality.

Let's verify the consistency of this route across the codebase:

✅ Verification successful

Route pattern is consistently used across the codebase

The verification confirms that the new route pattern "/(modal)/create-collaboration" is used consistently across all relevant components and layouts:

  • components/proposals/Invitations.tsx
  • components/proposals/Applications.tsx
  • app/_layout.tsx
  • app/(main)/(drawer)/(tabs)/create-collaboration.tsx
  • app/(main)/(drawer)/(tabs)/_layout.tsx

No instances of old route patterns were found, indicating a complete and consistent update.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage of the new route pattern across components
# Expected: All navigation to create collaboration should use the new route pattern

# Search for any remaining old routes
rg -i '/modal.*collab'

# Search for the new route pattern to ensure consistency
rg -i '\(modal\)/create-collaboration'

Length of output: 547

components/FilterModal.tsx (1)

Line range hint 28-28: LGTM: Clean implementation of BottomSheet visibility control

The addition of the isVisible prop and its implementation with the BottomSheet's index prop is well-structured and follows best practices.

Also applies to: 42-42, 76-76

Comment thread constants/Colors.ts
Comment on lines +16 to +17
primary: "#538BA6",
onSurface: "#538BA6",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Review dark theme color contrast for accessibility

Setting both primary and onSurface to the same color (#538BA6) may cause readability issues in dark theme. The onSurface color should typically have higher contrast against surface colors for better accessibility.

Consider adjusting the onSurface color to a lighter shade for better contrast in dark mode. For example:

  primary: "#538BA6",
- onSurface: "#538BA6",
+ onSurface: "#E1E3E4",
📝 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
primary: "#538BA6",
onSurface: "#538BA6",
primary: "#538BA6",
onSurface: "#E1E3E4",

setInfluencers(influencers);
} catch (error) {
console.log(error);
console.error(error);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance error handling with user feedback

While using console.error is appropriate, the error handling could be improved to provide a better user experience.

Consider implementing this more robust error handling:

     } catch (error) {
-      console.error(error);
+      console.error('Failed to fetch applications:', error);
+      Toast.show({
+        type: 'error',
+        text1: 'Failed to load applications',
+        text2: 'Please try again later'
+      });
+      setInfluencers([]);  // Reset to known state
     } finally {

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

Comment thread components/proposals/Invitations.tsx Outdated
Comment on lines +168 to +176
onPress={() => router.push("/(modal)/create-collaboration")}
style={{
backgroundColor: Colors(theme).platinum,
padding: 5,
borderRadius: 5,
}}
textColor={Colors(theme).text}
>
New Collaborations
New Collaboration

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codebase verification

Route is valid but text needs consistency updates

The route "/(modal)/create-collaboration" is properly configured and used consistently across the codebase. However, there's an inconsistency in terminology:

  • Button text uses singular form: "New Collaboration"
  • Empty state message uses plural form: "New Collaborations page"

Please update the empty state messages in both files to use the singular form for consistency:

  • components/proposals/Invitations.tsx
  • components/proposals/Applications.tsx
🔗 Analysis chain

Verify the new route and button text consistency

The route change and button text update look good, but please ensure:

  1. The new route "/(modal)/create-collaboration" is properly configured and handled
  2. Similar buttons across the app use consistent terminology ("New Collaboration" vs "New Collaborations")

The search results show that the route "/(modal)/create-collaboration" is consistently used across multiple files and the button text "New Collaboration" is consistently used in both Applications.tsx and Invitations.tsx. However, there's a potential inconsistency in the descriptive text that uses "New Collaborations" (plural). Let me verify the exact context of these occurrences.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other instances of collaboration creation buttons and routes
echo "Checking for route usage patterns:"
rg -g "*.{tsx,ts}" "router\.push.*modal.*collaboration"

echo "\nChecking for button text consistency:"
rg -g "*.{tsx,ts}" "New Collaboration[s]?"

Length of output: 1038


Script:

#!/bin/bash
# Get more context around the "New Collaborations" text
echo "Checking context of New Collaborations mentions:"
rg -g "*.{tsx,ts}" -B 2 -A 2 "Go to the New Collaborations page"

Length of output: 1015

setAlreadyInvited(alreadyInvited);
} catch (error) {
console.log(error);
console.error(error);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance error handling with user feedback and error recovery

While changing to console.error is good, the error handling could be more robust:

  1. Add specific error types and handling
  2. Provide user feedback via Toaster
  3. Ensure consistent state on error
 } catch (error) {
-  console.error(error);
+  console.error('Failed to fetch invitations:', error);
+  Toaster.error('Unable to load invitations. Please try again.');
+  setInfluencers([]); // Reset to consistent state
 } finally {

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Outside diff range and nitpick comments (10)
styles/empty-state/EmptyState.styles.tsx (1)

25-25: Maintain consistent quote style

The file uses double quotes elsewhere, but these lines use single quotes. Consider maintaining consistency:

-    textAlign: 'center'
+    textAlign: "center"

Also applies to: 29-29

components/contracts/index.tsx (1)

1-4: Consider improving import organization and type safety.

  1. Consider using absolute imports for better maintainability
  2. Add TypeScript types for the component props
-import AppLayout from "@/layouts/app-layout";
-import { Text, View } from "../theme/Themed";
-import EmptyState from "../ui/empty-state";
-import { useRouter } from "expo-router";
+import { useRouter } from "expo-router";
+import { Text, View } from "@/components/theme/Themed";
+import AppLayout from "@/layouts/app-layout";
+import EmptyState from "@/components/ui/empty-state";
+
+interface ContractsProps {
+  // Add any props if needed
+}
components/collaborations/index.tsx (1)

Line range hint 24-31: Consider moving inline styles to stylesFn for better theme maintainability.

The tab container uses inline styles while the rest of the component uses stylesFn. For better maintainability and theme consistency, consider moving these styles to the stylesFn.

// In stylesFn
+ tabContainer: {
+   display: "flex",
+   flexDirection: "row",
+   justifyContent: "space-around",
+   width: "100%",
+   marginBottom: 20,
+ },

// In component
- <View
-   style={{
-     display: "flex",
-     flexDirection: "row",
-     justifyContent: "space-around",
-     width: "100%",
-     marginBottom: 20,
-   }}
- >
+ <View style={styles.tabContainer}>
components/ui/empty-state/index.tsx (4)

14-15: Consider using proper TypeScript types for better type safety.

The style prop uses any type which bypasses TypeScript's type checking. Consider using proper React Native style types:

-  style?: any;
+  style?: StyleProp<ViewStyle>;

Don't forget to import:

import { StyleProp, ViewStyle } from 'react-native';

34-40: Consider adding error handling for the action callback.

The action handler should catch potential errors to prevent crashes:

   const handleAction = () => {
     if (action) {
-      action();
+      try {
+        action();
+      } catch (error) {
+        console.error('Error executing empty state action:', error);
+        navigation.goBack();
+      }
     } else {
       navigation.goBack();
     }
   };

51-56: Consider making image dimensions responsive.

The hardcoded width and height values might not scale well across different screen sizes.

-            width={256}
-            height={256}
+            style={[
+              styles.image,
+              {
+                width: '80%',
+                aspectRatio: 1,
+                maxWidth: 256,
+              },
+            ]}
-            style={styles.image}

Line range hint 1-83: Consider separating navigation concerns from the UI component.

The EmptyState component currently handles both UI rendering and navigation logic. Consider extracting the navigation logic to the parent component for better separation of concerns:

  1. Make the component purely presentational by removing navigation dependencies
  2. Always require an action prop from the parent
  3. Remove the default "Go back" behavior

This would make the component more reusable and easier to test.

Example usage in parent:

const ParentComponent = () => {
  const navigation = useNavigation();
  return (
    <EmptyState
      action={() => navigation.goBack()}
      actionLabel="Go back"
      title="No items found"
    />
  );
};
components/notifications/index.tsx (1)

44-48: Consider improving type safety for notification data.

The data object mapping could benefit from stricter typing to prevent potential runtime errors.

Consider creating a dedicated type for the notification data:

interface NotificationData {
  collaborationId?: string;
  groupId?: string;
  userId?: string;
}

Then update the props interface:

interface NotificationCardProps {
  data: NotificationData;
  // ... other props
}
components/proposals/Invitations.tsx (2)

134-139: Fix typo in EmptyState subtitle text

The subtitle text contains a typo and could be improved for clarity:

  • "woudl" should be "would"
  • The sentence structure could be more polished
-          subtitle="Start building your profile today to have better reach. If any brand invites you to collaborate we woudl show it here"
+          subtitle="Start building your profile today to have better reach. When brands invite you to collaborate, they will appear here"

Line range hint 45-93: Consider improving error handling and query performance

The fetchProposals function has several areas that could be improved:

  1. Error handling only logs to console without user feedback
  2. Multiple collection queries could impact performance
  3. No loading states for secondary data fetching operations

Consider these improvements:

  1. Add user-facing error feedback
  2. Batch the queries or use a composite query
  3. Add loading states for secondary operations

Example implementation:

const fetchProposals = async () => {
  try {
    setSecondaryLoading(true); // Add loading state for secondary operations
    const collaborationCol = collection(FirestoreDB, "collaborations");
    const q = query(
      collaborationCol,
      where("brandId", "==", selectedBrand?.id)
    );
    
    // Batch get all related collections
    const [querySnapshot, applicationsSnapshot, invitationsSnapshot] = await Promise.all([
      getDocs(q),
      // Add other collection queries here
    ]);

    // Process the data...
    
  } catch (error) {
    console.error("Error fetching proposals: ", error);
    // Add user-facing error feedback
    Toast.show({
      type: 'error',
      text1: 'Error loading invitations',
      text2: 'Please try again later'
    });
  } finally {
    setIsLoading(false);
    setSecondaryLoading(false);
  }
};
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between c97ee30 and 98bcc4b.

⛔ Files ignored due to path filters (6)
  • assets/images/illustration1.png is excluded by !**/*.png
  • assets/images/illustration2.png is excluded by !**/*.png
  • assets/images/illustration3.png is excluded by !**/*.png
  • assets/images/illustration4.png is excluded by !**/*.png
  • assets/images/illustration5.png is excluded by !**/*.png
  • assets/images/illustration6.png is excluded by !**/*.png
📒 Files selected for processing (9)
  • components/collaborations/index.tsx (1 hunks)
  • components/contracts/index.tsx (1 hunks)
  • components/notifications/index.tsx (2 hunks)
  • components/proposals/Applications.tsx (2 hunks)
  • components/proposals/Invitations.tsx (2 hunks)
  • components/ui/empty-state/index.tsx (2 hunks)
  • styles/NotificationCard.styles.tsx (1 hunks)
  • styles/empty-state/EmptyState.styles.tsx (2 hunks)
  • utils/url.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/proposals/Applications.tsx
🔇 Additional comments (15)
utils/url.ts (1)

9-9: Verify placeholder image existence

Ensure the placeholder image exists at the specified path.

✅ Verification successful

Placeholder image exists at the specified path

The placeholder image placeholder-image.jpg is present at the expected location assets/images/, confirming the import path is valid.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if the placeholder image exists
fd -t f "placeholder-image.jpg" assets/images/

Length of output: 82

styles/empty-state/EmptyState.styles.tsx (2)

9-9: Consider using design system spacing tokens

While adding gap spacing improves layout consistency, consider using design system spacing tokens instead of hardcoded values to maintain consistency across the application.

Let's check if there are existing spacing tokens in the codebase:

#!/bin/bash
# Search for spacing/theme token definitions
rg -g '*.{ts,tsx,js,jsx}' -l "(spacing|theme).*(token|constant)"

Line range hint 1-34: Verify removal of action style

The AI summary indicates that the action style was removed. Let's verify that this doesn't break any existing usage.

✅ Verification successful

Removal of action style is safe

The verification confirms that the removal of the action style from EmptyState.styles.tsx is safe because:

  1. The EmptyState component uses a standard Button from react-native-paper without any custom action styles
  2. The styles.action reference found in NotificationCard.tsx is unrelated as it belongs to a different component with its own styles
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for references to EmptyState action style
rg -g '*.{ts,tsx,js,jsx}' "EmptyState.*action" -A 2

Length of output: 66


Script:

#!/bin/bash
# Search for any usage of styles.action or .action in relation to EmptyState
rg -g '*.{ts,tsx,js,jsx}' 'styles\.action|\.action.*EmptyState'

# Also search for any file that imports and uses EmptyState styles
rg -g '*.{ts,tsx,js,jsx}' 'from.*empty-state.*EmptyState\.styles' -A 3

Length of output: 551


Script:

#!/bin/bash
# Let's check the actual usage in the empty state component
cat components/ui/empty-state/index.tsx

# Also check the NotificationCard styles to confirm the action style there is unrelated
cat components/NotificationCard.tsx

Length of output: 4727

styles/NotificationCard.styles.tsx (3)

8-9: LGTM! Improved spacing consistency

The padding increase to 16 and addition of gap: 8 follow better spacing patterns and create a more consistent visual hierarchy.


13-13: LGTM! Consistent padding with container

The padding increase to 16 maintains consistency with the container's spacing pattern. The removal of marginVertical works well with the container's new gap property.


21-21: LGTM! Aligned horizontal spacing

The marginLeft increase to 16 creates consistent horizontal spacing and properly aligns with the container's padding.

components/contracts/index.tsx (1)

15-15: Verify the existence and accessibility of the collaborations route.

Ensure that the "/collaborations" route exists and is properly configured in the router.

✅ Verification successful

Let me verify the content of the collaborations route file to ensure it's properly configured.


Let me check the Collaborations component to complete the verification.


The collaborations route is properly configured and accessible

The verification confirms that:

  • The route exists at app/(main)/(drawer)/(tabs)/collaborations.tsx
  • It correctly renders the Collaborations component
  • The Collaborations component is fully implemented with a tab-based interface for Applications and Invitations
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for the existence of the collaborations route
echo "Checking for collaborations route..."
fd -t f "collaborations.(tsx|js|ts)" app/

Length of output: 172


Script:

#!/bin/bash
# Check the content of the collaborations route file
cat "app/(main)/(drawer)/(tabs)/collaborations.tsx"

Length of output: 219


Script:

#!/bin/bash
# Check the Collaborations component implementation
cat "components/collaborations/index.tsx"

Length of output: 1823

components/collaborations/index.tsx (2)

3-3: LGTM! Clean import optimization.

The removal of the unused useIsFocused hook while retaining the necessary useTheme hook improves code cleanliness.


Line range hint 1-56: Verify theme consistency across related components.

The component properly implements theming using useTheme and theme-aware components. However, let's verify theme consistency across related components.

✅ Verification successful

Let me gather more information about the style implementations in the related components to ensure theme consistency.


Theme implementation is consistent across related components

The verification confirms that both child components (Applications and Invitations) follow the same theming pattern as the parent:

  • They import and use theme-aware components from @/components/theme/Themed
  • They properly use useTheme from @react-navigation/native
  • They consistently import and apply stylesFn from @/styles/Proposal.styles
  • The style implementation pattern is identical in both components
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify consistent theme implementation across related components

# Check for consistent theme-aware component usage
echo "Checking theme-aware component usage..."
rg -l "import.*from.*@/components/theme/Themed" components/proposals/

# Verify consistent theme hook usage
echo "Checking theme hook usage..."
rg -l "useTheme.*from.*@react-navigation/native" components/proposals/

# Look for potential theme-related style inconsistencies
echo "Checking style implementations..."
ast-grep --pattern 'stylesFn\($_\)'

Length of output: 565


Script:

#!/bin/bash
# Check the actual implementation of theme-related styles in Applications and Invitations
echo "Checking Applications.tsx implementation..."
rg "const styles = " -A 2 -B 2 components/proposals/Applications.tsx

echo "\nChecking Invitations.tsx implementation..."
rg "const styles = " -A 2 -B 2 components/proposals/Invitations.tsx

# Check if they're using the same stylesFn from Proposal.styles
echo "\nChecking stylesFn imports..."
rg "import.*stylesFn.*from.*Proposal\.styles" components/proposals/

Length of output: 858

components/ui/empty-state/index.tsx (2)

21-29: LGTM!

Props destructuring is clean with appropriate default values.


52-52: Verify the imageUrl utility handling.

Ensure that the imageUrl utility properly handles both string URLs and required image assets.

✅ Verification successful

The imageUrl utility correctly handles all image source types

The imageUrl utility in utils/url.ts properly handles:

  • HTTP/HTTPS URLs by returning an object with uri property
  • Required image assets by returning them directly
  • Undefined/null cases by falling back to a placeholder image

The implementation aligns with its usage in the empty state component.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check imageUrl utility implementation
rg -A 10 "export.*imageUrl" utils/

Length of output: 477

components/notifications/index.tsx (3)

7-7: LGTM! Good use of themed component.

Using the themed View component ensures consistent styling across light/dark modes.


27-62: LGTM! Clean conditional rendering structure.

The fragment wrapper and conditional rendering structure is well-organized and follows React best practices.


30-37: Verify the hideAction behavior and image asset.

A few points to consider:

  1. The hideAction prop needs verification as it might affect user interaction capabilities
  2. Ensure the image asset exists at the specified path
✅ Verification successful

Let me verify the EmptyState component implementation to understand how the hideAction prop is used.


Both the hideAction prop and image asset are properly implemented

The verification confirms:

  1. The image asset illustration2.png exists at the correct path assets/images/
  2. The hideAction prop is properly typed in the EmptyState component and controls the visibility of an action button, making it a valid use case for the notifications empty state
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the image asset exists
fd "illustration2.png" assets/images

# Check EmptyState component for hideAction prop usage
ast-grep --pattern 'interface $_Props {
  $$$
  hideAction?: $_
  $$$
}'

Length of output: 143


Script:

#!/bin/bash
# Check EmptyState component implementation
ast-grep --pattern 'const EmptyState = ({ $$ }) => {
  $$$
}'

# Also check for class implementation
ast-grep --pattern 'class EmptyState extends $$ {
  $$$
}'

# Search for hideAction usage in EmptyState
rg "hideAction" -A 5

Length of output: 2116

components/proposals/Invitations.tsx (1)

130-130: Verify layout behavior across different screen sizes

The addition of flex: 1 ensures the container takes up available space, but please verify that this change doesn't cause any layout issues on different screen sizes and orientations.

✅ Verification successful

The flex: 1 style addition is appropriate and consistent with the codebase

The addition of flex: 1 is safe and follows the established pattern in the codebase:

  • It's used within a tab-based layout where components need to fill available space
  • Similar implementation exists in the sibling Applications component
  • The parent component uses a standard View-based layout structure without ScrollView conflicts
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for similar flex container implementations
echo "Checking for similar container implementations:"
rg -g "*.{tsx,ts}" "style=\{.*flex:\s*1.*\}"

echo "\nChecking for potential ScrollView or container components that might be affected:"
rg -g "*.{tsx,ts}" -B 2 -A 2 "style=\{.*flex:\s*1.*\}" "components/"

Length of output: 847


Script:

#!/bin/bash
# Let's check the parent components and layout structure
echo "Checking parent components in Invitations.tsx:"
rg -g "Invitations.tsx" -B 5 -A 5 "style=\{.*flex:\s*1.*\}"

echo "\nChecking if Invitations component is used within ScrollView or similar containers:"
ast-grep --pattern 'import { $_ } from $path' components/proposals/Invitations.tsx

echo "\nChecking usage of Invitations component:"
rg -g "*.{tsx,ts}" "Invitations"

Length of output: 1221


Script:

#!/bin/bash
# Let's check the layout structure of the parent component where Invitations is used
echo "Checking layout structure in collaborations/index.tsx:"
rg -g "index.tsx" -B 10 -A 10 "selectedTab === \"invitations\"" "components/collaborations/"

# Check if there are any ScrollView or container components that might affect the layout
echo "\nChecking for ScrollView or container components in the parent:"
ast-grep --pattern 'import { $_, ScrollView, $_$ } from "react-native"' components/collaborations/index.tsx

Length of output: 2176

Comment thread utils/url.ts
Comment on lines +1 to +11
export const imageUrl = (image: string | NodeRequire | undefined) => {
if (image && typeof image === "string" && image.includes("http")) {
return {
uri: image,
};
} else if (image) {
return image;
} else {
return require("@/assets/images/placeholder-image.jpg");
}
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance URL validation and handling

Consider improving the URL handling logic:

  1. Use a more robust URL validation:
-if (image && typeof image === "string" && image.includes("http")) {
+if (image && typeof image === "string" && 
+    (image.startsWith("http://") || image.startsWith("https://") || image.startsWith("data:"))) {
  1. Consider handling relative URLs:
// Add this helper at the top of the file
const isValidUrl = (url: string): boolean => {
  try {
    if (url.startsWith('/')) return true;
    new URL(url);
    return true;
  } catch {
    return false;
  }
};
  1. Consider caching the placeholder image require:
const PLACEHOLDER_IMAGE = require("@/assets/images/placeholder-image.jpg");

⚠️ Potential issue

Add return type annotation and improve type safety

The function needs better type safety and validation:

-export const imageUrl = (image: string | NodeRequire | undefined) => {
+export const imageUrl = (
+  image: string | NodeRequire | undefined
+): { uri: string } | NodeRequire => {
   if (image && typeof image === "string" && image.includes("http")) {
+    try {
+      new URL(image); // Validate URL format
       return {
         uri: image,
       };
+    } catch {
+      return require("@/assets/images/placeholder-image.jpg");
+    }
   } else if (image) {
     return image;
   } else {
     return require("@/assets/images/placeholder-image.jpg");
   }
 };
📝 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
export const imageUrl = (image: string | NodeRequire | undefined) => {
if (image && typeof image === "string" && image.includes("http")) {
return {
uri: image,
};
} else if (image) {
return image;
} else {
return require("@/assets/images/placeholder-image.jpg");
}
};
export const imageUrl = (
image: string | NodeRequire | undefined
): { uri: string } | NodeRequire => {
if (image && typeof image === "string" && image.includes("http")) {
try {
new URL(image); // Validate URL format
return {
uri: image,
};
} catch {
return require("@/assets/images/placeholder-image.jpg");
}
} else if (image) {
return image;
} else {
return require("@/assets/images/placeholder-image.jpg");
}
};

Comment on lines +19 to +20
maxWidth: 256,
maxHeight: 256,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using responsive dimensions

Fixed pixel values for image dimensions might not scale well across different screen sizes. Consider using responsive units or percentage-based dimensions.

Example improvement:

  image: {
    borderRadius: 10,
-   maxWidth: 256,
-   maxHeight: 256,
+   maxWidth: '80%',
+   maxHeight: '30%',
  },
📝 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
maxWidth: 256,
maxHeight: 256,
maxWidth: '80%',
maxHeight: '30%',

Comment on lines +8 to +21
padding: 16,
gap: 8,
backgroundColor: Colors(theme).background,
},
card: {
marginVertical: 8,
padding: 10,
padding: 16,
backgroundColor: Colors(theme).card,
},
row: {
flexDirection: "row",
alignItems: "center",
},
content: {
marginLeft: 10,
marginLeft: 16,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codebase verification

Spacing values need standardization across the codebase

The analysis reveals inconsistent spacing patterns across the application's style files:

  • Multiple different padding values: 4, 5, 6, 10, 12, 14, 15, 16, 20, 24, 25
  • Various margin values: 4, 5, 8, 10, 15, 16, 20, 24, 30, 40
  • Inconsistent gap values: 8, 10, 12, 14, 20

While the changes in NotificationCard (padding: 16, gap: 8) are reasonable, they don't align with the most commonly used values in the codebase:

  • Most common padding: 20px (13 occurrences)
  • Most common margin: 20px (22 occurrences)
  • Most common gap: 10px (10 occurrences)

Consider:

  • Creating a spacing scale using design tokens
  • Documenting the spacing system in the style guide
  • Gradually aligning all components to use consistent spacing values
🔗 Analysis chain

Verify spacing consistency across components

The new spacing values (16 for padding/margins, 8 for gaps) create a better visual hierarchy. Let's verify if these values are consistently used across other components.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent spacing values across style files

# Search for different padding/margin values in style files
echo "Checking padding/margin values in style files:"
rg -g '*.styles.tsx' -g '*.styles.ts' "(padding|margin).*:\s*\d+" --no-filename | sort | uniq -c

# Search for gap values
echo -e "\nChecking gap values in style files:"
rg -g '*.styles.tsx' -g '*.styles.ts' "gap.*:\s*\d+" --no-filename | sort | uniq -c

Length of output: 2264

Comment on lines +7 to +8
const contracts = [];
const router = useRouter();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add proper state management for contracts data.

The current implementation uses a static empty array which won't reflect data changes. Consider using React's useState or a state management solution.

-const contracts = [];
+const [contracts, setContracts] = useState<Contract[]>([]);
+const [isLoading, setIsLoading] = useState(true);
+
+useEffect(() => {
+  const fetchContracts = async () => {
+    try {
+      setIsLoading(true);
+      // Add your fetch logic here
+    } catch (error) {
+      console.error('Failed to fetch contracts:', error);
+    } finally {
+      setIsLoading(false);
+    }
+  };
+
+  fetchContracts();
+}, []);

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

Comment on lines +11 to +27
<AppLayout>
{
contracts.length === 0 ? (
<EmptyState
action={() => router.push("/collaborations")}
actionLabel="Start Applying"
image={require("@/assets/images/illustration4.png")}
subtitle="Opps! No Contracts has been created yet. Start applying to collaboration to get contracts."
title="No Contracts"
/>
) : (
<View>
<Text>Contracts</Text>
</View>
)
}
</AppLayout>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance UI handling and accessibility.

Several improvements can be made to the render logic:

  1. Add loading state handling
  2. Add error handling for navigation
  3. Improve accessibility
  4. Externalize strings for internationalization
 return (
   <AppLayout>
+    {isLoading ? (
+      <LoadingSpinner />
+    ) : (
     {
       contracts.length === 0 ? (
         <EmptyState
-          action={() => router.push("/collaborations")}
+          action={async () => {
+            try {
+              await router.push("/collaborations");
+            } catch (error) {
+              console.error('Navigation failed:', error);
+              // Add error handling UI
+            }
+          }}
-          actionLabel="Start Applying"
+          actionLabel={t('contracts.empty.actionLabel')}
           image={require("@/assets/images/illustration4.png")}
-          subtitle="Opps! No Contracts has been created yet. Start applying to collaboration to get contracts."
-          title="No Contracts"
+          subtitle={t('contracts.empty.subtitle')}
+          title={t('contracts.empty.title')}
+          role="status"
+          aria-live="polite"
         />
       ) : (
         <View>
-          <Text>Contracts</Text>
+          <Text role="heading" aria-level={1}>{t('contracts.title')}</Text>
+          {/* Add your contracts list rendering logic here */}
         </View>
       )}
+    )}
   </AppLayout>
 );

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

{
notifications.map((item) => (
<NotificationCard
avatar="https://cdn.iconscout.com/icon/free/png-256/avatar-370-456322.png"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Replace hardcoded avatar URL with a configurable value.

The avatar URL is hardcoded which could cause issues if the CDN changes or if you want to use different avatars.

Consider:

  1. Moving it to a configuration file
  2. Making it a prop
  3. Using a local asset
-  avatar="https://cdn.iconscout.com/icon/free/png-256/avatar-370-456322.png"
+  avatar={notification.avatar || defaultAvatarUrl}

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (3)
components/ui/modal/DocumentUploadModal.tsx (2)

Line range hint 31-45: Add error handling for failed uploads

While the media type handling is good, consider adding try-catch blocks to handle potential upload failures gracefully.

 const openGallery = async () => {
+  try {
     const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
     if (status !== 'granted') {
-      alert('Media library access is required to select images, videos, or documents');
+      console.error('Media library permission denied');
+      alert('Please enable media library access in your device settings to continue');
       return;
     }

     const result = await ImagePicker.launchImageLibraryAsync({
       mediaTypes: ImagePicker.MediaTypeOptions.All,
       allowsEditing: true,
       aspect: [4, 3],
       quality: 1,
     });

     if (!result.canceled) {
       if (result.assets[0].type === "video") {
         onVideoUpload(result.assets[0].uri);
         setVisible(false);
         return;
       }
       onImageUpload(result.assets[0].uri);
       setVisible(false);
     }
+  } catch (error) {
+    console.error('Failed to process media:', error);
+    alert('Failed to process media. Please try again.');
+  }
 }

92-96: Consider adding loading state to prevent multiple submissions

While the Button implementation is good, consider adding a loading state to prevent multiple submissions during upload operations.

+const [isLoading, setIsLoading] = useState(false);

 <Button
   mode="contained"
+  loading={isLoading}
+  disabled={isLoading}
   onPress={() => setVisible(false)}
 >
   Cancel
 </Button>
components/ui/modal/ImageUploadModal.tsx (1)

Line range hint 1-115: Consider adding robust error handling and loading states

The component could benefit from additional improvements:

  1. Add loading states during image selection and processing
  2. Implement error handling for failed uploads
  3. Add image type and size validation
  4. Consider adding accessibility labels for better screen reader support

Would you like me to provide a code example implementing these improvements?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 98bcc4b and a0c31dc.

📒 Files selected for processing (2)
  • components/ui/modal/DocumentUploadModal.tsx (2 hunks)
  • components/ui/modal/ImageUploadModal.tsx (2 hunks)
🔇 Additional comments (3)
components/ui/modal/DocumentUploadModal.tsx (2)

8-8: LGTM: Good choice using react-native-paper Button

The switch to react-native-paper's Button component is a good choice as it provides better theming capabilities and maintains consistency with the Material Design system.


92-96: Verify theme consistency across the application

Since this is part of a theme update, ensure the Button styling is consistent with other components using react-native-paper.

✅ Verification successful

Theme consistency verified for Button component

The Button usage in DocumentUploadModal is consistent with other components in the codebase:

  • All Button components from react-native-paper use the mode="contained" prop consistently across multiple files (FilterModal, create-collaboration)
  • No custom theme overrides or inconsistent styling patterns were found
  • The component properly uses the theme context as other components do
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other Button usages to verify consistent styling
rg -l "from 'react-native-paper'" | xargs rg "Button.*mode=\"contained\""

# Check for any remaining react-native Button imports that should be migrated
rg "import.*Button.*from 'react-native'"

Length of output: 617


Script:

#!/bin/bash
# Search for Button imports from react-native-paper to understand styling patterns
rg "import.*Button.*from 'react-native-paper'"

# Search for useTheme usage with Button components
rg -A 3 "useTheme" | grep -A 3 "Button"

# Look for theme-related props on Buttons
rg "Button.*theme="

# Search for style props on Buttons to check consistency
rg "Button.*style="

Length of output: 2907

components/ui/modal/ImageUploadModal.tsx (1)

1-1: LGTM: Import changes align with UI library migration

The import changes correctly reflect the migration to react-native-paper's Button component while maintaining the necessary imports for existing functionality.

Also applies to: 4-4

Comment on lines +103 to +107
mode="contained"
onPress={removeImage}
/>
>
Cancel
</Button>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider improving button clarity and behavior

The current implementation has several UX concerns:

  1. Using a contained button style for a destructive/cancel action might not follow common UI patterns
  2. The button label "Cancel" doesn't clearly convey that it will remove the image
  3. The action combines two different intents: closing the modal and removing the image

Consider these improvements:

 <Button
-  mode="contained"
+  mode="text"
   onPress={removeImage}
+  textColor={theme.colors.error}
 >
-  Cancel
+  Remove Image
 </Button>

Also, consider separating the cancel and remove actions:

<View style={styles.buttonContainer}>
  <Button mode="text" onPress={() => setVisible(false)}>
    Cancel
  </Button>
  <Button 
    mode="text" 
    textColor={theme.colors.error}
    onPress={removeImage}
  >
    Remove Image
  </Button>
</View>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 15

🧹 Outside diff range and nitpick comments (23)
utils/conversion.ts (1)

1-6: The basic implementation looks good but could be enhanced.

The function successfully handles the primary use case of converting numbers ≥ 1000 to a k-format.

Consider these improvements:

-export const convertToKUnits = (num: number) => {
+export const convertToKUnits = (num: number): string => {
+  if (!Number.isFinite(num)) return '0';
+  if (num < 0) return `-${convertToKUnits(Math.abs(num))}`;
+  if (num >= 1_000_000) return `${(num / 1_000_000).toFixed(1)}M`;
   if (num >= 1000) {
     return `${(num / 1000).toFixed(1)}k`;
   }
-  return num;
+  return num.toString();
 };

Improvements include:

  • Explicit return type annotation
  • Handling of negative numbers
  • Support for millions (M) format
  • Handling of NaN/Infinity
  • Consistent string return type
styles/InfluencerCard.styles.tsx (2)

23-24: Consider accessibility in stat item spacing

The change from column to row layout with a small gap of 2 units might affect touch target sizes. Consider increasing the gap to improve touch accessibility.

   statItem: {
     alignItems: "center",
     flexDirection: "row",
-    gap: 2,
+    gap: 8, // Increased for better touch targets
   },

73-79: Consider consolidating similar style properties

There appears to be some duplication between stats and statsContainer styles, both using flexDirection: "row" and justifyContent: "space-between".

Consider consolidating these styles:

   stats: {
     marginVertical: 10,
-    flexDirection: "row",
-    justifyContent: "space-between",
+    ...statsContainer, // Inherit common properties
   },
   statsContainer: {
     gap: 20,
     flexDirection: "row",
     justifyContent: "space-between",
   },
components/drawer-layout/DrawerMenuItem.tsx (2)

Line range hint 42-54: Remove commented code

The old FontAwesome implementation should be removed rather than left commented out. This helps maintain code cleanliness and prevents confusion.

-        {/* <FontAwesome
-          name={tab.icon as any}
-          color={
-            tab.href.includes(pathname)
-              ? Colors(theme).white
-              : Colors(theme).gray100
-          }
-          style={{
-            width: 32,
-            textAlign: "center",
-          }}
-          size={28}
-        /> */}

55-65: Optimize icon styles

Consider extracting the icon styles to a StyleSheet for better performance and reusability.

+const styles = StyleSheet.create({
+  icon: {
+    width: 32,
+  },
+});

         <FontAwesomeIcon
           icon={tab.icon}
           color={
             tab.href.includes(pathname)
               ? Colors(theme).white
               : Colors(theme).gray100
           }
           size={28}
-          style={{
-            width: 32,
-          }}
+          style={styles.icon}
         />
components/collaboration/collaboration-details/index.tsx (3)

9-18: Consider making brand-related fields optional or adding validation.

The Collaboration interface assumes brand fields will always be present, but the component's implementation shows these could be undefined or have fallback values.

Consider updating the interface to better reflect the actual data structure:

 interface Collaboration extends ICollaboration {
-  brandName: string;
-  paymentVerified: boolean;
-  brandDescription: string;
+  brandName?: string;
+  paymentVerified?: boolean;
+  brandDescription?: string;
 }

51-85: Simplify rendering logic and improve effect dependencies.

The current implementation has unnecessary complexity in the rendering logic and could benefit from some improvements:

Consider these improvements:

 useEffect(() => {
-  fetchCollaboration();
+  const cleanup = fetchCollaboration();
+  return cleanup;
-}, [pageID]);
+}, [pageID, fetchCollaboration]);

 if (loading)
   return (
     <View
       style={{
         flex: 1,
         justifyContent: "center",
         alignItems: "center",
       }}
     >
       <ActivityIndicator />
     </View>
   );

 if (!collaboration) return null;

-return (
-  <>
-    {
-      activeTab === "Overview" && (
-        <CollaborationDetailsContent
-          collaborationDetail={collaboration}
-          pageID={pageID}
-        />
-      )
-    }
-  </>
-);
+return (
+  <CollaborationDetailsContent
+    collaborationDetail={collaboration}
+    pageID={pageID}
+  />
+);

19-83: Consider implementing error boundaries.

The component handles data fetching and display but lacks proper error boundary implementation. This could lead to uncaught runtime errors affecting the entire app.

Consider wrapping the component with an error boundary to gracefully handle runtime errors:

import { ErrorBoundary } from '@/components/ErrorBoundary';

// Usage
<ErrorBoundary fallback={<ErrorFallback />}>
  <CollaborationDetails pageID={pageID} />
</ErrorBoundary>
components/collaboration/CollaborationCard.tsx (1)

Line range hint 30-30: Address component naming inconsistency and remove redundant comment

  1. The component name JobCard doesn't match the file name CollaborationCard.tsx. Consider renaming for consistency.
  2. The comment "show applied, invited and shortlisted chips" is now redundant as it describes implemented functionality.
- const JobCard = (props: CollaborationAdCardProps) => {
+ const CollaborationCard = (props: CollaborationAdCardProps) => {

  // Later in the code...
- {/* 
-   show applied, invited and shortlisted chips
-  */}

Don't forget to update the export statement at the bottom of the file:

- export default JobCard;
+ export default CollaborationCard;

Also applies to: 81-81

components/explore-influencers/index.tsx (3)

Line range hint 22-56: Inconsistency between UI and filtering logic

The influencer type filtering logic has been commented out, but the UI controls for influencer type filtering are still present. This creates a confusing user experience where the filter appears to be available but doesn't actually affect the results.

Consider either:

  1. Removing the influencer type filter UI completely if it's no longer needed, or
  2. Re-enabling the filtering logic if this was unintentional
- // const isInfluencerTypeMatch =
- //   currentInfluencerType === "All" ||
- //   influencer.influencerType === currentInfluencerType;

+ const isInfluencerTypeMatch =
+   currentInfluencerType === "All" ||
+   influencer.influencerType === currentInfluencerType;

  return (
    isCollaborationTypeMatch &&
-   // isInfluencerTypeMatch &&
+   isInfluencerTypeMatch &&
    isFollowersRangeMatch &&
    isReachRangeMatch &&
    isEngagementRangeMatch &&
    isSearchQueryMatch
  );

Line range hint 15-21: Inconsistent default values between state and props

The default values for ranges are defined differently in the initial state versus the CollaborationFilter props:

  • Engagement range in state: [0, 10000000]
  • Engagement range in props: [0, 100000]

This inconsistency could lead to unexpected filtering behavior.

Consider:

  1. Using constants for these values to ensure consistency
  2. Aligning the maximum values between state and props
// Add at the top of the file
const DEFAULT_RANGES = {
  followers: [0, 1000000],
  reach: [0, 1000000],
  engagement: [0, 10000000] // or 100000, decide which is correct
} as const;

// Use in both state and props
const [currentEngagementRange, setCurrentEngagementRange] = useState(DEFAULT_RANGES.engagement);

// Later in JSX
currentEngagementRange={DEFAULT_RANGES.engagement}

Consider extracting the filter default values to a configuration object to improve maintainability:

const FILTER_DEFAULTS = {
  collaborationType: "All",
  influencerType: "All",
  ranges: DEFAULT_RANGES
} as const;

Also applies to: 106-116


Line range hint 22-56: Consider memoizing the filtered results

The filteredInfluencers calculation runs on every render. For better performance, especially with large lists, consider memoizing the results using useMemo.

const filteredInfluencers = useMemo(() => {
  return influencers.filter((influencer) => {
    // ... existing filter logic ...
  });
}, [
  currentCollaborationType,
  currentFollowersRange,
  currentReachRange,
  currentEngagementRange,
  searchQuery
]);
components/drawer-layout/DrawerMenuContent.tsx (1)

Line range hint 156-158: Fix operator precedence in bottom margin calculation.

The current expression has incorrect operator precedence which could lead to unexpected margin values.

-          marginBottom: bottom + Platform.OS === 'android' ? 24 : 44,
+          marginBottom: bottom + (Platform.OS === 'android' ? 24 : 44),
🧰 Tools
🪛 Biome

[error] 25-26: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

app/(main)/(drawer)/(tabs)/_layout.tsx (2)

51-55: Consider standardizing icon sizes

The explore-influencers tab icon uses size 28 while other tabs use size 24. Consider standardizing the size for visual consistency.


Line range hint 63-71: Refactor duplicate color definition and extract styles

The icon color is defined twice (via prop and style) and the styles could be extracted:

+ const drawerToggleIconStyles = {
+   marginLeft: 14,
+   marginBottom: -2,
+ };

  <FontAwesomeIcon
    color={Colors(theme).text}
    icon={faRefresh}
    size={26}
-   style={{
-     marginLeft: 14,
-     color: Colors(theme).text,
-     marginBottom: -2,
-   }}
+   style={drawerToggleIconStyles}
  />
components/InfluencerCard.tsx (2)

Line range hint 89-92: Address TODO comment in onImagePress function

The TODO comment indicates that the data parameter should be replaced with uri.

Consider updating the implementation:

-const onImagePress = (data: any) => {
-  setSelectedImage(data); // TODO: data -> uri
+const onImagePress = (uri: string) => {
+  setSelectedImage(uri);
   setIsZoomed(true);
 }

Line range hint 33-46: Consider strengthening type definitions

The media array in the influencer interface could benefit from a more specific type definition.

Consider updating the type definition:

 interface InfluencerCardPropsType {
   influencer: {
     name: string;
     handle: string;
     profilePic: string;
-    media: {
-      type: string;
-      uri: string;
-    }[];
+    media: Array<{
+      type: 'image' | 'video';
+      uri: string;
+      thumbnail?: string;
+    }>;
     followers: number | string;
     reach: number | string;
     rating: number | string;
     bio: string;
     jobsCompleted: number | string;
     successRate: number | string;
   };
   // ...rest of the interface
 }
components/collaboration/collaboration-details/invitations.tsx (3)

65-65: Enhance error handling implementation.

While changing to console.error is good, consider these improvements for better error management:

  1. Add descriptive error messages
  2. Implement error boundaries
  3. Add retry mechanism for failed fetches
- console.error(error);
+ console.error('Failed to fetch invitations:', error);
+ // Consider implementing retry logic
+ if (retryCount < MAX_RETRIES) {
+   await new Promise(resolve => setTimeout(resolve, RETRY_DELAY));
+   return fetchInvitations();
+ }
// Add this error boundary component
class CollaborationErrorBoundary extends React.Component {
  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    console.error('Collaboration component error:', error);
    Toaster.error('Something went wrong. Please try again later.');
  }
  
  render() {
    return this.props.children;
  }
}

Also applies to: 128-128


197-199: Consider responsive spacing and theme constants.

While adding gap improves layout, consider:

  1. Using theme constants instead of magic numbers
  2. Adding responsive spacing for different screen sizes
 contentContainerStyle={{
-  gap: 8,
+  gap: theme.spacing.small, // Define this in your theme
+  paddingHorizontal: theme.spacing.medium,
 }}

Line range hint 1-276: Consider architectural improvements for better maintainability.

The component could benefit from several architectural improvements:

  1. Define TypeScript interfaces instead of using 'any'
  2. Extract data fetching logic into custom hooks
  3. Break down into smaller components
  4. Use consistent styling patterns

Here's a suggested approach:

// Types
interface Influencer {
  id: string;
  name: string;
  profileImage: string;
  handle?: string;
  backend?: {
    followers: number;
    reach: number;
    rating: number;
  };
}

interface CollaborationInvitationPageProps {
  pageID: string;
  logo?: string;
}

// Custom hook
function useInvitations(pageID: string) {
  const [influencers, setInfluencers] = useState<Influencer[]>([]);
  const [loading, setLoading] = useState(true);
  
  // ... fetch logic here
  
  return { influencers, loading };
}

// Smaller components
const EmptyState = ({ logo }: { logo?: string }) => {
  const theme = useTheme();
  return (
    // ... empty state JSX
  );
};

const InvitationList = ({ influencers }: { influencers: Influencer[] }) => {
  return (
    // ... list JSX
  );
};

Would you like me to help break this down further or create a GitHub issue to track these improvements?

components/collaboration/collaboration-details/CollaborationDetailsContent.tsx (2)

54-89: Consider using TabView for better navigation UX.

The current implementation using ScrollView for tabs has several limitations:

  1. Horizontal scrolling for tabs is not a common pattern
  2. No visual indicator for more tabs when they overflow
  3. No smooth animation between tab transitions

Consider using react-native-paper's TabView component for a more native feel:

import { TabView, SceneMap } from 'react-native-paper';

const renderScene = SceneMap({
  overview: OverviewRoute,
  application: ApplicationRoute,
  invitation: InvitationRoute,
  settings: SettingsRoute,
});

// In your component
<TabView
  navigationState={{ index, routes }}
  renderScene={renderScene}
  onIndexChange={setIndex}
/>

119-129: Remove commented code.

If the Apply Now button is no longer needed, remove the commented code. If it's a temporary removal, track it with a TODO comment explaining why it's disabled and when it should be re-enabled.

app/(main)/(screens)/collaboration-details/[pageID].tsx (1)

19-19: Avoid hardcoding statusBarHeight

Setting statusBarHeight to 0 may cause layout issues on devices with different status bar heights. Hardcoding this value can lead to UI inconsistencies across different devices and platforms.

Consider using the Constants.statusBarHeight from expo-constants or allowing the Appbar.Header component to handle the status bar height automatically:

-<Appbar.Header
-  statusBarHeight={0}
-  style={{
-    backgroundColor: Colors(theme).background,
-  }}
->
+<Appbar.Header
+  style={{
+    backgroundColor: Colors(theme).background,
+  }}
+>

Or, if you need to adjust the status bar height:

import Constants from 'expo-constants';

// ...

<Appbar.Header
  statusBarHeight={Constants.statusBarHeight}
  style={{
    backgroundColor: Colors(theme).background,
  }}
>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between a0c31dc and fb1b71e.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (22)
  • app/(main)/(drawer)/(tabs)/_layout.tsx (5 hunks)
  • app/(main)/(screens)/_layout.tsx (0 hunks)
  • app/(main)/(screens)/collaboration-details/[pageID].tsx (1 hunks)
  • app/(onboarding)/onboarding-brand-preference.tsx (0 hunks)
  • app/(onboarding)/onboarding-get-started.tsx (4 hunks)
  • app/(onboarding)/onboarding-your-brand.tsx (0 hunks)
  • components/BottomSheetActions.tsx (7 hunks)
  • components/InfluencerCard.tsx (4 hunks)
  • components/SearchComponent.tsx (2 hunks)
  • components/collaboration/CollaborationCard.tsx (1 hunks)
  • components/collaboration/CollaborationDetails.tsx (0 hunks)
  • components/collaboration/collaboration-details/CollaborationDetailsContent.tsx (1 hunks)
  • components/collaboration/collaboration-details/applications.tsx (2 hunks)
  • components/collaboration/collaboration-details/index.tsx (1 hunks)
  • components/collaboration/collaboration-details/invitations.tsx (3 hunks)
  • components/drawer-layout/DrawerMenuContent.tsx (2 hunks)
  • components/drawer-layout/DrawerMenuItem.tsx (3 hunks)
  • components/explore-influencers/index.tsx (1 hunks)
  • package.json (2 hunks)
  • styles/CollaborationCard.styles.tsx (1 hunks)
  • styles/InfluencerCard.styles.tsx (3 hunks)
  • utils/conversion.ts (1 hunks)
💤 Files with no reviewable changes (4)
  • app/(main)/(screens)/_layout.tsx
  • app/(onboarding)/onboarding-brand-preference.tsx
  • app/(onboarding)/onboarding-your-brand.tsx
  • components/collaboration/CollaborationDetails.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • app/(onboarding)/onboarding-get-started.tsx
  • components/BottomSheetActions.tsx
  • components/SearchComponent.tsx
🧰 Additional context used
🪛 Biome
components/drawer-layout/DrawerMenuContent.tsx

[error] 25-26: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

🔇 Additional comments (21)
utils/conversion.ts (1)

1-6: Verify the function's usage context.

Let's ensure this function is used consistently across the codebase for similar numeric displays.

✅ Verification successful

Based on the search results, I can now generate the final response:

The convertToKUnits function is being used appropriately for social metrics display

The function is currently being used consistently in InfluencerCard.tsx for formatting both followers and reach metrics:

  • convertToKUnits(Number(influencer.followers))
  • convertToKUnits(Number(influencer.reach))

Other numeric displays in the codebase are either:

  • Using toString() for React key generation (which is appropriate)
  • Using toLocaleString() for range displays in filter UI (which is appropriate for full number formatting with commas)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find similar numeric formatting patterns that could benefit from using this function

# Look for direct number formatting
rg -g '*.{ts,tsx}' -A 2 'toFixed\(1\)|toString\(\)'

# Look for potential social media metric displays
rg -g '*.{ts,tsx}' -i 'followers|likes|views|reach'

Length of output: 6669

styles/CollaborationCard.styles.tsx (1)

57-59: LGTM! The spacing improvements look good.

The changes to chipRow style provide more precise control over spacing:

  • Separate margin values offer better vertical rhythm control
  • The gap property is a clean solution for spacing between wrapped items

Please verify the layout renders correctly across different screen sizes and orientations. Run this script to find relevant test files:

✅ Verification successful

The style changes are safe and well-integrated

The component implementation shows that:

  • The chipRow style is used in a simple View containing three Chip components
  • The layout is not dependent on screen dimensions or responsive utilities
  • The chips have fixed content (Applied, Invited, Shortlisted counts) that won't cause unexpected wrapping

The margin and gap changes are safe because:

  • The component uses React Native Paper's Card component which provides consistent padding
  • The chips have fixed-width content that benefits from the new gap spacing
  • The vertical margins help maintain proper spacing with adjacent card elements
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find test files related to CollaborationCard component
# to ensure proper testing of responsive layout

# Search for test files
echo "Searching for CollaborationCard test files..."
fd -e test.tsx -e test.ts -e spec.tsx -e spec.ts CollaborationCard

# Search for snapshot tests
echo "Searching for snapshot tests..."
rg -l "toMatchSnapshot" | rg -i "collaborationcard"

Length of output: 292


Script:

#!/bin/bash
# Let's try a broader search to understand the component's test coverage and styling

# Search for any files related to CollaborationCard
echo "Searching for all CollaborationCard files..."
fd CollaborationCard

# Search for style imports or references
echo "Searching for style references..."
rg -l "CollaborationCard.styles"

# Search for any test files that might reference this component
echo "Searching for test references..."
rg -l "CollaborationCard" | rg -i "test|spec"

# Look for style-related tests or snapshots in the codebase
echo "Searching for style-related tests..."
rg -l "style" | rg -i "test|spec"

Length of output: 599


Script:

#!/bin/bash
# Let's examine the component implementation to understand how these styles are used

# Check the CollaborationCard component implementation
echo "CollaborationCard component content:"
cat components/collaboration/CollaborationCard.tsx

# Check if there are any responsive style utilities or hooks used
echo -e "\nSearching for responsive utilities:"
rg -l "useWindowDimensions|Dimensions|responsive" components/collaboration/CollaborationCard.tsx styles/CollaborationCard.styles.tsx

# Look for any existing style tests in the component directory
echo -e "\nChecking for style tests in component directory:"
rg -l "style" components/collaboration/

Length of output: 4475

styles/InfluencerCard.styles.tsx (5)

12-13: Verify header padding asymmetry

The header now has asymmetric padding (16px all around except 8px on the right). While this might be intentional, please verify that this doesn't cause any visual alignment issues with the content, especially considering the new content padding of 16px.

Consider using a shared spacing constant for consistency across the app:

const SPACING = {
  default: 16,
  compact: 8,
} as const;

18-20: LGTM! Good spacing consistency

The new content padding matches the header's primary padding, creating a visually balanced layout.


45-46: LGTM! Good use of theme colors

The addition of horizontal padding and theme-based background color improves visual consistency.


50-50: LGTM! Consistent theme usage

Good addition of theme-based background color for the indicator container.


29-29: Verify text readability with reduced font size

The reduction in font size from 16 to 12 might impact readability. Please ensure this meets accessibility guidelines for minimum text size.

✅ Verification successful

Font size of 12px appears to be consistently used for secondary information

The font size of 12px is used in multiple components for similar purposes:

  • InfluencerCard.styles.tsx
  • CollaborationCard.styles.tsx
  • CollaborationDetailsContent.tsx (multiple instances)

This appears to be a deliberate design choice for secondary or supporting text elements. The codebase shows a clear font size hierarchy:

  • Large text (20-30px) for headers and titles
  • Medium text (16-18px) for primary content
  • Small text (12-14px) for secondary information
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for consistent font size usage across the app
rg "fontSize.*12" --type tsx

Length of output: 58


Script:

#!/bin/bash
# Search for consistent font size usage across the app with .ts and .tsx extensions
rg "fontSize.*12" -t ts -t tsx

# Also search for any font size definitions to understand the pattern
rg "fontSize.*\d+" -t ts -t tsx

Length of output: 122


Script:

#!/bin/bash
# Search for font size patterns in all files
rg "fontSize.*12"

# Search for any font size definitions to understand the pattern
rg "fontSize.*\d+"

# List all style files to understand the file extensions
fd "style" --type f

Length of output: 4763

components/collaboration/collaboration-details/index.tsx (1)

1-8: LGTM! Imports are well-organized and necessary.

All imports are being used and follow a logical grouping pattern.

package.json (1)

21-25: Verify package compatibility

Please ensure compatibility between FontAwesome and react-native-svg versions. Additionally, consider updating react-native-svg to the latest stable version (18.2.0).

Run the following script to check for compatibility issues and latest versions:

Also applies to: 71-71

✅ Verification successful

Let me verify the react-native-svg version in the package.json to ensure it meets the requirements.


FontAwesome packages and react-native-svg are compatible

The current setup is compatible:

  • react-native-fontawesome@0.3.2 requires react-native-svg >= 11.x
  • Your react-native-svg@15.2.0 satisfies this requirement
  • FontAwesome core/icon packages are at v6.7.0, which is compatible with react-native-fontawesome (requires ~1 || ~6)

While react-native-svg 15.9.0 is available, the current version (15.2.0) is recent enough and there's no compelling reason to update at this time.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check package compatibility and latest versions

# Check latest versions
echo "Checking latest versions..."
npm view @fortawesome/react-native-fontawesome version
npm view react-native-svg version

# Check peer dependencies
echo "Checking peer dependencies..."
npm view @fortawesome/react-native-fontawesome peerDependencies
npm view react-native-svg peerDependencies

Length of output: 502


Script:

#!/bin/bash
# Check react-native-svg version in package.json
grep "react-native-svg" package.json

Length of output: 71

components/explore-influencers/index.tsx (1)

72-72: LGTM: Improved list item spacing

The addition of a gap property provides consistent spacing between list items, improving the visual layout.

components/drawer-layout/DrawerMenuContent.tsx (3)

16-24: LGTM! Well-organized icon imports.

The FontAwesome icons are properly imported and grouped by their respective packages (regular and solid).


31-51: LGTM! Semantic icon choices.

The FontAwesome icons are appropriately matched to their menu items:

  • faHandshake for "Explore Influencers"
  • faStar for "Collaborations"
  • faPlusCircle for "Create Collaboration"
  • faComment for "Messages"
  • faFileSignature for "Contracts"

112-112: LGTM! Theme-aware placeholder color.

The placeholder color is properly implemented using the theme-aware Colors utility.

app/(main)/(drawer)/(tabs)/_layout.tsx (2)

13-23: LGTM! Clean import organization

The FontAwesome imports are well-organized, with clear separation between regular and solid icons. Each imported icon is used in the component.


94-100: LGTM! Consistent icon implementation

The icon implementations for collaborations, create-collaboration, messages, and contracts tabs are consistent and well-structured, using the same size and required props.

Also applies to: 116-120, 128-132, 144-148

components/collaboration/collaboration-details/applications.tsx (1)

110-112: LGTM! Improved list item spacing

The addition of gap spacing between list items enhances visual clarity and is consistent with the theme update objectives.

components/InfluencerCard.tsx (4)

26-28: LGTM: Import changes align with the component updates

The new imports support the transition to FontAwesome icons and add number formatting capabilities.


105-109: LGTM: Improved header structure

The added View wrapper enhances the layout organization while maintaining the existing functionality.


146-178: LGTM: Well-structured stats section with improved formatting

The stats section has been nicely organized with:

  • Consistent icon usage through FontAwesome
  • Improved number formatting using convertToKUnits
  • Clear visual hierarchy through nested Views

188-203: LGTM: Clean implementation of bio and job history sections

The expandable bio and job history link are well implemented with appropriate user interactions.

components/collaboration/collaboration-details/CollaborationDetailsContent.tsx (1)

82-89: Verify the Settings tab implementation.

The Settings tab is included in the navigation but has no corresponding content rendering logic.

Comment on lines +6 to +7
import { FontAwesomeIcon } from "@fortawesome/react-native-fontawesome";
import { IconProp } from "@fortawesome/fontawesome-svg-core";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codebase verification

Based on the verification results, I can now provide a final response:

FontAwesome setup needs to be initialized

The required FontAwesome dependencies are correctly installed in package.json, but the icon library initialization is missing. To properly use FontAwesome icons:

  • Add library initialization in your app's entry point (e.g., App.tsx):
import { library } from '@fortawesome/fontawesome-svg-core';
import { fab } from '@fortawesome/free-brands-svg-icons';
import { far } from '@fortawesome/free-regular-svg-icons';
import { fas } from '@fortawesome/free-solid-svg-icons';

library.add(fab, far, fas);
🔗 Analysis chain

Verify FontAwesome configuration

The migration to @fortawesome/react-native-fontawesome requires proper setup of FontAwesome dependencies and icon library initialization.

Also applies to: 11-11

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if required FontAwesome dependencies are configured
# Description: Verify package.json for required dependencies and icon library initialization

# Check for required dependencies
echo "Checking package.json for FontAwesome dependencies..."
cat package.json | jq '.dependencies | select(has("@fortawesome/fontawesome-svg-core") and has("@fortawesome/react-native-fontawesome"))'

# Look for icon library initialization
echo "Checking for icon library initialization..."
rg -l "library.add" --type ts --type tsx

Length of output: 2669


Script:

#!/bin/bash
# Check for FontAwesome library initialization and icon usage
# Using different search patterns to find library initialization

# Look for FontAwesome library initialization
echo "Checking for library initialization with different patterns..."
rg -l "import.*library.*fontawesome" --type-add 'tsx:*.tsx' --type-add 'ts:*.ts' --type ts --type tsx

# Look for icon imports and usage
echo "Checking icon imports and usage..."
rg "from '@fortawesome/free-.*-icons'" --type-add 'tsx:*.tsx' --type-add 'ts:*.ts' --type ts --type tsx

# Check App.tsx or similar entry files for FontAwesome setup
echo "Checking entry files..."
fd -e tsx -e ts "^(App|index)\." -x cat {}

Length of output: 27210

Comment on lines +19 to +26
const CollaborationDetails: React.FC<CollaborationDetailsProps> = ({
pageID
}) => {
const [collaboration, setCollaboration] = useState<Collaboration | undefined>(
undefined
); // Explicit type
const [loading, setLoading] = useState(true);
const [activeTab, setActiveTab] = useState("Overview");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove unused tab functionality or implement additional tabs.

The component initializes activeTab state but only implements the "Overview" tab. This suggests either:

  1. Missing implementation of additional tabs
  2. Unnecessary tab-related code that should be removed

If no other tabs are planned, simplify the code by removing tab-related logic:

-  const [activeTab, setActiveTab] = useState("Overview");
📝 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
const CollaborationDetails: React.FC<CollaborationDetailsProps> = ({
pageID
}) => {
const [collaboration, setCollaboration] = useState<Collaboration | undefined>(
undefined
); // Explicit type
const [loading, setLoading] = useState(true);
const [activeTab, setActiveTab] = useState("Overview");
const CollaborationDetails: React.FC<CollaborationDetailsProps> = ({
pageID
}) => {
const [collaboration, setCollaboration] = useState<Collaboration | undefined>(
undefined
); // Explicit type
const [loading, setLoading] = useState(true);

Comment on lines +27 to +50
const fetchCollaboration = async () => {
if (!pageID) return;
try {
const collabRef = doc(FirestoreDB, "collaborations", pageID as string);
const snapshot = await getDoc(collabRef);
const data = snapshot.data() as ICollaboration;
if (!data) return;

const brandRef = doc(FirestoreDB, "brands", data.brandId);
const brandSnapshot = await getDoc(brandRef);
const brandData = brandSnapshot.data();

setCollaboration({
...data,
brandName: brandData?.name || "Unknown Brand",
paymentVerified: brandData?.paymentMethodVerified || false,
brandDescription: brandData?.description || "",
});
} catch (e) {
console.error(e);
} finally {
setLoading(false);
}
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance error handling and add cleanup logic.

The current implementation has several areas for improvement:

  1. Error handling only logs to console
  2. No cleanup for unmounted component
  3. No loading state reset on error

Consider implementing these improvements:

 const fetchCollaboration = async () => {
+  const abortController = new AbortController();
   if (!pageID) return;
   try {
     const collabRef = doc(FirestoreDB, "collaborations", pageID as string);
     const snapshot = await getDoc(collabRef);
     const data = snapshot.data() as ICollaboration;
-    if (!data) return;
+    if (!data) {
+      throw new Error('Collaboration not found');
+    }

     const brandRef = doc(FirestoreDB, "brands", data.brandId);
     const brandSnapshot = await getDoc(brandRef);
     const brandData = brandSnapshot.data();

+    if (abortController.signal.aborted) return;
     setCollaboration({
       ...data,
       brandName: brandData?.name || "Unknown Brand",
       paymentVerified: brandData?.paymentMethodVerified || false,
       brandDescription: brandData?.description || "",
     });
   } catch (e) {
-    console.error(e);
+    console.error('Failed to fetch collaboration:', e);
+    // Handle error appropriately (e.g., show error message to user)
   } finally {
     setLoading(false);
   }
+  return () => abortController.abort();
 };

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

Comment thread package.json
Comment on lines +21 to +25
"@fortawesome/fontawesome-svg-core": "^6.7.0",
"@fortawesome/free-brands-svg-icons": "^6.7.0",
"@fortawesome/free-regular-svg-icons": "^6.7.0",
"@fortawesome/free-solid-svg-icons": "^6.7.0",
"@fortawesome/react-native-fontawesome": "^0.3.2",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Verify FontAwesome package versions

The specified version 6.7.0 for FontAwesome packages appears to be incorrect as it doesn't exist. The latest stable version is 6.5.1.

Apply this diff to update to the correct version:

-    "@fortawesome/fontawesome-svg-core": "^6.7.0",
-    "@fortawesome/free-brands-svg-icons": "^6.7.0",
-    "@fortawesome/free-regular-svg-icons": "^6.7.0",
-    "@fortawesome/free-solid-svg-icons": "^6.7.0",
+    "@fortawesome/fontawesome-svg-core": "^6.5.1",
+    "@fortawesome/free-brands-svg-icons": "^6.5.1",
+    "@fortawesome/free-regular-svg-icons": "^6.5.1",
+    "@fortawesome/free-solid-svg-icons": "^6.5.1",
📝 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
"@fortawesome/fontawesome-svg-core": "^6.7.0",
"@fortawesome/free-brands-svg-icons": "^6.7.0",
"@fortawesome/free-regular-svg-icons": "^6.7.0",
"@fortawesome/free-solid-svg-icons": "^6.7.0",
"@fortawesome/react-native-fontawesome": "^0.3.2",
"@fortawesome/fontawesome-svg-core": "^6.5.1",
"@fortawesome/free-brands-svg-icons": "^6.5.1",
"@fortawesome/free-regular-svg-icons": "^6.5.1",
"@fortawesome/free-solid-svg-icons": "^6.5.1",
"@fortawesome/react-native-fontawesome": "^0.3.2",

Comment on lines 81 to 89
<Chip mode="outlined">
Applied: {props.applications}
</Chip>
<Chip style={styles.chip} mode="outlined">
<Chip mode="outlined">
Invited: {props.invitations}
</Chip>
<Chip style={styles.chip} mode="outlined">
<Chip mode="outlined">
Shortlisted: {props.acceptedApplications}
</Chip>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance Chip components with error handling, accessibility, and formatting

The current implementation could benefit from several improvements:

+ const formatCount = (count?: number) => count?.toString() ?? '0';
+ 
+ interface MetricChipProps {
+   label: string;
+   value?: number;
+ }
+ 
+ const MetricChip = ({ label, value }: MetricChipProps) => (
+   <Chip
+     mode="outlined"
+     accessibilityLabel={`${label}: ${formatCount(value)}`}
+   >
+     {`${label}: ${formatCount(value)}`}
+   </Chip>
+ );
+
- <Chip mode="outlined">
-   Applied: {props.applications}
- </Chip>
- <Chip mode="outlined">
-   Invited: {props.invitations}
- </Chip>
- <Chip mode="outlined">
-   Shortlisted: {props.acceptedApplications}
- </Chip>
+ <MetricChip label="Applied" value={props.applications} />
+ <MetricChip label="Invited" value={props.invitations} />
+ <MetricChip label="Shortlisted" value={props.acceptedApplications} />

This refactor:

  1. Adds proper error handling for undefined values
  2. Improves accessibility with proper labels
  3. Reduces code duplication with a reusable component
  4. Makes the code more maintainable

Consider also adding number formatting for large values (e.g., "1.2K" instead of "1200") using the convertToKUnits utility mentioned in the summary.

📝 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
<Chip mode="outlined">
Applied: {props.applications}
</Chip>
<Chip style={styles.chip} mode="outlined">
<Chip mode="outlined">
Invited: {props.invitations}
</Chip>
<Chip style={styles.chip} mode="outlined">
<Chip mode="outlined">
Shortlisted: {props.acceptedApplications}
</Chip>
const formatCount = (count?: number) => count?.toString() ?? '0';
interface MetricChipProps {
label: string;
value?: number;
}
const MetricChip = ({ label, value }: MetricChipProps) => (
<Chip
mode="outlined"
accessibilityLabel={`${label}: ${formatCount(value)}`}
>
{`${label}: ${formatCount(value)}`}
</Chip>
);
<MetricChip label="Applied" value={props.applications} />
<MetricChip label="Invited" value={props.invitations} />
<MetricChip label="Shortlisted" value={props.acceptedApplications} />

</Text>
</View>
<Card.Content>
<Paragraph style={styles.paragraph}>Instagram</Paragraph>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove hardcoded platform value.

The platform value "Instagram" is hardcoded instead of using the platform value from props.

-<Paragraph style={styles.paragraph}>Instagram</Paragraph>
+<Paragraph style={styles.paragraph}>{props.collaborationDetail.platform}</Paragraph>
📝 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
<Paragraph style={styles.paragraph}>Instagram</Paragraph>
<Paragraph style={styles.paragraph}>{props.collaborationDetail.platform}</Paragraph>

location: {
type: string;
name?: string;
latlong?: any;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Replace any type with a more specific type definition.

Using any type reduces TypeScript's type safety benefits. Consider defining a proper type for the latlong property.

-  latlong?: any;
+  latlong?: {
+    latitude: number;
+    longitude: number;
+  };
📝 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
latlong?: any;
latlong?: {
latitude: number;
longitude: number;
};

logo: string;
}

const CollaborationDetailsContent = (props: any) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use the defined interface instead of any for props typing.

The component is using any type for props despite having a well-defined CollaborationAdCardProps interface.

-const CollaborationDetailsContent = (props: any) => {
+const CollaborationDetailsContent = (props: CollaborationAdCardProps & {
+  pageID: string;
+  collaborationDetail: {
+    name: string;
+    brandName: string;
+    description: string;
+    appliedCount: number;
+    brandViewed: number;
+    location: {
+      type: string;
+      name?: string;
+    };
+    budget: {
+      min: string;
+    };
+  };
+}) => {

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


const CollaborationDetailsScreen = () => {
const theme = useTheme();
const [isVisible, setIsVisible] = useState(false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

isVisible state is never set to true

The isVisible state is initialized to false and only set to false in the onClose handler of BottomSheetActions. Without setting isVisible to true, the BottomSheetActions component may never appear.

Consider adding logic to set isVisible to true when appropriate. For example, you might add a button or handle an event to toggle the visibility:

// Example: Adding a button to open BottomSheetActions
<Button onPress={() => setIsVisible(true)}>Open Actions</Button>

)}
</Appbar.Header>
<CollaborationDetails
pageID={pageID as string}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Check if pageID is defined before usage

The pageID value is cast to a string without checking if it's undefined. If pageID is undefined, this could lead to runtime errors in the CollaborationDetails component.

Add a check to ensure that pageID is defined before using it:

if (!pageID) {
  // Handle the missing pageID scenario, e.g., show an error, navigate back, etc.
  return (
    <View>
      <Text>Error: Page ID is missing.</Text>
    </View>
  );
}

Alternatively, you can provide a default value or handle the case within the component.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 27

🧹 Outside diff range and nitpick comments (55)
styles/tag/Tag.styles.tsx (2)

12-15: Consider using typography system constants.

Instead of hard-coding the font size, consider using typography constants from your design system for better maintainability and consistency.

 tagText: {
-  fontSize: 12,
+  fontSize: Typography.size.small, // or similar constant from your design system
   color: Colors(theme).tagForeground,
 },

1-16: Verify component architecture alignment.

Since this is a new tag component style, ensure it follows the project's component architecture:

  1. Confirm that the parent Tag component properly uses these styles
  2. Verify that the component is properly documented in the project's component library
  3. Consider adding style variants (e.g., size, emphasis) if needed by the design system
components/ui/tag/index.tsx (2)

9-10: Optimize theme-based styles with useMemo

The styles are recalculated on every render. Consider memoizing the styles to prevent unnecessary recalculations.

+import { useMemo } from 'react';

 const Tag: React.FC<ChipProps> = ({ icon, children }) => {
   const theme = useTheme();
-  const styles = stylesFn(theme);
+  const styles = useMemo(() => stylesFn(theme), [theme]);

1-23: Consider adding JSDoc documentation

As this is a reusable UI component used across the application for status tags, adding comprehensive documentation would improve maintainability.

+/**
+ * A themed tag component wrapping react-native-paper's Chip.
+ * Primarily used for displaying status indicators in CollaborationCard.
+ *
+ * @example
+ * ```tsx
+ * <Tag icon="check">Applied</Tag>
+ * ```
+ *
+ * @param props - Extended ChipProps from react-native-paper
+ * @param props.icon - Optional icon name from react-native-paper's icon set
+ * @param props.children - The text content of the tag
+ */
 const Tag: React.FC<ChipProps> = ({
components/collaborations/index.tsx (1)

20-34: Add TypeScript types and improve code organization

The component implementation could benefit from better TypeScript usage and style organization.

Consider applying these changes:

+ import { FC } from 'react';
+ import { StyleSheet } from 'react-native';

- const Collaborations = () => {
+ const Collaborations: FC = () => {
   return (
     <AppLayout>
       <View
-        style={{
-          flex: 1,
-          paddingTop: 16,
-        }}
+        style={styles.container}
       >
         <TopTabNavigation
           tabs={tabs}
         />
       </View>
     </AppLayout>
   );
 };

+ const styles = StyleSheet.create({
+   container: {
+     flex: 1,
+     paddingTop: 16,
+   },
+ });

 export default Collaborations;
styles/collaboration-details/CollaborationHeader.styles.tsx (2)

11-24: Consider backwards compatibility for gap property.

While the gap property provides clean spacing, it might not be supported in older React Native versions. Consider adding a fallback using margin/padding.

 logoSection: {
   flexDirection: 'row',
   alignItems: 'center',
-  gap: 12,
+  // Fallback for older RN versions
+  marginRight: 12,
 },

25-43: Consider centralizing typography values.

Font sizes and weights could be moved to a typography constants file for better maintainability and consistency across the app.

Consider creating a Typography constant file:

// constants/Typography.ts
export const Typography = {
  heading: {
    size: 18,
    weight: '600'
  },
  body: {
    size: 14,
    weight: '400'
  }
};
components/ui/menu-icon/index.tsx (1)

Line range hint 2-2: Remove unused FontAwesome import

The FontAwesome import from @expo/vector-icons is no longer used after switching to @fortawesome/react-native-fontawesome.

-import { FontAwesome } from "@expo/vector-icons";
styles/top-tab-navigation/TopTabNavigation.styles.tsx (2)

5-10: Add consistent theme null checking

The code uses optional chaining for theme in other places (line 26: theme?.dark), but not in Colors(theme).transparent. Consider handling potential undefined theme consistently throughout the styles.

-    backgroundColor: Colors(theme).transparent,
+    backgroundColor: Colors(theme?.dark ? theme : undefined).transparent,

20-34: Review positioning and consider extracting style constants

  1. The position: 'relative' seems unnecessary as there are no absolute-positioned children elements.
  2. Magic numbers for border radius (15, 10) should be extracted into constants for better maintainability.

Consider creating a constants file for common style values:

// constants/Layout.ts
export const BorderRadius = {
  LARGE: 15,
  MEDIUM: 10,
  // ... other radius values
};

Then update the styles:

-    borderRadius: 15,
+    borderRadius: BorderRadius.LARGE,
     // ...
-    borderRadius: 10,
+    borderRadius: BorderRadius.MEDIUM,
constants/Colors.ts (1)

51-54: Good addition of Material Design color tokens

The new semantic color properties align well with Material Design principles. Consider adding documentation comments to explain the intended usage of each token.

Add JSDoc comments to document the color system:

+ /** Material Design color tokens for consistent theming */
  onPrimary: "#ffffff",
  secondary: "#054463",
  secondaryContainer: "#538BA6",
  onSecondaryContainer: "#ffffff",
components/ui/empty-state/index.tsx (2)

15-15: Consider using proper TypeScript types for style prop

Instead of using any, consider using proper React Native style types for better type safety and IDE support.

-  style?: any;
+  style?: StyleProp<ViewStyle>;

Don't forget to add the import:

import { StyleProp, ViewStyle } from 'react-native';

51-56: Consider making image dimensions responsive

The hardcoded dimensions (256x256) might not be optimal for all screen sizes. Consider using responsive dimensions or making them configurable via props.

-            width={256}
-            height={256}
+            style={[
+              styles.image,
+              { width: '80%', aspectRatio: 1 }
+            ]}
components/collaboration/CollaborationHeader.tsx (4)

13-15: Fix typo in interface name

The interface name has a typo: 'Colloboration' should be 'Collaboration'.

-const CollaborationHeader: React.FC<ColloborationHeaderProps> = ({
+const CollaborationHeader: React.FC<CollaborationHeaderProps> = ({

23-26: Consider adding fallback UI for missing logo

The Avatar component should handle missing or failed logo images gracefully.

 <Avatar.Image
   size={48}
   source={imageUrl(collaboration?.logo)}
+  fallback={
+    <Avatar.Text
+      size={48}
+      label={collaboration.name.substring(0, 2).toUpperCase()}
+    />
+  }
 />

54-58: Improve type safety for location handling

The location type check could be more robust with proper type guards or constants.

-collaboration.location.type === "Remote"
+collaboration.location.type === 'REMOTE'

Consider defining an enum or constant for location types:

enum LocationType {
  REMOTE = 'REMOTE',
  ONSITE = 'ONSITE'
}

40-60: Add error boundaries and loading states

The component doesn't handle loading states or errors when collaboration data is being fetched or is invalid.

Consider:

  1. Adding a loading skeleton UI
  2. Implementing error boundaries
  3. Adding prop-types validation or runtime checks for required fields
components/ui/brand-switcher/index.tsx (2)

40-43: Remove duplicate color property and improve icon alignment

The icon color is defined twice - both in the color prop and in the style object. Additionally, the negative margin bottom seems like a workaround for alignment issues.

Consider this cleaner implementation:

 <FontAwesomeIcon
-  color={Colors(theme).text}
   icon={faRightLeft}
   size={24}
   style={{
     marginLeft: 14,
     color: Colors(theme).text,
-    marginBottom: -2,
+    alignSelf: 'center'
   }}
 />

Line range hint 12-21: Consider adding error handling for brands data

While the implementation is solid, it might be worth adding error handling and loading states for the brands data to improve user experience when data fetching fails or is in progress.

Consider adding:

  1. A loading indicator while brands are being fetched
  2. Error handling for cases where brands data is undefined or empty
  3. Fallback UI for error states

Example implementation:

const BrandSwitcher = () => {
  const [visible, setVisible] = useState(false);
  const theme = useTheme();

  const {
    brands,
    selectedBrand,
    setSelectedBrand,
    isLoading, // Add these to your context
    error      // Add these to your context
  } = useBrandContext();

  if (isLoading) {
    return <ActivityIndicator color={Colors(theme).primary} />;
  }

  if (error || !brands?.length) {
    return null; // or a fallback UI
  }
  
  // ... rest of your component
constants/Influencers.ts (3)

3-3: Consider using UUIDs for influencer IDs

While adding IDs is a good practice, using sequential string IDs ("1", "2") might lead to scalability and uniqueness issues. Consider using UUIDs or auto-incrementing numbers instead.

Example implementation:

-    id: "1",
+    id: "550e8400-e29b-41d4-a716-446655440000",

Also applies to: 31-31


Line range hint 1-54: Add TypeScript interface definitions

The file lacks type definitions for the influencer objects. Adding interfaces would improve type safety and maintainability.

Consider adding these type definitions at the beginning of the file:

interface Media {
  type: 'video' | 'image';
  uri: string;
}

interface Influencer {
  id: string;
  name: string;
  handle: string;
  followers: number;
  reach: number;
  rating: string;
  bio: string;
  jobsCompleted: number;
  successRate: string;
  profilePic: string;
  collaborationType: string;
  engagement: number;
  media: Media[];
}

export const influencers: Influencer[] = [
  // ... existing data
];

Line range hint 1-54: Consider separating mock data

This appears to be mock/sample data. Consider moving it to a separate test file or mock data directory to maintain a clear separation between actual constants and test data.

Suggested structure:

src/
  constants/
    types.ts          # Contains interfaces/types
  __mocks__/
    influencers.ts    # Contains mock data
components/collaboration/collaboration-details/OverviewTabContent.tsx (4)

24-36: Move inline styles to stylesheet for better maintainability

Consider moving the ScrollView styles to the stylesFn for consistency and maintainability. Also, the gap property might need a fallback for older React Native versions.

-    <ScrollView
-      style={{
-        flex: 1,
-        paddingTop: 8,
-        paddingHorizontal: 16,
-        paddingBottom: 16,
-      }}
-      showsVerticalScrollIndicator={false}
-      contentContainerStyle={{
-        gap: 16,
-        paddingBottom: 16,
-      }}
+    <ScrollView
+      style={styles.scrollView}
+      showsVerticalScrollIndicator={false}
+      contentContainerStyle={styles.scrollViewContent}

54-55: Remove unnecessary string padding

The empty string with space {" "} is unnecessary and should be removed.


41-44: Move default image URL to constants

Hardcoded URLs should be moved to a constants file for better maintainability.

+ // In constants/images.ts
+ export const DEFAULT_COLLABORATION_IMAGE = "https://cdn.pixabay.com/photo/2022/09/21/17/02/blue-background-7470781_640.jpg";

- uri:
-   logo ||
-   "https://cdn.pixabay.com/photo/2022/09/21/17/02/blue-background-7470781_640.jpg",
+ uri: logo || DEFAULT_COLLABORATION_IMAGE,

16-104: Add error handling and accessibility support

The component needs additional improvements for production readiness:

  1. Add error boundaries for graceful error handling
  2. Include accessibility labels for images and interactive elements
  3. Add loading states for async data

Would you like me to provide example implementations for these improvements?

components/ui/top-tab-navigation/index.tsx (3)

7-13: Consider strengthening type definitions

The props interface could be improved for better type safety:

 interface TopTabNavigationProps {
-  tabs: {
+  readonly tabs: ReadonlyArray<{
     id: string;
     title: string;
-    component: React.ReactNode;
+    component: JSX.Element;
   }[];
 }

45-52: Document magic numbers and add scroll configuration

The scroll offset calculation uses a magic number and could benefit from configuration options.

+  const TAB_SCROLL_PADDING = 16; // Padding to ensure tab is fully visible
+
   const scrollToTab = (index: number) => {
     if (scrollViewRef.current && tabLayout[tabs[index].id]) {
       scrollViewRef.current.scrollTo({
-        x: tabLayout[tabs[index].id].x - 16,
+        x: tabLayout[tabs[index].id].x - TAB_SCROLL_PADDING,
         animated: true,
+        behavior: 'smooth',
       });
     }
   };

15-103: Consider architectural improvements for better maintainability and performance

The component could benefit from the following architectural improvements:

  1. Separate scroll logic into a custom hook
  2. Add virtualization for large tab lists
  3. Consider implementing a context for theme and tab state management

Would you like me to provide an example implementation of these improvements?

components/collaboration/collaboration-details/index.tsx (2)

62-66: Remove commented code and clarify logo handling.

The logo handling appears incomplete:

  1. There's commented out code that should be removed
  2. An empty string is hardcoded instead of using the actual logo
-          // logo={collaboration?.logo}
-          logo={""}
+          logo={collaboration?.logo ?? ""}

73-79: Improve type safety in ApplicationsTabContent props.

The collaboration object is partially constructed with optional properties. Consider creating a specific interface for the minimal collaboration data needed by ApplicationsTabContent.

+interface MinimalCollaboration {
+  id: string;
+  name?: string;
+}

-          collaboration={{
-            id: pageID,
-            name: collaboration?.name,
-          }}
+          collaboration={({
+            id: pageID,
+            name: collaboration?.name,
+          } satisfies MinimalCollaboration)}
components/collaboration/collaboration-details/ApplicationsTabContent.tsx (3)

77-85: Remove unnecessary semicolon after if block.

The empty state implementation looks good, but there's an unnecessary semicolon after the if block.

  if (influencers.length === 0) {
    return (
      <EmptyState
        subtitle="No applications yet. Check back later."
        image={require("@/assets/images/illustration6.png")}
        hideAction
      />
    );
-  };
+  }

119-123: Consider extracting magic numbers to constants.

While the spacing improvements are good, consider moving the magic numbers (8, 16) to a theme or constants file for better maintainability.

+ // In theme/spacing.ts
+ export const SPACING = {
+   xs: 8,
+   md: 16,
+ };

  contentContainerStyle={{
-   gap: 8,
+   gap: SPACING.xs,
  }}
  style={{
-   paddingHorizontal: 16,
+   paddingHorizontal: SPACING.md,
    paddingBottom: 100,
  }}

127-141: Consider memoizing BottomSheetActions.

To prevent unnecessary re-renders, consider wrapping BottomSheetActions with React.memo and using useCallback for the onClose handler.

+ const handleClose = useCallback(() => setIsVisible(false), []);

  {isVisible && (
    <BottomSheetActions
      cardType="applicationCard"
      data={{
        collaboration: props.collaboration
      }}
      isVisible={isVisible}
-     onClose={() => setIsVisible(false)}
+     onClose={handleClose}
      snapPointsRange={["30%", "50%"]}
      cardId={selectedApplication}
      key={selectedApplication?.applicationID}
    />
  )}
app/(main)/(drawer)/(tabs)/_layout.tsx (3)

64-67: Consider using a more intuitive drawer toggle icon

The faRightLeft icon might not be immediately recognizable as a drawer toggle. Consider using more conventional options like faBars or faHamburger which are commonly used for drawer/menu toggles.

-                    icon={faRightLeft}
+                    icon={faBars}

52-56: Maintain consistent icon sizes across tabs

The explore-influencers tab uses a size of 28, while all other tabs use 24. Consider standardizing the icon sizes for visual consistency.

              color={color}
              icon={faHandshake}
-              size={28}
+              size={24}

Also applies to: 96-100, 117-121, 129-133, 145-149


52-56: Consider memoizing tab icons for performance

The tab icons are created inline which could cause unnecessary re-renders. Consider extracting them into memoized components.

Example implementation:

const MemoizedTabIcon = React.memo(({ icon, color, size = 24 }) => (
  <FontAwesomeIcon color={color} icon={icon} size={size} />
));

// Usage in tab options
tabBarIcon: ({ color }) => <MemoizedTabIcon icon={faHandshake} color={color} />

Also applies to: 96-100, 117-121, 129-133, 145-149

components/drawer-layout/DrawerMenuContent.tsx (1)

Line range hint 158-158: Fix operator precedence in marginBottom calculation.

The current expression marginBottom: bottom + Platform.OS === 'android' ? 24 : 44 has incorrect operator precedence. The addition will be performed before the comparison, leading to unexpected results.

Apply this fix:

-marginBottom: bottom + Platform.OS === 'android' ? 24 : 44,
+marginBottom: bottom + (Platform.OS === 'android' ? 24 : 44),
🧰 Tools
🪛 Biome

[error] 25-26: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

components/InfluencerCard.tsx (1)

62-62: Consider handling loading state during invitation check

With the removal of loading state, users might experience UI jumps when the invitation status is being fetched. Consider adding a loading indicator or skeleton state during the check.

 const [isInvited, setIsInvited] = useState(false);
+const [isCheckingInvitation, setIsCheckingInvitation] = useState(false);
components/FilterModal.tsx (5)

113-117: Consider adding accessibility props

While the icon replacement looks good, consider enhancing accessibility.

 <TouchableOpacity onPress={onClose}>
   <FontAwesomeIcon
     color={Colors(theme).text}
     icon={faClose}
     size={24}
+    accessibilityRole="button"
+    accessibilityLabel="Close filter modal"
   />
 </TouchableOpacity>

121-123: Simplify inline styles

Consider moving these styles to the StyleSheet for better maintainability.

-style={{
-  flex: 1,
-}}
+style={styles.scrollViewContainer}

// Add to stylesFn:
scrollViewContainer: {
  flex: 1,
},

164-166: Potential DRY violation in MultiSlider styling

The same selectedStyle object is repeated three times. Consider extracting it to a constant or style.

+ const sliderSelectedStyle = {
+   backgroundColor: Colors(theme).primary,
+ };

// Then use it in all MultiSlider components:
- selectedStyle={{
-   backgroundColor: Colors(theme).primary,
- }}
+ selectedStyle={sliderSelectedStyle}

Also applies to: 189-191, 216-218


235-247: Consider layout improvements for the Apply button

The current implementation has hardcoded padding values. Consider using the theme's spacing system for consistency.

-style={{
-  width: "100%",
-  padding: 20,
-}}
+style={styles.applyButtonContainer}

// Add to stylesFn:
applyButtonContainer: {
  width: '100%',
  padding: theme.spacing.large, // Assuming your theme has a spacing system
},

Line range hint 171-182: Fix incorrect range display

The Reach section is displaying follower range values instead of reach range values.

 <Text style={styles.salaryLabel}>
-  Min Reach: {localFollowersRange[0].toLocaleString() || "0"}
+  Min Reach: {localReachRange[0].toLocaleString() || "0"}
 </Text>
 // ...
 <Text style={styles.salaryLabel}>
-  Max Reach: {localFollowersRange[1].toLocaleString() || "0"}
+  Max Reach: {localReachRange[1].toLocaleString() || "0"}
 </Text>
hooks/request/use-influencers.tsx (5)

12-12: Define a specific type for the influencers state

The influencers state variable is currently typed as any[]. Defining a specific TypeScript interface for your influencer data will enhance type safety and improve code maintainability.


13-13: Specify a more precise type for lastVisible

The lastVisible state variable is typed as DocumentData | null. Consider specifying a more precise type, such as QueryDocumentSnapshot<Influencer> | null, where Influencer is your defined interface for influencer data.


20-48: Reassess the need for real-time listeners in fetchInitialInfluencers

The fetchInitialInfluencers function uses onSnapshot, setting up a real-time listener. If real-time updates aren't necessary for the initial data load, consider using getDocs to fetch the data once. This can reduce resource usage and prevent unnecessary listeners.

Apply this diff to modify fetchInitialInfluencers:

- const unsubscribe = onSnapshot(
-   influencersQuery,
-   (snapshot: QuerySnapshot) => {
-     const data = snapshot.docs.map(doc => ({
-       ...doc.data(),
-       id: doc.id,
-     }));
-     setInfluencers(data);
-     setLastVisible(snapshot.docs[snapshot.docs.length - 1]);
-     setHasMore(snapshot.docs.length === PAGE_SIZE);
-     setIsInitialLoading(false);
-   },
-   error => {
-     console.error("Error fetching influencers:", error);
-     setIsInitialLoading(false);
-   }
- );
-
- return unsubscribe;

+ getDocs(influencersQuery)
+   .then(snapshot => {
+     const data = snapshot.docs.map(doc => ({
+       ...doc.data(),
+       id: doc.id,
+     }));
+     setInfluencers(data);
+     setLastVisible(snapshot.docs[snapshot.docs.length - 1]);
+     setHasMore(snapshot.docs.length === PAGE_SIZE);
+   })
+   .catch(error => {
+     console.error("Error fetching influencers:", error);
+   })
+   .finally(() => {
+     setIsInitialLoading(false);
+   });

Note: Since getDocs doesn't return an unsubscribe function, adjust the cleanup function in useEffect accordingly.


91-96: Optimize the query in checkIfAlreadyInvited

To enhance performance, especially with large datasets, add a limit(1) to the query in checkIfAlreadyInvited. This stops the search after finding the first matching document.

Apply this diff:

 const influencersQuery = query(
   invitationsRef,
   where("userId", "==", influencerID),
+  limit(1)
 );

106-115: Limit the returned values to necessary state and functions

Consider returning only the necessary state variables and functions from your hook. This enhances encapsulation and reduces the risk of unintended side effects.

components/collaboration/collaboration-details/InvitationsTabContent.tsx (6)

Line range hint 45-72: Add null checks for selectedInfluencer and AuthApp.currentUser

Ensure that selectedInfluencer and AuthApp.currentUser are not null before accessing their properties to prevent potential runtime errors.

Apply this diff to include null checks:

const handleCollaborationInvite = async () => {
  try {
    setIsInviting(true);
+   if (!selectedInfluencer || !AuthApp.currentUser) {
+     Toaster.error("Required data is missing. Please try again.");
+     setIsInviting(false);
+     return;
+   }
    const invitationRef = collection(
      FirestoreDB,
      "collaborations",
      props.pageID,
      "invitations"
    );

Line range hint 61-64: Simplify async function by removing unnecessary .then() after await

Using .then() after await is redundant and can be replaced with sequential code for better readability.

Modify the code as follows:

-   await addDoc(invitationRef, invitationPayload).then(() => {
-     setIsMessageModalVisible(false);
-     Toaster.success("Invitation sent successfully");
-   });
+   await addDoc(invitationRef, invitationPayload);
+   setIsMessageModalVisible(false);
+   Toaster.success("Invitation sent successfully");

99-99: Remove unnecessary semicolon after the closing brace

The semicolon after the closing brace of the if statement is unnecessary and can be removed to maintain code consistency.

Apply this diff:

    />
-   );
- };
+   )
+ }

Line range hint 116-132: Retrieve influencer details from data instead of hardcoded values

Using actual data from item ensures that displayed information is accurate and enhances user experience.

Update the code to fetch real data:

             influencer={{
               id: item.id,
               profilePic: item.profileImage,
               name: item.name,
               handle: item.handle || "@handle",
               media: [
                 {
                   type: "image",
                   uri: item.profileImage,
                 },
               ],
               followers: item.backend ? item.backend.followers : 1000,
               reach: item.backend ? item.backend.reach : 10000,
               rating: item.backend ? item.backend.rating : 4.5,
-              bio: "I am a content creator",
-              jobsCompleted: 12,
-              successRate: 100,
+              bio: item.bio || "No bio available",
+              jobsCompleted: item.jobsCompleted || 0,
+              successRate: item.successRate || 0,
             }}

187-191: Disable 'Send Invitation' button when message is empty

To prevent sending empty messages, disable the 'Send Invitation' button when the message input is empty or only contains whitespace.

Apply this change to the <Button> component:

<Button
  mode="contained"
  onPress={handleCollaborationInvite}
  loading={isInviting}
+ disabled={isInviting || !message.trim()}
>
  Send Invitation
</Button>

105-109: Avoid clearing influencers list before fetching new data on refresh

Clearing the influencers array before fetching can cause unnecessary UI flickering. Consider setting a loading state or relying on the refreshing prop.

Apply this diff:

onRefresh={() => {
- setInfluencers([]);
  fetchInitialInfluencers();
}}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between fb1b71e and f78cd27.

📒 Files selected for processing (35)
  • app/(main)/(drawer)/(tabs)/_layout.tsx (6 hunks)
  • components/FilterModal.tsx (6 hunks)
  • components/InfluencerCard.tsx (7 hunks)
  • components/SearchComponent.tsx (3 hunks)
  • components/channel/channel-list.tsx (1 hunks)
  • components/collaboration/CollaborationCard.tsx (3 hunks)
  • components/collaboration/CollaborationHeader.tsx (1 hunks)
  • components/collaboration/collaboration-details/ApplicationsTabContent.tsx (5 hunks)
  • components/collaboration/collaboration-details/InvitationsTabContent.tsx (5 hunks)
  • components/collaboration/collaboration-details/OverviewTabContent.tsx (1 hunks)
  • components/collaboration/collaboration-details/SettingsTabContent.tsx (1 hunks)
  • components/collaboration/collaboration-details/index.tsx (1 hunks)
  • components/collaborations/index.tsx (1 hunks)
  • components/drawer-layout/DrawerMenuContent.tsx (3 hunks)
  • components/drawer-layout/DrawerMenuItem.tsx (2 hunks)
  • components/explore-influencers/index.tsx (1 hunks)
  • components/notifications/notification-icon.tsx (2 hunks)
  • components/proposals/Applications.tsx (3 hunks)
  • components/proposals/Invitations.tsx (2 hunks)
  • components/ui/brand-switcher/index.tsx (2 hunks)
  • components/ui/empty-state/index.tsx (2 hunks)
  • components/ui/menu-icon/index.tsx (2 hunks)
  • components/ui/tag/index.tsx (1 hunks)
  • components/ui/top-tab-navigation/index.tsx (1 hunks)
  • constants/Colors.ts (3 hunks)
  • constants/Influencers.ts (2 hunks)
  • hooks/request/index.ts (1 hunks)
  • hooks/request/use-influencers.tsx (1 hunks)
  • styles/CollaborationCard.styles.tsx (3 hunks)
  • styles/FilterModal.styles.tsx (1 hunks)
  • styles/InfluencerCard.styles.tsx (3 hunks)
  • styles/collaboration-details/CollaborationDetails.styles.tsx (4 hunks)
  • styles/collaboration-details/CollaborationHeader.styles.tsx (1 hunks)
  • styles/tag/Tag.styles.tsx (1 hunks)
  • styles/top-tab-navigation/TopTabNavigation.styles.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • components/collaboration/collaboration-details/SettingsTabContent.tsx
  • hooks/request/index.ts
🚧 Files skipped from review as they are similar to previous changes (9)
  • components/SearchComponent.tsx
  • components/channel/channel-list.tsx
  • components/collaboration/CollaborationCard.tsx
  • components/drawer-layout/DrawerMenuItem.tsx
  • components/explore-influencers/index.tsx
  • components/proposals/Applications.tsx
  • components/proposals/Invitations.tsx
  • styles/CollaborationCard.styles.tsx
  • styles/InfluencerCard.styles.tsx
🧰 Additional context used
🪛 Biome
components/drawer-layout/DrawerMenuContent.tsx

[error] 25-26: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

hooks/request/use-influencers.tsx

[error] 103-103: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (35)
styles/tag/Tag.styles.tsx (2)

1-3: LGTM! Imports are well-organized.

The imports are properly structured, using appropriate type definitions and following good practices with module aliasing.


7-7: Verify theme color implementation.

Let's ensure the Colors utility properly handles the theme color for tags.

✅ Verification successful

Theme color implementation for tags is properly handled

The Colors utility correctly implements tag colors for both light and dark themes:

  • Dark theme: tag: "#5f6368", tagForeground: "#f1f3f4"
  • Light theme: tag: "#f1f3f4", tagForeground: "#5f6368"
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if Colors implementation properly handles the tag color
ast-grep --pattern 'const Colors = $_theme => ({
  $$$
  tag: $_,
  $$$
})'

# Check for any direct usage of the tag color
rg "Colors\(.*\)\.tag"

Length of output: 234


Script:

#!/bin/bash
# Let's check the Colors implementation to see how tag colors are defined
fd "Colors" --exec cat {}

# Also check for any theme-related files that might define these colors
fd "theme" --type f --exec cat {}

Length of output: 6230

components/collaborations/index.tsx (1)

29-31: Verify TopTabNavigation implementation

Let's ensure the new TopTabNavigation component properly handles edge cases and provides appropriate feedback.

styles/collaboration-details/CollaborationHeader.styles.tsx (1)

1-4: LGTM! Clean and well-organized imports.

The imports follow a good pattern with external dependencies first, followed by internal ones.

components/ui/menu-icon/index.tsx (1)

23-26: LGTM! Icon implementation looks good

The switch to FontAwesomeIcon is implemented correctly with proper color, size, and icon props. The size adjustment to 24 helps maintain consistency with other icons in the application.

styles/FilterModal.styles.tsx (1)

9-10: LGTM! Consider verifying spacing consistency.

The split into paddingHorizontal and paddingVertical provides better spacing control. The values appear to follow a consistent scale.

Let's verify if these spacing values are consistent across other modal components:

✅ Verification successful

Padding values align with design system patterns

The verification shows that these padding values are consistently used across the codebase:

  • paddingVertical: 16 is used in multiple modal components (UploadModal, create-collaboration)
  • paddingHorizontal: 20 appears in several layout components (MenuItem, tab1)

This confirms that the changes follow established spacing patterns in the design system.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check padding values in other modal style files
# Expected: Find similar padding patterns in other modal components

# Search for modal style files and their padding definitions
rg -l "Modal.styles" | xargs rg "padding(Horizontal|Vertical)?"

# Check for any other components using these specific padding values
rg "(paddingHorizontal: 20|paddingVertical: 16)"

Length of output: 972

styles/top-tab-navigation/TopTabNavigation.styles.tsx (2)

1-4: LGTM! Well-structured imports with proper type safety.

The imports are well-organized and include necessary type definitions for theme support.


1-46: Verify navigation integration and usage

Let's ensure this component is properly integrated with the navigation system and follows the expected usage patterns.

✅ Verification successful

Navigation integration is properly implemented

The component is correctly integrated and used within the application:

  • Properly imported and used in collaboration-related components (components/collaborations/index.tsx and components/collaboration/collaboration-details/index.tsx)
  • Uses correct navigation dependencies (@react-navigation/native is installed)
  • Follows React Navigation theming patterns through the Theme type usage
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for usage of TopTabNavigation component
rg -t tsx -t ts "TopTabNavigation" -A 5

# Check for other similar navigation patterns
ast-grep --pattern 'createMaterialTopTabNavigator()' -l

Length of output: 231


Script:

#!/bin/bash
# Search for usage of TopTabNavigation component with correct file extensions
rg "TopTabNavigation" "*.tsx" "*.ts" -A 5

# Search for files containing TopTabNavigation in their name
fd "TopTabNavigation"

# Check for navigation-related imports and setup
rg "@react-navigation/native" "*.tsx" "*.ts" -A 3

# Check for material top tab navigator usage with proper ast-grep syntax
ast-grep --pattern 'createMaterialTopTabNavigator()' --lang typescript

Length of output: 422


Script:

#!/bin/bash
# Search for TopTabNavigation usage in the entire codebase
rg -t typescript -t tsx "TopTabNavigation"

# Look for navigation setup and imports
rg -t typescript -t tsx "@react-navigation"

# Find any files that might be using this component
rg -t typescript -t tsx "import.*TopTabNavigation"

# Check the component's directory for related files
ls -la styles/top-tab-navigation/

Length of output: 468


Script:

#!/bin/bash
# Search for any file content using ripgrep without file type restrictions
rg "TopTabNavigation"

# Look for the actual component implementation
rg -l "export.*TopTabNavigation"

# Check content of any navigation setup files
fd "navigation" --type f --exec cat {}

# Check package.json for navigation dependencies
cat package.json | grep -A 5 -B 5 "navigation"

Length of output: 2410

constants/Colors.ts (2)

16-17: Color contrast issue in dark theme persists

The previously identified accessibility concern about identical primary and onSurface colors remains unaddressed.


28-31: Verify light theme color contrast ratios

Similar to the dark theme, setting both primary and onSurface to #054463 may cause accessibility issues. Additionally, verify that the tag colors provide sufficient contrast.

Run this script to check color contrast ratios:

✅ Verification successful

Light theme color contrast ratios meet WCAG guidelines

The contrast ratios for the light theme colors are within acceptable ranges according to WCAG 2.0 guidelines:

  • Primary (#054463) vs Background (#ffffff): 10.43:1 (exceeds AA and AAA requirements)
  • Tag (#f1f3f4) vs TagForeground (#5f6368): 5.44:1 (exceeds AA requirements)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Calculate color contrast ratios using WCAG 2.0 guidelines
# Note: This is a simplified check. Use tools like WebAIM for production.

cat << 'EOF' > check_contrast.js
function luminance(r, g, b) {
    let [rs, gs, bs] = [r, g, b].map(c => {
        c = c / 255;
        return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
    });
    return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
}

function contrast(color1, color2) {
    let hex2rgb = hex => [
        parseInt(hex.slice(1,3), 16),
        parseInt(hex.slice(3,5), 16),
        parseInt(hex.slice(5,7), 16)
    ];
    
    let [l1, l2] = [color1, color2].map(c => luminance(...hex2rgb(c)));
    let ratio = (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05);
    return ratio.toFixed(2);
}

// Check light theme contrasts
console.log('Light theme contrasts:');
console.log('primary vs background:', contrast('#054463', '#ffffff'));
console.log('tag vs tagForeground:', contrast('#f1f3f4', '#5f6368'));
EOF

node check_contrast.js

Length of output: 109

components/notifications/notification-icon.tsx (2)

Line range hint 1-58: Overall implementation looks great!

The NotificationIcon component is well-implemented with:

  • Proper type safety
  • Consistent theming
  • Good accessibility with Pressable feedback
  • Clean notification badge implementation

43-46: Verify icon size consistency across the app

The icon implementation has been correctly migrated to FontAwesomeIcon with proper color and opacity handling. However, the size change from 25 to 24 should be verified for consistency.

Let's check if the icon size is consistent across other components:

✅ Verification successful

Icon size is consistent with app-wide patterns

The size change from 25 to 24 pixels is consistent with the codebase patterns. The search results show that:

  • Most UI icons, including navigation, filters, and action buttons, consistently use size={24}
  • Tab bar icons use either 24px or 28px (one instance)
  • Smaller icons in stat displays use 18-20px for visual hierarchy

The notification bell icon at 24px aligns perfectly with these established patterns.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for FontAwesomeIcon size props across the codebase
ast-grep --pattern 'FontAwesomeIcon $$$size={$_}$$$'

Length of output: 54


Script:

#!/bin/bash
# Let's try a different pattern to search for FontAwesomeIcon with size attribute
rg -U "FontAwesomeIcon.*size.*" -A 1 -B 1

Length of output: 43


Script:

#!/bin/bash
# Let's search for any FontAwesomeIcon usage in the codebase
rg "<FontAwesomeIcon" -A 3 -B 1

Length of output: 6167

components/ui/empty-state/index.tsx (3)

34-40: Well-structured action handling implementation!

The handleAction function provides a clean way to handle both custom actions and default navigation behavior.


44-47: Clean style composition implementation!

The style composition using array syntax allows for proper style overrides while maintaining default styles.


52-52: Verify imageUrl utility handling

Let's ensure the imageUrl utility properly handles both string and NodeRequire image sources.

✅ Verification successful

imageUrl utility implementation is correct and safe

The imageUrl utility in utils/url.ts properly handles all image source types:

  • HTTP URLs (returns { uri: image })
  • Local requires (returns as-is)
  • Undefined/empty cases (returns default placeholder image)

The usage in empty-state/index.tsx and other components is consistent with this implementation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check imageUrl utility implementation
ast-grep --pattern 'export function imageUrl($_) {
  $$$
}'

# Check other usages of imageUrl
rg -l 'imageUrl\(' --type typescript

Length of output: 135


Script:

#!/bin/bash
# Search for imageUrl function definition with broader file types
ast-grep --pattern 'function imageUrl($_) {
  $$$
}'

# Search for imageUrl usage with common extensions
rg -l 'imageUrl\(' -g '*.{ts,tsx,js,jsx}'

# Search for files containing imageUrl in their name
fd imageUrl

Length of output: 227


Script:

#!/bin/bash
# Let's check the actual implementation and usage context
rg 'imageUrl' -A 5 -B 5 components/InfluencerCard.tsx components/ui/empty-state/index.tsx components/collaboration/CollaborationHeader.tsx

Length of output: 5070


Script:

#!/bin/bash
# Let's check the implementation of imageUrl in utils/url
rg -g 'url.{ts,tsx,js,jsx}' -A 10 'export.*imageUrl'

Length of output: 495

components/collaboration/CollaborationHeader.tsx (1)

16-17: LGTM! Proper theme implementation

Good use of React Navigation's theming system with externalized styles.

components/ui/brand-switcher/index.tsx (1)

4-5: LGTM: Icon library transition looks good

The transition from Octicons to FontAwesome is a solid choice, as FontAwesome provides a comprehensive set of icons with good React Native support.

styles/collaboration-details/CollaborationDetails.styles.tsx (4)

85-85: LGTM: Consistent theme usage

Good change - using the theme's border color maintains consistency with the theming system.


1-4: Verify migration of removed styles

The AI summary indicates that profileCard, profileContent, name, and statChip styles were removed. Please ensure these styles have been properly migrated or are no longer needed.

✅ Verification successful

The removed styles have been properly migrated and refactored

The verification shows that:

  • The file CollaborationDetails.styles.tsx is a new file with a complete set of styles
  • The mentioned styles (profileCard, profileContent, name, statChip) were replaced with a more comprehensive set of styles including contentContainer, profileImage, brandName, and statsContainer that serve similar purposes
  • The new styles are properly organized and follow a consistent theme-based approach using the stylesFn pattern
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining references to the removed styles
rg -l "profileCard|profileContent|name|statChip" --type tsx

# Check for new style definitions that might have replaced these
fd . -e tsx -e ts --exec grep -l "StyleSheet.create"

Length of output: 1225


Script:

#!/bin/bash
# Search for the removed style names in the repository with proper file extension
rg -l "profileCard|profileContent|name|statChip" --type-add 'tsx:*.tsx' --type tsx

# Check the specific file for style definitions before and after the change
git show HEAD -- 'styles/collaboration-details/CollaborationDetails.styles.tsx'

# Look for potential style migrations in related files
rg -l "CollaborationDetails" --type-add 'tsx:*.tsx' --type tsx

Length of output: 4691


50-53: Consider layout implications and shadow removal

Two potential concerns:

  1. Setting minWidth: "100%" might cause overflow in constrained containers
  2. Using transparent for shadowColor effectively removes the card's shadow. Is this intentional as part of the theme update?
✅ Verification successful

Transparent shadows are consistently used across card components

The changes are consistent with the codebase's design patterns:

  • All card components (InfluencerCard, CollaborationCard, and this CollaborationDetails) use Colors(theme).transparent for shadowColor
  • Only modals and tab components have visible shadows with defined shadowOffset, shadowOpacity, and shadowRadius
  • The minWidth: "100%" is safe as it's contained within the card's parent container
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for other card components that might need consistent shadow treatment
ast-grep --pattern 'shadowColor: $_'

Length of output: 38


Script:

#!/bin/bash
# Search for shadow-related styles using ripgrep for broader coverage
rg "shadow" --type tsx --type ts -A 2 -B 2

Length of output: 70


Script:

#!/bin/bash
# Search for shadow-related styles with correct file extensions
rg "shadow" -g "*.ts" -g "*.tsx" -A 2 -B 2

# Also check for any other card styles that might be relevant
rg "card" -g "*.styles.ts*" -A 2 -B 2

Length of output: 5686


23-26: Verify layout impact of increased profileImage height

The profileImage height has been significantly increased (160px) and styling changed to only round the top corners. Please ensure this doesn't cause any layout issues on smaller devices or in different orientations.

✅ Verification successful

Height change in profileImage appears safe to implement

The codebase scan reveals:

  • The app uses responsive dimensions in several places (Dimensions.get("window")) showing awareness of screen adaptability
  • The height:160 is only used in CollaborationDetails.styles.tsx and isn't conflicting with other components
  • Similar height:100 values are used across multiple style files, indicating this is a common pattern
  • The change appears to be an intentional design update specific to this component

The increased height and rounded corners are unlikely to cause layout issues since:

  1. The app already handles different screen sizes via Dimensions API
  2. This is an isolated style change in a specific component
  3. The change follows existing styling patterns in the codebase
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any responsive design adjustments for different screen sizes
rg -l "Dimensions.get\('window'\)" --type tsx

Length of output: 81


Script:

#!/bin/bash
# Search for any responsive design adjustments or screen dimensions, trying different file extensions
rg -l "Dimensions.get\('window'\)" --type ts --type jsx --type tsx --type js

# Look for any existing usage of these style properties in the codebase
rg "height:\s*(160|100)" --type ts --type jsx --type tsx --type js

# Check if there are any media queries or responsive styles
rg "StyleSheet.create.*@media|responsive" --type ts --type jsx --type tsx --type js

Length of output: 323


Script:

#!/bin/bash
# Search for any responsive design adjustments or screen dimensions
rg "Dimensions.get"

# Look for any existing usage of these style properties in the codebase
rg "height:\s*(160|100)"

# Check if there are any media queries or responsive styles
rg "StyleSheet.create.*@media|responsive"

# Find all files that might contain React Native styles
fd -e ts -e tsx -e js -e jsx

Length of output: 6718

components/collaboration/collaboration-details/index.tsx (3)

1-22: LGTM! Clean imports and well-defined interfaces.

The imports are properly organized and the interfaces are well-structured, extending the base ICollaboration interface appropriately.


32-55: Previous comment about error handling improvements is still applicable.

The error handling and cleanup concerns raised in the previous review remain valid.


128-130: Verify TopTabNavigation integration.

The component appears to be properly integrated, but let's verify its usage across the codebase.

✅ Verification successful

TopTabNavigation integration is properly implemented

The component is correctly integrated and follows a consistent pattern across the codebase:

  • The component is properly defined in components/ui/top-tab-navigation/index.tsx with correct prop types
  • It's used consistently in both components/collaborations/index.tsx and components/collaboration/collaboration-details/index.tsx
  • The tabs prop is properly passed in all usage locations
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify TopTabNavigation usage patterns
# Check for consistent props usage across the codebase

# Search for TopTabNavigation imports and usage
rg -A 3 "import.*TopTabNavigation" --type typescript --type tsx

# Search for tabs prop structure
ast-grep --pattern 'tabs={$_}'

Length of output: 133


Script:

#!/bin/bash
# Let's try a different approach to find TopTabNavigation usage

# Search for TopTabNavigation component usage
rg "TopTabNavigation" -A 3

# Search for files containing TopTabNavigation
fd -e tsx -e ts -x grep -l "TopTabNavigation" {}

# Look for any tabs prop usage in tsx files
rg "tabs=" -A 2 --type-add 'tsx:*.tsx' --type tsx

Length of output: 2660

components/collaboration/collaboration-details/ApplicationsTabContent.tsx (2)

2-2: LGTM! Clean import statements and improved component naming.

The component rename from CollaborationApplicationPage to ApplicationsTabContent better reflects its purpose as a tab content component, and unused imports have been properly removed.

Also applies to: 13-13, 15-15


146-146: LGTM! Export statement matches component name.

The default export properly reflects the renamed component.

app/(main)/(drawer)/(tabs)/_layout.tsx (1)

38-38: LGTM: Improved tab bar spacing

The added horizontal padding enhances the visual spacing of the tab bar items.

components/drawer-layout/DrawerMenuContent.tsx (4)

16-24: LGTM! Well-organized icon imports.

The FontAwesome icons are properly organized between regular and solid variants.


25-26: Empty interface issue remains unaddressed.

The previous review comment about replacing the empty interface with a type alias is still applicable.

🧰 Tools
🪛 Biome

[error] 25-26: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)


31-51: LGTM! Improved type safety with FontAwesome icons.

The replacement of string-based icons with FontAwesome icon objects improves type safety and maintainability.


112-119: LGTM! Consistent theme-aware styling.

The styling changes properly utilize the theme system and maintain visual consistency.

Also applies to: 132-136

components/InfluencerCard.tsx (2)

27-31: LGTM: Well-structured type definitions and imports

The changes to the interface and imports are well-organized. The transition to async invitation checking via a function prop is a good practice for handling remote state.

Also applies to: 37-37, 53-53


171-213: ⚠️ Potential issue

Add onPress handler for message icon

The message icon is still missing an onPress handler, as noted in the previous review.

 <View style={styles.statItem}>
   <FontAwesomeIcon
     icon={faMessage}
     color={Colors(theme).primary}
     size={18}
+    onPress={() => {
+      if (props.ToggleMessageModal) {
+        props.ToggleMessageModal();
+      }
+    }}
   />
 </View>
components/FilterModal.tsx (2)

10-12: LGTM: Icon library migration

The switch to FontAwesome icons aligns with the standardization effort mentioned in the summary.


101-106: LGTM: Theme-aware BottomSheet styling

Good addition of theme-aware styling for the handle and indicator, improving visual consistency.

Comment thread styles/tag/Tag.styles.tsx
Comment on lines +5 to +11
export const stylesFn = (theme: Theme) => StyleSheet.create({
tag: {
backgroundColor: Colors(theme).tag,
paddingVertical: 0,
paddingHorizontal: 0,
borderRadius: 10,
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Review padding values and consider using design system constants.

  1. Setting both paddings to 0 will remove all internal spacing. Consider adding appropriate padding for better content spacing.
  2. The borderRadius value should ideally come from a design system constant for consistency.
 tag: {
   backgroundColor: Colors(theme).tag,
-  paddingVertical: 0,
-  paddingHorizontal: 0,
+  paddingVertical: 4,
+  paddingHorizontal: 8,
   borderRadius: 10,
 },
📝 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
export const stylesFn = (theme: Theme) => StyleSheet.create({
tag: {
backgroundColor: Colors(theme).tag,
paddingVertical: 0,
paddingHorizontal: 0,
borderRadius: 10,
},
export const stylesFn = (theme: Theme) => StyleSheet.create({
tag: {
backgroundColor: Colors(theme).tag,
paddingVertical: 4,
paddingHorizontal: 8,
borderRadius: 10,
},

Comment on lines +5 to +8
const Tag: React.FC<ChipProps> = ({
icon,
children,
}) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider forwarding all ChipProps to maintain component flexibility

The component currently only destructures icon and children from ChipProps, but doesn't forward other potentially useful Chip props like onPress, disabled, etc. This limits the component's reusability.

-const Tag: React.FC<ChipProps> = ({
-  icon,
-  children,
-}) => {
+const Tag: React.FC<ChipProps> = ({ 
+  icon, 
+  children, 
+  ...chipProps 
+}) => {

And in the render:

   <Chip
     icon={icon}
     style={styles.tag}
     textStyle={styles.tagText}
+    {...chipProps}
   >

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

Comment on lines +13 to +20
<Chip
icon={icon}
style={styles.tag}
textStyle={styles.tagText}
>
{children}
</Chip>
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add prop types validation and error handling

Since this component is used for displaying status tags in CollaborationCard, it should handle edge cases gracefully.

+import { Text } from 'react-native';

 const Tag: React.FC<ChipProps> = ({ icon, children, ...chipProps }) => {
   const theme = useTheme();
   const styles = useMemo(() => stylesFn(theme), [theme]);
+
+  if (!children) {
+    return null;
+  }
+
+  if (typeof children !== 'string') {
+    console.warn('Tag component expects string children');
+    return <Text style={styles.tagText}>{String(children)}</Text>;
+  }

   return (
     <Chip
📝 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
<Chip
icon={icon}
style={styles.tag}
textStyle={styles.tagText}
>
{children}
</Chip>
);
import { Text } from 'react-native';
const Tag: React.FC<ChipProps> = ({ icon, children, ...chipProps }) => {
const theme = useTheme();
const styles = useMemo(() => stylesFn(theme), [theme]);
if (!children) {
return null;
}
if (typeof children !== 'string') {
console.warn('Tag component expects string children');
return <Text style={styles.tagText}>{String(children)}</Text>;
}
return (
<Chip
icon={icon}
style={styles.tag}
textStyle={styles.tagText}
>
{children}
</Chip>
);
}

Comment on lines +7 to +18
const tabs = [
{
id: "Active",
title: "Active",
component: <Applications />,
},
{
id: "Past",
title: "Past",
component: <Invitations />,
},
];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve tab configuration for better maintainability and performance

The tab configuration has several areas for improvement:

  1. Tab IDs and components don't align semantically (e.g., "Active" renders Applications)
  2. Components are instantiated directly in the configuration

Consider applying these changes:

 const tabs = [
   {
-    id: "Active",
-    title: "Active",
-    component: <Applications />,
+    id: "applications",
+    title: "Applications",
+    component: Applications,
   },
   {
-    id: "Past",
-    title: "Past",
-    component: <Invitations />,
+    id: "invitations",
+    title: "Invitations",
+    component: Invitations,
   },
 ];

This change:

  • Aligns tab IDs with their actual content
  • Passes component references instead of instances to prevent unnecessary re-renders
📝 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
const tabs = [
{
id: "Active",
title: "Active",
component: <Applications />,
},
{
id: "Past",
title: "Past",
component: <Invitations />,
},
];
const tabs = [
{
id: "applications",
title: "Applications",
component: Applications,
},
{
id: "invitations",
title: "Invitations",
component: Invitations,
},
];

Comment on lines +5 to +10
export const stylesFn = (theme: Theme) => StyleSheet.create({
container: {
paddingHorizontal: 16,
paddingBottom: 16,
maxWidth: 600,
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codebase verification

Consider using responsive width handling for better adaptability

The codebase shows evidence of using Dimensions.get("window") for responsive layouts in multiple components, but the CollaborationHeader uses a fixed maxWidth. For consistency with the codebase's responsive approach:

  • Consider replacing the fixed maxWidth: 600 with a responsive value using Dimensions.get("window").width
  • Other components in the codebase (InfluencerCard, carousel) already implement this responsive pattern
  • The only other fixed maxWidth found is 256 in EmptyState.styles.tsx, which is a more conservative value
🔗 Analysis chain

Verify the maxWidth constraint across different screen sizes.

The fixed maxWidth of 600 might need adjustment for different device sizes. Consider making it responsive or verifying its behavior on smaller screens.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other maxWidth constraints in the codebase for consistency
rg "maxWidth:\s*600" --type tsx --type ts

# Look for responsive width handling patterns
rg "Dimensions.get\('window'\)" --type tsx --type ts

Length of output: 160


Script:

#!/bin/bash
# Search for maxWidth constraints without file type restrictions
rg "maxWidth:\s*600"

# Look for responsive width handling patterns
rg "Dimensions\.get"

# Look for other maxWidth patterns to understand the common approaches
rg "maxWidth:"

Length of output: 530


useEffect(() => {
const unsubscribe = fetchInitialInfluencers();
return () => unsubscribe && unsubscribe();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Simplify the cleanup function using optional chaining

You can simplify the cleanup function in useEffect by using optional chaining. This makes the code cleaner and addresses the static analysis hint.

Apply this diff:

-     return () => unsubscribe && unsubscribe();
+     return () => unsubscribe?.();
📝 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
return () => unsubscribe && unsubscribe();
return () => unsubscribe?.();
🧰 Tools
🪛 Biome

[error] 103-103: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

Comment on lines +50 to +81
const fetchMoreInfluencers = async () => {
if (!lastVisible || !hasMore || isLoading) return;

setIsLoading(true);
const influencerRef = collection(FirestoreDB, "users");
const influencersQuery = query(
influencerRef,
orderBy("name"),
startAfter(lastVisible),
limit(PAGE_SIZE)
);

const unsubscribe = onSnapshot(
influencersQuery,
(snapshot: QuerySnapshot) => {
const data = snapshot.docs.map(doc => ({
...doc.data(),
id: doc.id,
}));
setInfluencers(prev => [...prev, ...data]);
setLastVisible(snapshot.docs[snapshot.docs.length - 1]);
setHasMore(snapshot.docs.length === PAGE_SIZE);
setIsLoading(false);
},
error => {
console.error("Error fetching more influencers:", error);
setIsLoading(false);
}
);

return unsubscribe;
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid unnecessary real-time listeners in fetchMoreInfluencers

The fetchMoreInfluencers function sets up a real-time listener with onSnapshot. If real-time updates aren't required when loading more data, use getDocs instead to fetch data once. This prevents potential memory leaks from untracked listeners and improves performance.

Apply this diff to modify fetchMoreInfluencers:

- const unsubscribe = onSnapshot(
-   influencersQuery,
-   (snapshot: QuerySnapshot) => {
-     const data = snapshot.docs.map(doc => ({
-       ...doc.data(),
-       id: doc.id,
-     }));
-     setInfluencers(prev => [...prev, ...data]);
-     setLastVisible(snapshot.docs[snapshot.docs.length - 1]);
-     setHasMore(snapshot.docs.length === PAGE_SIZE);
-     setIsLoading(false);
-   },
-   error => {
-     console.error("Error fetching more influencers:", error);
-     setIsLoading(false);
-   }
- );
-
- return unsubscribe;

+ try {
+   const snapshot = await getDocs(influencersQuery);
+   const data = snapshot.docs.map(doc => ({
+     ...doc.data(),
+     id: doc.id,
+   }));
+   setInfluencers(prev => [...prev, ...data]);
+   setLastVisible(snapshot.docs[snapshot.docs.length - 1]);
+   setHasMore(snapshot.docs.length === PAGE_SIZE);
+ } catch (error) {
+   console.error("Error fetching more influencers:", error);
+ } finally {
+   setIsLoading(false);
+ }
📝 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
const fetchMoreInfluencers = async () => {
if (!lastVisible || !hasMore || isLoading) return;
setIsLoading(true);
const influencerRef = collection(FirestoreDB, "users");
const influencersQuery = query(
influencerRef,
orderBy("name"),
startAfter(lastVisible),
limit(PAGE_SIZE)
);
const unsubscribe = onSnapshot(
influencersQuery,
(snapshot: QuerySnapshot) => {
const data = snapshot.docs.map(doc => ({
...doc.data(),
id: doc.id,
}));
setInfluencers(prev => [...prev, ...data]);
setLastVisible(snapshot.docs[snapshot.docs.length - 1]);
setHasMore(snapshot.docs.length === PAGE_SIZE);
setIsLoading(false);
},
error => {
console.error("Error fetching more influencers:", error);
setIsLoading(false);
}
);
return unsubscribe;
};
const fetchMoreInfluencers = async () => {
if (!lastVisible || !hasMore || isLoading) return;
setIsLoading(true);
const influencerRef = collection(FirestoreDB, "users");
const influencersQuery = query(
influencerRef,
orderBy("name"),
startAfter(lastVisible),
limit(PAGE_SIZE)
);
try {
const snapshot = await getDocs(influencersQuery);
const data = snapshot.docs.map(doc => ({
...doc.data(),
id: doc.id,
}));
setInfluencers(prev => [...prev, ...data]);
setLastVisible(snapshot.docs[snapshot.docs.length - 1]);
setHasMore(snapshot.docs.length === PAGE_SIZE);
} catch (error) {
console.error("Error fetching more influencers:", error);
} finally {
setIsLoading(false);
}
};

const [isVisible, setIsVisible] = useState(false);
const [influencers, setInfluencers] = useState<any>([]);
const [loading, setLoading] = useState(true);
const [selectedInfluencer, setSelectedInfluencer] = useState<any>(null);

ghost Nov 20, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Define a specific type for selectedInfluencer instead of using any

Using explicit types for state variables improves code clarity and helps prevent runtime errors due to unintended property accesses.

Consider defining an interface for selectedInfluencer:

interface SelectedInfluencer {
  collaborationID: string;
  influencerID: string;
}

const [selectedInfluencer, setSelectedInfluencer] = useState<SelectedInfluencer | null>(null);

import EmptyState from "@/components/ui/empty-state";

const CollaborationInvitationPage = (props: any) => {
const InvitationsTabContent = (props: any) => {

ghost Nov 20, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use explicit props type instead of any for better type safety

Defining a specific type for props enhances code reliability and takes full advantage of TypeScript's type-checking capabilities.

Apply this diff to define an interface for the component's props:

+interface InvitationsTabContentProps {
+  pageID: string;
+  // Add other props as needed
+}
-
-const InvitationsTabContent = (props: any) => {
+const InvitationsTabContent: React.FC<InvitationsTabContentProps> = (props) => {

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

onClose={() => setIsVisible(false)}
snapPointsRange={["30%", "80%"]}
cardId={selectedInfluencer}
key={selectedInfluencer.collaborationID}

ghost Nov 20, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure selectedInfluencer is valid before using collaborationID as the key

If selectedInfluencer is null, accessing collaborationID could cause a runtime error. Ensure it's valid before using it.

Modify the key prop:

<BottomSheetActions
  cardType="invitationCard"
  isVisible={isVisible}
  onClose={() => setIsVisible(false)}
  snapPointsRange={["30%", "80%"]}
  cardId={selectedInfluencer}
- key={selectedInfluencer.collaborationID}
+ key={selectedInfluencer ? selectedInfluencer.collaborationID : 'default_key'}
/>

Alternatively, ensure selectedInfluencer is always set when isVisible is true.

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

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Outside diff range and nitpick comments (16)
styles/top-tab-navigation/TopTabNavigation.styles.tsx (2)

20-28: Consider removing unnecessary position property.

The position: 'relative' style appears to be unnecessary as there are no absolutely positioned child elements.

   tabContainer: {
     flexDirection: 'row',
     gap: 8,
     paddingHorizontal: 8,
     paddingVertical: 8,
     borderRadius: 15,
     backgroundColor: theme?.dark ? Colors(theme).card : Colors(theme).aliceBlue,
-    position: 'relative',
   },

43-52: Consider using relative dimensions for compact styles.

The compact styles use fixed dimensions which might not scale well across different devices.

   compactTabScroll: {
-    maxHeight: 62,
+    maxHeight: '13%', // or use dynamic calculation
   },
   compactTab: {
     paddingVertical: 10,
     paddingHorizontal: 16,
   },
   compactText: {
-    fontSize: 14,
+    fontSize: Platform.select({
+      ios: 14,
+      android: 12,
+    }),
   },
components/ui/carousel/carousel.tsx (3)

Line range hint 11-14: Improve type safety by replacing any types

The component uses any type for the data array and callback parameters, which bypasses TypeScript's type checking benefits.

Consider defining a proper interface:

interface CarouselItem {
  uri: string;
  type: 'image' | 'video';
  // Add other relevant fields
}

interface CarouselProps {
  data: CarouselItem[];
  onImagePress?: (item: CarouselItem) => void;
}

Line range hint 31-39: Add cleanup for setTimeout and video refs

The component should clean up resources when unmounting to prevent memory leaks.

Add a cleanup effect:

useEffect(() => {
  return () => {
    // Clear any pending timeouts
    clearTimeout(swipingTimeout);
    // Reset video refs
    Object.values(videoRefs.current).forEach((videoRef) => {
      if (videoRef && videoRef.unload) {
        videoRef.unload();
      }
    });
  };
}, []);

Line range hint 73-85: Optimize indicator rendering

The indicator dots mapping could be optimized for better performance and type safety.

Consider these improvements:

<View style={styles.indicatorContainer}>
  {Array.from({ length: data.length }, (_, index) => (
    <View
      key={index}
      style={[
        styles.indicatorDot,
        currentIndex === index && styles.activeDot,
      ]}
    />
  ))}
</View>
components/ui/top-tab-navigation/index.tsx (3)

7-14: Enhance type safety and validation in the interface

Consider strengthening the interface with these improvements:

  1. Add runtime validation for minimum tab length
  2. Make the interface more type-safe with theme types
+import { Theme } from '@react-navigation/native';
+
 interface TopTabNavigationProps {
   tabs: {
     id: string;
     title: string;
     component: React.ReactNode;
   }[];
   size?: "compact" | "default";
+  theme?: Theme;
 }
+
+export const validateTabs = (tabs: TopTabNavigationProps['tabs']) => {
+  if (!Array.isArray(tabs) || tabs.length === 0) {
+    throw new Error('TopTabNavigation requires at least one tab');
+  }
+};

47-54: Extract magic numbers to named constants

The scroll offset (-16) should be extracted to a named constant for better maintainability and clarity.

+const TAB_SCROLL_OFFSET = 16;
+
 const scrollToTab = (index: number) => {
   if (scrollViewRef.current && tabLayout[tabs[index].id]) {
     scrollViewRef.current.scrollTo({
-      x: tabLayout[tabs[index].id].x - 16,
+      x: tabLayout[tabs[index].id].x - TAB_SCROLL_OFFSET,
       animated: true,
     });
   }
 };

16-112: Add error boundary and loading states

Consider enhancing the component's robustness:

  1. Wrap the component with an error boundary
  2. Add loading states for tab transitions
  3. Proper cleanup in effects
+import { ErrorBoundary } from '@/components/ErrorBoundary';
+
 const TopTabNavigation: React.FC<TopTabNavigationProps> = ({
   tabs,
   size = "default",
 }) => {
+  const [isLoading, setIsLoading] = useState(false);
+
+  useEffect(() => {
+    return () => {
+      // Cleanup scroll listener if needed
+      scrollViewRef.current?.removeEventListener('scroll');
+    };
+  }, []);
+
   return (
+    <ErrorBoundary fallback={<Text>Error loading tabs</Text>}>
       {/* ... existing code ... */}
+    </ErrorBoundary>
   );
 };
components/collaboration/collaboration-details/index.tsx (2)

24-31: Consider enhancing loading state management.

While the loading state is initialized correctly, consider adding an error state to handle failed fetches more gracefully.

 const [collaboration, setCollaboration] = useState<Collaboration | undefined>(undefined);
 const [loading, setLoading] = useState(true);
+const [error, setError] = useState<Error | null>(null);

62-66: Remove commented code and clarify logo handling.

The logo handling needs attention:

  1. Remove the commented code
  2. Clarify why an empty string is being passed instead of the actual logo
 <OverviewTabContent
   collaboration={collaboration as Collaboration}
-  // logo={collaboration?.logo}
-  logo={""}
+  logo={collaboration?.logo ?? ""}
 />
components/proposals/Invitations.tsx (2)

135-139: Fix typo in EmptyState subtitle

There's a typo in the subtitle text: "woudl" should be "would".

-            subtitle="Start building your profile today to have better reach. If any brand invites you to collaborate we woudl show it here"
+            subtitle="Start building your profile today to have better reach. If any brand invites you to collaborate we would show it here"

172-180: Consider moving inline styles to stylesFn

The FlatList uses inline styles while the component has a stylesFn utility. Consider moving these styles to maintain consistency.

// In stylesFn
+ flatListStyle: {
+   flexGrow: 1,
+   paddingTop: 8,
+   paddingHorizontal: 16,
+   paddingBottom: 16,
+ },
+ flatListContentStyle: {
+   gap: 16,
+   paddingBottom: 24,
+ },
components/collaboration/collaboration-details/InvitationsTabContent.tsx (3)

34-44: Destructure pageID prop at the component level for better readability

Consider destructuring the pageID prop at the component level instead of passing props object to useInfluencers.

-const InvitationsTabContent = (props: any) => {
+const InvitationsTabContent = ({ pageID }: any) => {
   // ...
   const {
     isInitialLoading,
     checkIfAlreadyInvited,
     fetchInitialInfluencers,
     setInfluencers,
     fetchMoreInfluencers,
     influencers,
     isLoading,
   } = useInfluencers({
-    pageID: props.pageID,
+    pageID,
   });

Line range hint 102-172: Optimize FlatList performance

Consider implementing performance optimizations for the FlatList:

  1. Add getItemLayout for fixed height items
  2. Implement memo for InfluencerCard
  3. Use stable keyExtractor
// Add memo to prevent unnecessary re-renders
const MemoizedInfluencerCard = React.memo(InfluencerCard);

// Add getItemLayout if items have fixed height
const getItemLayout = (data: any, index: number) => ({
  length: ITEM_HEIGHT + 16, // item height + separator
  offset: (ITEM_HEIGHT + 16) * index,
  index,
});

// Use in FlatList
<FlatList
  ...
  renderItem={({ item }) => <MemoizedInfluencerCard ... />}
  getItemLayout={getItemLayout}
  keyExtractor={item => `influencer-${item.id}`}
/>

Line range hint 173-207: Consider extracting MessageModal into a separate component

The message modal logic could be extracted into a reusable component to improve maintainability and reduce the main component's complexity.

interface MessageModalProps {
  visible: boolean;
  onClose: () => void;
  onSubmit: (message: string) => void;
  loading?: boolean;
}

const MessageModal: React.FC<MessageModalProps> = ({
  visible,
  onClose,
  onSubmit,
  loading
}) => {
  const [message, setMessage] = useState("");
  // ... rest of the modal implementation
};
components/InfluencerCard.tsx (1)

215-230: Optimize navigation handler with useCallback

The collaboration history navigation handler should be memoized to prevent unnecessary re-renders.

+ const handleNavigateToHistory = useCallback(() => {
+   return router.push("/(main)/CollaborationHistory");
+ }, []);

  // Then in the JSX:
- onPress={() => {
-   return router.push("/(main)/CollaborationHistory");
- }}
+ onPress={handleNavigateToHistory}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between f78cd27 and e99cd4d.

📒 Files selected for processing (24)
  • app/(auth)/create-new-account.tsx (1 hunks)
  • app/(auth)/forget-password.tsx (0 hunks)
  • app/(auth)/login.tsx (0 hunks)
  • app/(main)/(screens)/CollaborationHistory.tsx (1 hunks)
  • app/(onboarding)/onboarding-your-brand.tsx (5 hunks)
  • components/BottomSheetActions.tsx (7 hunks)
  • components/InfluencerCard.tsx (7 hunks)
  • components/SearchComponent.tsx (3 hunks)
  • components/collaboration/collaboration-details/ApplicationsTabContent.tsx (5 hunks)
  • components/collaboration/collaboration-details/InvitationsTabContent.tsx (5 hunks)
  • components/collaboration/collaboration-details/index.tsx (1 hunks)
  • components/drawer-layout/BrandItem.tsx (2 hunks)
  • components/explore-influencers/index.tsx (3 hunks)
  • components/notifications/index.tsx (2 hunks)
  • components/proposals/Applications.tsx (3 hunks)
  • components/proposals/Invitations.tsx (2 hunks)
  • components/ui/carousel/carousel.tsx (1 hunks)
  • components/ui/carousel/render-media-item.tsx (3 hunks)
  • components/ui/top-tab-navigation/index.tsx (1 hunks)
  • styles/InfluencerCard.styles.tsx (3 hunks)
  • styles/NotificationCard.styles.tsx (1 hunks)
  • styles/brand-item/BrandItem.styles.tsx (1 hunks)
  • styles/onboarding/brand.styles.tsx (1 hunks)
  • styles/top-tab-navigation/TopTabNavigation.styles.tsx (1 hunks)
💤 Files with no reviewable changes (2)
  • app/(auth)/forget-password.tsx
  • app/(auth)/login.tsx
🚧 Files skipped from review as they are similar to previous changes (11)
  • app/(auth)/create-new-account.tsx
  • app/(onboarding)/onboarding-your-brand.tsx
  • components/BottomSheetActions.tsx
  • components/SearchComponent.tsx
  • components/collaboration/collaboration-details/ApplicationsTabContent.tsx
  • components/explore-influencers/index.tsx
  • components/notifications/index.tsx
  • components/proposals/Applications.tsx
  • components/ui/carousel/render-media-item.tsx
  • styles/InfluencerCard.styles.tsx
  • styles/NotificationCard.styles.tsx
🔇 Additional comments (26)
app/(main)/(screens)/CollaborationHistory.tsx (2)

1-3: LGTM! Clean and well-organized imports.

The imports are minimal and follow a logical order.


8-10: LGTM! Clean implementation of the screen header.

The implementation is simple and follows the theme update objectives by using the standardized ScreenHeader component.

styles/brand-item/BrandItem.styles.tsx (2)

15-15: LGTM! Vertical padding adjustment.

The increased vertical padding (12px) aligns with the theme update objectives and should improve visual spacing consistency.


14-14: Verify the asymmetric horizontal padding.

The container has asymmetric horizontal padding (14px left vs 8px right). While this might be intentional for specific layout requirements, please confirm if this is the desired design.

✅ Verification successful

The asymmetric padding appears to be intentional and consistent with the design system

The asymmetric padding (14px left, 8px right) in BrandItem.styles.tsx is part of a consistent pattern in the codebase. This design choice appears in a list item context where the left side typically contains content (with more padding for readability) and the right side might have actions or icons (with less padding for compactness). The TopTabNavigation component also uses asymmetric padding, suggesting this is a deliberate design pattern.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for similar padding patterns in other list item components
rg -l 'paddingLeft.*14.*paddingRight.*8' --type tsx

Length of output: 81


Script:

#!/bin/bash
# Search for similar padding patterns in other list item components, using .ts and .tsx extensions
rg -l 'paddingLeft.*14.*paddingRight.*8' -g '*.ts' -g '*.tsx'

# Let's also check for the specific file to understand the full context
cat styles/brand-item/BrandItem.styles.tsx

# Search for other style files with similar patterns
rg 'padding(Left|Right)' -g '*.styles.ts*' -A 1 -B 1

Length of output: 1377

styles/top-tab-navigation/TopTabNavigation.styles.tsx (5)

1-5: LGTM! Clean imports and proper type usage.

The imports are appropriate and the function declaration with proper TypeScript typing is well-structured.


6-10: LGTM! Container styles follow best practices.

The container styles properly utilize flex layout and theme-aware background color.


11-19: Skipping comment as past review feedback is still valid.

The previous review comment about improving responsive design and padding consistency still applies to these lines.


29-42: Skipping comment as past review feedback is still valid.

The previous review comment about enhancing text accessibility and theme handling still applies to these lines.


53-55: LGTM! Simple and effective content style.

The tab content style properly uses flex to fill available space.

styles/onboarding/brand.styles.tsx (2)

33-33: LGTM! Good improvement to visual hierarchy

Using the primary color for the autocomplete input container's border helps emphasize this interactive element, making it more prominent in the UI.


33-33: Verify color contrast for accessibility

Since this is using the primary theme color for a border, ensure that there's sufficient contrast between:

  1. The border color and the input background
  2. The input text and the input background

This is especially important for different theme modes (light/dark).

components/ui/carousel/carousel.tsx (1)

55-55: Verify layout implications of full-width carousel

The carousel width has been changed to use the full window width. While this works, consider these potential issues:

  1. The padding: 10 style on the Carousel might create unexpected layout with full width
  2. Content might appear too close to screen edges

Consider maintaining some side padding for better visual balance:

-width={Dimensions.get("window").width}
+width={Dimensions.get("window").width - 20}  // 10px padding on each side
✅ Verification successful

Full-width carousel layout is appropriate for this implementation

The change to use full window width (Dimensions.get("window").width) is consistent with the app's design patterns:

  • The InfluencerCard component also uses full window width, indicating this is an intentional design choice
  • No other carousel implementations show different width patterns
  • The carousel is properly contained within a View wrapper that can handle any necessary layout adjustments
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other carousel implementations or similar components to verify consistent spacing
rg -t tsx -t jsx "Dimensions.get\(\"window\"\).width" --no-heading

Length of output: 94


Script:

#!/bin/bash
# Search for Dimensions.get usage with proper file extensions
rg "Dimensions\.get\(.*window.*width" --type-add 'tsx:*.tsx' --type-add 'jsx:*.jsx' -t tsx -t jsx

# Also check for similar components that might use width calculations
rg "width.*window|window.*width" --type-add 'tsx:*.tsx' --type-add 'jsx:*.jsx' -t tsx -t jsx

Length of output: 434


Script:

#!/bin/bash
# Let's check the context of InfluencerCard usage to understand the width pattern
rg -B 5 -A 5 "const \{ width \} = Dimensions\.get\(\"window\"\)" --type-add 'tsx:*.tsx' -t tsx

# Check the carousel component's style context
rg -B 10 -A 10 "width=\{Dimensions\.get\(\"window\"\)\.width\}" --type-add 'tsx:*.tsx' -t tsx

Length of output: 2122


Script:

#!/bin/bash
# Let's check the styles of the carousel container and any padding/margin
rg -B 5 -A 5 "styles\.carouselContainer" --type-add 'tsx:*.tsx' -t tsx

# Also check if there are any style definitions in the carousel component
rg "StyleSheet\.create" -A 15 --type-add 'tsx:*.tsx' -t tsx components/ui/carousel/carousel.tsx

Length of output: 758

components/drawer-layout/BrandItem.tsx (2)

6-8: LGTM! Clean import statements

The imports are well-organized and all are being utilized in the component.


Line range hint 1-93: Verify theme consistency across the application

Let's ensure this theme update maintains consistency with other components.

✅ Verification successful

Theme implementation is consistent across the application

The verification shows that:

  • The faEllipsisV icon usage is consistent and only used in this component
  • The theme implementation using Colors(theme).primary is consistently applied across multiple components including modals, cards, and navigation elements
  • The useTheme hook from "@react-navigation/native" is properly used throughout the application in a consistent manner
  • BrandItem component's theme implementation aligns with other drawer components like BrandActionItem and DrawerMenuItem
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistency in icon usage and theme implementation

# Check for other menu icon implementations
echo "Checking for other menu icon implementations..."
rg -l "faEllipsisV|IconButton.*menu" --type tsx

# Check for BrandItem usage to verify theme propagation
echo "Checking BrandItem usage..."
rg -l "BrandItem" --type tsx

# Check for similar theme patterns
echo "Checking theme patterns..."
ast-grep --pattern 'Colors($_).primary' --type tsx

Length of output: 600


Script:

#!/bin/bash
# Let's modify our approach to use correct command flags

# Check for other menu icon implementations
echo "Checking for other menu icon implementations..."
rg "faEllipsisV|IconButton.*menu"

# Check for BrandItem usage
echo "Checking BrandItem usage..."
rg "BrandItem"

# Check theme patterns in the codebase
echo "Checking theme patterns..."
rg "Colors\(theme\)\.primary"

# Check for similar components using theme
echo "Checking theme usage patterns..."
rg "useTheme.*from.*@react-navigation/native"

Length of output: 9430

components/ui/top-tab-navigation/index.tsx (3)

20-22: Previous review comment about validation and type safety is still applicable

The concerns about empty tabs array validation and type safety for tabLayout remain valid.


31-45: Previous review comment about optimizing content rendering is still applicable

Additionally, consider memoizing the active tab component to prevent unnecessary re-renders.

+const MemoizedTabContent = React.memo(({ component }: { component: React.ReactNode }) => (
+  <View style={styles.tabContent}>
+    {component}
+  </View>
+));

74-104: Previous review comment about accessibility support is still applicable

Additionally, consider optimizing style calculations:

+const getTabStyles = React.useMemo(() => (
+  isActive: boolean,
+  isCompact: boolean,
+  theme: Theme
+) => [
+  styles.tab,
+  isCompact && styles.compactTab,
+  {
+    backgroundColor: isActive ? Colors(theme).primary : 'transparent',
+  }
+], [styles, theme]);

 <Pressable
   key={tab.id}
-  style={[
-    styles.tab,
-    size === "compact" && styles.compactTab,
-    {
-      backgroundColor: activeTab === tab ? Colors(theme).primary : 'transparent',
-    }
-  ]}
+  style={getTabStyles(activeTab === tab, size === "compact", theme)}
components/collaboration/collaboration-details/index.tsx (4)

1-23: LGTM! Clean interface and import structure.

The interfaces and imports are well-organized and properly typed.


32-55: Previous review comment about error handling remains valid.

The existing review comment about enhancing error handling and adding cleanup logic is still applicable to this code segment.


100-102: Previous review comment about useEffect cleanup remains valid.

The existing review comment about adding cleanup to prevent memory leaks is still applicable to this code segment.


104-134: LGTM! Clean conditional rendering implementation.

The loading and error states are handled appropriately, with clean JSX structure.

components/proposals/Invitations.tsx (3)

130-130: LGTM! Good layout practice.

Adding flex: 1 ensures proper container expansion within the parent layout.


196-196: Verify cardType value consistency

The cardType prop is set to "influencerCard" but this component handles proposals/invitations. This seems inconsistent with the component's purpose.

✅ Verification successful

Based on the shell script results, I can now generate the final response:

The cardType value "influencerCard" is appropriate for this component

Looking at the BottomSheetActions component implementation and its usage patterns across the codebase, the "influencerCard" cardType is correctly used here as it renders actions specific to influencer interactions (View Profile, Send Message, Block Influencer). This is consistent with other components that deal with influencer-related actions.

The component has distinct cardTypes for different contexts:

  • "influencerCard" for influencer-related actions
  • "applicationCard" for handling applications
  • "invitationCard" for invitation-specific actions
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check BottomSheetActions usage patterns
echo "Checking BottomSheetActions cardType values:"
rg -g "*.{tsx,ts}" "cardType.*[\"'].*Card[\"']"

echo "\nChecking BottomSheetActions component definition:"
ast-grep --pattern 'const BottomSheetActions = ({ cardType, $$$ }) => $$$'

Length of output: 17843


136-136: Verify the necessity of hideAction prop

The hideAction prop is being used but it's not clear why actions are being hidden in this context. Consider documenting the reason or removing if unnecessary.

✅ Verification successful

Let me try a different search pattern to find more information about the EmptyState component and its usage.


Let me try another search approach without specifying file types.


The hideAction prop is correctly used and necessary

The hideAction prop is a valid prop of the EmptyState component and is being used appropriately in the Invitations component. Looking at the codebase:

  1. The EmptyState component is defined with hideAction as an optional boolean prop in components/ui/empty-state/index.tsx
  2. This prop is consistently used across multiple empty states where no action button is needed:
    • In notifications when there are no notifications
    • In collaboration invitations/applications tabs when there are no entries
    • In the current Invitations component where no action is required from the user

The prop's usage aligns with the component's design pattern throughout the application, where some empty states require user actions (like in Applications.tsx with "Explore Collaborations" button) while others are purely informational.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check EmptyState component usage patterns
echo "Checking EmptyState component implementations:"
ast-grep --pattern 'EmptyState $$$hideAction$$$'

echo "\nChecking EmptyState component definition:"
ast-grep --pattern 'const EmptyState = ({ hideAction, $$$ }) => $$$'

Length of output: 321


Script:

#!/bin/bash
# Search for EmptyState component definition and usages
echo "Searching for EmptyState component definition:"
rg -l "EmptyState" --type tsx --type ts

echo "\nChecking content of files containing EmptyState:"
rg "EmptyState" -A 5 -B 5 --type tsx --type ts

Length of output: 356


Script:

#!/bin/bash
# Search for EmptyState component definition and usages
echo "Searching for EmptyState component definition:"
rg -l "EmptyState"

echo "\nChecking content of files containing EmptyState:"
rg "EmptyState" -A 5 -B 5

Length of output: 14475

components/collaboration/collaboration-details/InvitationsTabContent.tsx (1)

Line range hint 46-75: Well-structured invitation handling logic!

The handleCollaborationInvite function is well-implemented with:

  • Proper loading state management
  • Good error handling with user feedback
  • Clean state reset in finally block
components/InfluencerCard.tsx (1)

27-31: LGTM: Clean interface updates and imports

The changes to imports and type definitions are well-structured:

  • New utility imports support the enhanced functionality
  • Interface updates properly type the new invitation checking mechanism
  • FontAwesome migration improves icon consistency

Also applies to: 37-37, 53-53

} from "react-native-paper";
import ScreenHeader from "@/components/ui/screen-header";

const CreateCollaborationScreen = () => {

ghost Nov 20, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Component name doesn't match its functionality

The component is named CreateCollaborationScreen but it displays collaboration history. This naming mismatch could lead to confusion.

Consider renaming to better reflect its purpose:

-const CreateCollaborationScreen = () => {
+const CollaborationHistoryScreen = () => {

Don't forget to update the export statement accordingly.

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

Comment on lines 5 to 13
const CreateCollaborationScreen = () => {
const theme = useTheme();
const styles = stylesFn(theme);
return (
<AppLayout>
<ScrollView style={styles.container}>
<View
style={{
flexDirection: "row",
alignItems: "center",
marginBottom: 16,
gap: 16,
}}
>
<BackButton />
<Title style={styles.title}>Collaboration History</Title>
</View>
</ScrollView>
<ScreenHeader
title="Collaboration History"
/>
</AppLayout>
);
};

ghost Nov 20, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codebase verification

Component name mismatch with file purpose

The file CollaborationHistory.tsx incorrectly exports a component named CreateCollaborationScreen, which doesn't match its intended purpose. This component should be renamed to CollaborationHistoryScreen to maintain consistency with:

  • The file name CollaborationHistory.tsx
  • The router configuration in app/(main)/(screens)/_layout.tsx which references CollaborationHistory
  • The screen's actual purpose of showing collaboration history
🔗 Analysis chain

Verify component references after renaming

If you rename the component as suggested, ensure all references are updated.


Let me gather more context about the component usage.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for references to the component in router configs and navigation
rg -l "CreateCollaborationScreen"

# Search for navigation references to this screen
ast-grep --pattern 'navigation.navigate("$_")' 

Length of output: 258


Script:

#!/bin/bash
# Check the content of files that reference CreateCollaborationScreen
rg "CreateCollaborationScreen" -A 5 -B 5

# Look for any router configurations
rg -g "*.tsx" "createStackNavigator|Stack.Screen" -A 2 -B 2

Length of output: 7064

Comment on lines +76 to +82
<Pressable>
<FontAwesomeIcon
icon={faEllipsisV}
size={20}
color={active ? Colors(theme).white : Colors(theme).text}
/>
</Pressable>

ghost Nov 20, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add interaction handling and accessibility improvements

The menu icon button implementation needs the following improvements:

  1. The Pressable is non-interactive without an onPress handler
  2. Missing accessibility properties
  3. No visual feedback on interaction

Here's the suggested implementation:

-            <Pressable>
+            <Pressable
+              onPress={() => {/* TODO: Implement menu action */}}
+              accessibilityRole="button"
+              accessibilityLabel="More options"
+              style={({ pressed }) => ({
+                opacity: pressed ? 0.7 : 1,
+              })}
+            >
               <FontAwesomeIcon
                 icon={faEllipsisV}
                 size={20}
                 color={active ? Colors(theme).white : Colors(theme).text}
               />
             </Pressable>

Would you like me to help implement the menu action handler or create an issue to track this task?

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

Comment on lines +73 to +80
<ApplicationsTabContent
pageID={pageID}
collaboration={{
id: pageID,
name: collaboration?.name,
}}
/>
),

ghost Nov 20, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Improve type safety in ApplicationsTabContent props.

The current implementation might cause runtime issues if collaboration is undefined.

 <ApplicationsTabContent
   pageID={pageID}
   collaboration={{
     id: pageID,
-    name: collaboration?.name,
+    name: collaboration?.name ?? 'Untitled Collaboration',
   }}
 />
📝 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
<ApplicationsTabContent
pageID={pageID}
collaboration={{
id: pageID,
name: collaboration?.name,
}}
/>
),
<ApplicationsTabContent
pageID={pageID}
collaboration={{
id: pageID,
name: collaboration?.name ?? 'Untitled Collaboration',
}}
/>
),

status={item.status}
/>
)}
keyExtractor={(item, index) => index.toString()}

ghost Nov 20, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve list performance with proper keyExtractor

Using array indices as keys can lead to performance issues and incorrect component reuse. Consider using a unique identifier from the item.

-            keyExtractor={(item, index) => index.toString()}
+            keyExtractor={(item) => item.id}

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

Comment on lines +176 to +183
<FontAwesomeIcon
icon={faUsers}
color={Colors(theme).primary}
size={20}
/>
<Text style={styles.statsText}>
{convertToKUnits(Number(influencer.followers))}
</Text>

ghost Nov 20, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add accessibility labels to statistics icons

The statistics icons should have accessibility labels for screen readers.

 <FontAwesomeIcon
   icon={faUsers}
   color={Colors(theme).primary}
   size={20}
+  accessibilityLabel={`${convertToKUnits(Number(influencer.followers))} followers`}
 />

 <FontAwesomeIcon
   icon={faBullseye}
   color={Colors(theme).primary}
   size={20}
+  accessibilityLabel={`${convertToKUnits(Number(influencer.reach))} reach`}
 />

 <FontAwesomeIcon
   icon={faStar}
   color={Colors(theme).primary}
   size={20}
+  accessibilityLabel={`${influencer.rating} rating`}
 />

Also applies to: 186-193, 196-203

Comment on lines +107 to +111
if (props?.alreadyInvited) {
props.alreadyInvited(props.influencer.id).then((invited) => {
setIsInvited(invited);
});
}

ghost Nov 20, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling and fix dependency array

The invitation check needs error handling and proper dependencies:

 useEffect(() => {
   if (props?.alreadyInvited) {
+    try {
       props.alreadyInvited(props.influencer.id).then((invited) => {
         setIsInvited(invited);
+      }).catch((error) => {
+        console.error('Failed to check invitation status:', error);
       });
+    } catch (error) {
+      console.error('Error in invitation check:', error);
+    }
   }
-  }, []);
+  }, [props.alreadyInvited, props.influencer.id]);
📝 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
if (props?.alreadyInvited) {
props.alreadyInvited(props.influencer.id).then((invited) => {
setIsInvited(invited);
});
}
if (props?.alreadyInvited) {
try {
props.alreadyInvited(props.influencer.id).then((invited) => {
setIsInvited(invited);
}).catch((error) => {
console.error('Failed to check invitation status:', error);
});
} catch (error) {
console.error('Error in invitation check:', error);
}
}
}, [props.alreadyInvited, props.influencer.id]);

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Outside diff range and nitpick comments (8)
components/collaboration/create-collaboration/CreateCollaborationMap.tsx (1)

20-22: Consider consolidating map region state

Maintaining separate states for web and native platforms increases complexity. Consider using a single state with a platform check in the handlers.

-const [nativeMapRegion, setNativeMapRegion] = useState(mapRegion);
-const [webMapRegion, setWebMapRegion] = useState(mapRegion);
+const [currentMapRegion, setCurrentMapRegion] = useState(mapRegion);
components/ui/select/index.tsx (2)

26-42: Simplify multiselect logic using filter

The multiselect logic can be simplified using array filter for better readability and maintenance.

Consider this simplified version:

   const handleSelect = (item: SelectItem) => {
     if (multiselect) {
-      const index = value.findIndex(
-        (selectedItem) => selectedItem.value === item.value,
-      );
-      if (index === -1) {
+      const isSelected = value.some(selectedItem => selectedItem.value === item.value);
+      if (!isSelected) {
         onSelect([...value, item]);
       } else {
-        onSelect([
-          ...value.slice(0, index),
-          ...value.slice(index + 1),
-        ]);
+        onSelect(value.filter(selectedItem => selectedItem.value !== item.value));
       }
     } else {
       onSelect([item]);
     }
   };

18-90: Consider adding accessibility support

The component could benefit from improved accessibility support for screen readers and keyboard navigation.

Consider adding these accessibility props to the Pressable:

           <Pressable
             key={item.value}
             onPress={() => handleSelect(item)}
+            accessible={true}
+            accessibilityRole="button"
+            accessibilityState={{ selected: isSelected }}
+            accessibilityLabel={`Select ${item.label}`}
           >
🧰 Tools
🪛 Biome

[error] 55-57: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

styles/modal/UploadModal.styles.tsx (2)

58-67: LGTM with a minor suggestion regarding color property

The new button styles look good, but there's a minor improvement possible.

The color property in iconButton style won't affect the background or container. If this is meant for the icon color, it should be applied to the icon component directly or moved to a separate icon style object.

 iconButton: {
   backgroundColor: Colors(theme).primary,
   borderRadius: 10,
   marginHorizontal: 0,
-  color: Colors(theme).white,
   padding: 8,
 },

117-119: Consider centralizing common spacing values

The style changes look good and improve consistency. However, I notice that spacing values like 16 and 8 are used throughout the file.

Consider creating a centralized spacing scale in your theme constants:

// constants/Spacing.ts
export default {
  xs: 4,
  sm: 8,
  md: 16,
  lg: 24,
  xl: 32,
} as const;

This would make it easier to maintain consistent spacing across the app and make global spacing adjustments when needed.

app/(modal)/create-collaboration.tsx (3)

38-45: Ensure consistent state initialization

Currently, promotionType and collabType are initialized as empty arrays, while platform is initialized with a default value of "Instagram". For consistency and predictability, consider initializing all selections either with default values or as empty arrays.


420-433: Add URL validation for new links

To enhance data integrity and user experience, consider validating the format of the URLs when users add new links.

You might use a regular expression or a URL validation library to ensure that the URLs are properly formatted before accepting them.


350-382: Enhance scalability by using a dropdown for location selection

If there's a possibility of adding more location options in the future, consider using a dropdown or select component instead of radio buttons for location selection. This approach offers better scalability and a cleaner UI as options grow.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 96a279e and 32b264d.

📒 Files selected for processing (6)
  • app/(modal)/create-collaboration.tsx (8 hunks)
  • app/(modal)/create-collaboration.web.tsx (0 hunks)
  • components/collaboration/create-collaboration/CreateCollaborationMap.tsx (1 hunks)
  • components/ui/select/index.tsx (1 hunks)
  • constants/CreateCollaborationForm.ts (1 hunks)
  • styles/modal/UploadModal.styles.tsx (2 hunks)
💤 Files with no reviewable changes (1)
  • app/(modal)/create-collaboration.web.tsx
✅ Files skipped from review due to trivial changes (1)
  • constants/CreateCollaborationForm.ts
🧰 Additional context used
🪛 Biome
components/collaboration/create-collaboration/CreateCollaborationMap.tsx

[error] 6-6: Do not shadow the global "Map" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

components/ui/select/index.tsx

[error] 55-57: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

🔇 Additional comments (5)
components/collaboration/create-collaboration/CreateCollaborationMap.tsx (1)

43-49: Verify effect cleanup and component unmounting

The effect updates parent state but lacks cleanup. Verify the component properly handles unmounting and rapid region changes.

components/ui/select/index.tsx (2)

1-16: LGTM! Well-structured type definitions

The type definitions and imports are clean and well-organized. The SelectProps interface properly handles both empty and populated states.


18-25: LGTM! Clean component setup

The component definition follows React best practices with proper TypeScript typing and default prop values.

styles/modal/UploadModal.styles.tsx (1)

68-70: Great use of modern layout properties!

The switch to using gap property is a good practice for maintaining consistent spacing between elements. This approach is more maintainable than using margin or justifyContent.

Also applies to: 79-79

app/(modal)/create-collaboration.tsx (1)

82-85: Good job adding input validation in addLink

The added checks effectively ensure that both newLinkName and newLinkUrl are provided before adding the link, enhancing robustness.

import MapView, { Marker } from "react-native-maps";
import { APIProvider } from "@vis.gl/react-google-maps";

import Map from "@/components/map";

ghost Nov 21, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Rename imported Map component to avoid shadowing

The current import shadows the global Map object, which could lead to confusion.

-import Map from "@/components/map";
+import GoogleMapComponent from "@/components/map";
📝 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
import Map from "@/components/map";
import GoogleMapComponent from "@/components/map";
🧰 Tools
🪛 Biome

[error] 6-6: Do not shadow the global "Map" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

Comment on lines +53 to +55
<APIProvider
apiKey={process.env.EXPO_PUBLIC_GOOGLE_MAPS_API_KEY!}
>

ghost Nov 21, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling for Google Maps API key

The current implementation assumes the API key exists. Add proper error handling to prevent runtime issues.

+const GOOGLE_MAPS_API_KEY = process.env.EXPO_PUBLIC_GOOGLE_MAPS_API_KEY;
+if (!GOOGLE_MAPS_API_KEY) {
+  throw new Error('Google Maps API key is required');
+}

 <APIProvider
-  apiKey={process.env.EXPO_PUBLIC_GOOGLE_MAPS_API_KEY!}
+  apiKey={GOOGLE_MAPS_API_KEY}
 >
📝 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
<APIProvider
apiKey={process.env.EXPO_PUBLIC_GOOGLE_MAPS_API_KEY!}
>
const GOOGLE_MAPS_API_KEY = process.env.EXPO_PUBLIC_GOOGLE_MAPS_API_KEY;
if (!GOOGLE_MAPS_API_KEY) {
throw new Error('Google Maps API key is required');
}
<APIProvider
apiKey={GOOGLE_MAPS_API_KEY}
>

Comment on lines +27 to +41
const onLocationChange = (
location: { latitude: number; longitude: number },
) => {
setWebMapRegion({
latitude: location.latitude,
longitude: location.longitude,
});
};

const onMapLocationChange = (region: any) => {
onMapRegionChange({
latitude: region.latitude,
longitude: region.longitude,
});
}

ghost Nov 21, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Preserve all map region properties in handlers

The current implementation drops important map properties like zoom level (latitudeDelta, longitudeDelta).

 const onLocationChange = (
   location: { latitude: number; longitude: number },
 ) => {
   setWebMapRegion({
+    ...webMapRegion,
     latitude: location.latitude,
     longitude: location.longitude,
   });
 };

 const onMapLocationChange = (region: any) => {
   onMapRegionChange({
+    ...region,
     latitude: region.latitude,
     longitude: region.longitude,
   });
 }
📝 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
const onLocationChange = (
location: { latitude: number; longitude: number },
) => {
setWebMapRegion({
latitude: location.latitude,
longitude: location.longitude,
});
};
const onMapLocationChange = (region: any) => {
onMapRegionChange({
latitude: region.latitude,
longitude: region.longitude,
});
}
const onLocationChange = (
location: { latitude: number; longitude: number },
) => {
setWebMapRegion({
...webMapRegion,
latitude: location.latitude,
longitude: location.longitude,
});
};
const onMapLocationChange = (region: any) => {
onMapRegionChange({
...region,
latitude: region.latitude,
longitude: region.longitude,
});
}

Comment on lines +11 to +14
interface CreateCollaborationMapProps {
mapRegion: any;
onMapRegionChange: (region: any) => void;
}

ghost Nov 21, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Define specific types for mapRegion prop

The any type reduces type safety. Consider defining a specific interface for the region shape.

+interface MapRegion {
+  latitude: number;
+  longitude: number;
+  latitudeDelta?: number;
+  longitudeDelta?: number;
+}

 interface CreateCollaborationMapProps {
-  mapRegion: any;
-  onMapRegionChange: (region: any) => void;
+  mapRegion: MapRegion;
+  onMapRegionChange: (region: MapRegion) => void;
 }
📝 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
interface CreateCollaborationMapProps {
mapRegion: any;
onMapRegionChange: (region: any) => void;
}
interface MapRegion {
latitude: number;
longitude: number;
latitudeDelta?: number;
longitudeDelta?: number;
}
interface CreateCollaborationMapProps {
mapRegion: MapRegion;
onMapRegionChange: (region: MapRegion) => void;
}

Comment on lines +66 to +70
<MapView
style={styles.map}
region={nativeMapRegion}
onRegionChangeComplete={(region) => setNativeMapRegion(region)}
>

ghost Nov 21, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add essential MapView configuration props

The MapView component is missing important configuration props for user interaction.

 <MapView
   style={styles.map}
   region={nativeMapRegion}
   onRegionChangeComplete={(region) => setNativeMapRegion(region)}
+  zoomEnabled={true}
+  scrollEnabled={true}
+  rotateEnabled={false}
+  minZoomLevel={5}
+  maxZoomLevel={20}
 >
📝 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
<MapView
style={styles.map}
region={nativeMapRegion}
onRegionChangeComplete={(region) => setNativeMapRegion(region)}
>
<MapView
style={styles.map}
region={nativeMapRegion}
onRegionChangeComplete={(region) => setNativeMapRegion(region)}
zoomEnabled={true}
scrollEnabled={true}
rotateEnabled={false}
minZoomLevel={5}
maxZoomLevel={20}
>

Comment on lines +54 to +86
items.map((item) => (
<Pressable
onPress={() => handleSelect(item)}
>
<View
key={item.value}
style={{
backgroundColor: value.find(
(selectedItem) => selectedItem.value === item.value,
)
? Colors(theme).primary
: Colors(theme).tag,
borderRadius: 10,
flexDirection: "row",
justifyContent: "space-between",
paddingHorizontal: 16,
paddingVertical: 12,
}}
>
<Text
style={{
color: value.find(
(selectedItem) => selectedItem.value === item.value,
)
? Colors(theme).white
: Colors(theme).text,
}}
>
{item.label}
</Text>
</View>
</Pressable>
))

ghost Nov 21, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Fix key prop placement and optimize style calculations

There are several issues in the render logic:

  1. The key prop is incorrectly placed on the inner View instead of the outer Pressable
  2. The selection check is duplicated in both View and Text styles
  3. Style objects are recreated on every render

Apply these fixes:

-        items.map((item) => (
-          <Pressable
+        items.map((item) => {
+          const isSelected = value.find(
+            (selectedItem) => selectedItem.value === item.value,
+          );
+          return (
+          <Pressable
+            key={item.value}
             onPress={() => handleSelect(item)}
           >
             <View
-              key={item.value}
               style={{
                 backgroundColor: value.find(
                   (selectedItem) => selectedItem.value === item.value,
                )
                   ? Colors(theme).primary
                   : Colors(theme).tag,
                 borderRadius: 10,
                 flexDirection: "row",
                 justifyContent: "space-between",
                 paddingHorizontal: 16,
                 paddingVertical: 12,
               }}
             >
               <Text
                 style={{
-                  color: value.find(
-                    (selectedItem) => selectedItem.value === item.value,
-                  )
+                  color: isSelected
                     ? Colors(theme).white
                     : Colors(theme).text,
                 }}
               >
                 {item.label}
               </Text>
             </View>
           </Pressable>
+          );
+        })

Consider extracting styles and memoizing them:

const getStyles = (theme: Theme) => {
  const styles = {
    container: {
      flex: 1,
      flexDirection: "row" as const,
      flexWrap: "wrap" as const,
      gap: 8,
    },
    item: (isSelected: boolean) => ({
      backgroundColor: isSelected ? Colors(theme).primary : Colors(theme).tag,
      borderRadius: 10,
      flexDirection: "row" as const,
      justifyContent: "space-between" as const,
      paddingHorizontal: 16,
      paddingVertical: 12,
    }),
    text: (isSelected: boolean) => ({
      color: isSelected ? Colors(theme).white : Colors(theme).text,
    }),
  };
  return styles;
};

// In component:
const styles = useMemo(() => getStyles(theme), [theme]);
🧰 Tools
🪛 Biome

[error] 55-57: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

Comment on lines +402 to +408
{
links.map((link, index) => (
<Paragraph key={index}>
{link.name}: {link.url}
</Paragraph>
))
}

ghost Nov 21, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Sanitize link names and URLs before rendering

When displaying user-provided link names and URLs, it's important to sanitize the inputs to prevent potential XSS attacks, especially in a web environment.

Consider utilizing a sanitization library or built-in methods to escape or sanitize the displayed content.

Comment on lines +125 to +128
promotionType: promotionType[0].value,
collaborationType: collabType[0].value,
numberOfInfluencersNeeded: numInfluencers,
platform,
platform: platform[0].value,

ghost Nov 21, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Prevent potential runtime errors from empty selections

Accessing promotionType[0].value, collabType[0].value, and platform[0].value without verifying that the arrays are non-empty can lead to runtime errors if any array is empty. Ensure that each array contains at least one item before accessing its elements.

Apply this diff to add necessary checks:

+ if (promotionType.length === 0 || collabType.length === 0 || platform.length === 0) {
+   Toaster.error("Please select promotion type, collaboration type, and platform");
+   return;
+ }

  await addDoc(collabRef, {
    name: collaborationName,
    // ...
-   promotionType: promotionType[0].value,
-   collaborationType: collabType[0].value,
-   platform: platform[0].value,
+   promotionType: promotionType.length > 0 ? promotionType[0].value : null,
+   collaborationType: collabType.length > 0 ? collabType[0].value : null,
+   platform: platform.length > 0 ? platform[0].value : null,
    // ...
  });

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

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Outside diff range and nitpick comments (10)
styles/empty-state/EmptyState.styles.tsx (2)

9-10: Consider using responsive padding

While the gap property is good for consistent spacing, fixed pixel values for padding might not scale well across different devices.

Consider using responsive units:

-   paddingHorizontal: 16,
+   paddingHorizontal: '5%',
    gap: 20,

Breaking change: 'action' style removal affects NotificationCard component

The removed 'action' style is still being referenced in components/NotificationCard.tsx where it's used as styles.actions. This removal will cause runtime errors.

  • components/NotificationCard.tsx: Update or remove the styles.actions reference
🔗 Analysis chain

Line range hint 1-35: Verify removal of 'action' style

The AI summary indicates that the 'action' style was removed. Let's verify that this removal doesn't break existing usage.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining references to the removed 'action' style
rg -l "styles\.action" || echo "No references found"

# Search for potential style applications using string literal
rg -l 'style="action"' || echo "No references found"

Length of output: 130


Script:

#!/bin/bash
# Check the actual usage in NotificationCard.tsx
rg "styles\.action" components/NotificationCard.tsx -A 2 -B 2

Length of output: 177

styles/tab1.styles.tsx (1)

16-20: Good architectural improvement!

The separation of positioning logic (skipButtonContainer) from button styling is a better approach. Leveraging the Paper component's built-in contained mode for consistent button styling across the app is a good practice.

app/(public)/pre-signin.tsx (1)

62-73: Consider accessibility and UX improvements for the Skip button

While the implementation is functional, consider the following improvements:

  1. Add accessibility props for better screen reader support
  2. Consider using mode="text" instead of "contained" as skip actions are typically less prominent
  3. Verify the skipButtonContainer styling for proper positioning

Here's a suggested improvement:

 <View
   style={styles.skipButtonContainer}
 >
   <Button
-    mode="contained"
+    mode="text"
+    accessibilityLabel="Skip onboarding"
+    accessibilityHint="Skips to the connect screen"
     onPress={() => {
       skipToConnect();
     }}
   >
     Skip
   </Button>
 </View>
components/collaboration/collaboration-details/ApplicationsTabContent.tsx (3)

78-82: Consider externalizing strings for internationalization.

The empty state message should be externalized to support multiple languages.

 <EmptyState
-  subtitle="No applications yet. Check back later."
+  subtitle={t('applications.empty.subtitle')}
   image={require("@/assets/images/illustration6.png")}
   hideAction
 />

Line range hint 94-112: Refactor data transformation logic.

The influencer object construction contains hardcoded values and complex nested ternaries. Consider:

  1. Extracting default values to constants
  2. Moving transformation logic to a separate utility function
const DEFAULT_INFLUENCER_VALUES = {
  handle: '@handle',
  followers: 1000,
  reach: 10000,
  rating: 4.5,
  bio: 'I am a content creator',
  jobsCompleted: 12,
  successRate: 100,
};

const transformInfluencerData = (item: any) => ({
  id: item.id,
  profilePic: item.profileImage,
  name: item.name,
  handle: item.handle || DEFAULT_INFLUENCER_VALUES.handle,
  media: item.attachments,
  followers: item.backend?.followers ?? DEFAULT_INFLUENCER_VALUES.followers,
  reach: item.backend?.reach ?? DEFAULT_INFLUENCER_VALUES.reach,
  rating: item.backend?.rating ?? DEFAULT_INFLUENCER_VALUES.rating,
  ...DEFAULT_INFLUENCER_VALUES
});

127-137: Implementation looks good with a minor suggestion.

The BottomSheetActions implementation is clean. Consider adding a null check for selectedApplication to prevent potential runtime issues.

-  key={selectedApplication?.applicationID}
+  key={selectedApplication?.applicationID ?? 'default-key'}
components/collaboration/collaboration-details/InvitationsTabContent.tsx (3)

Line range hint 45-74: Consider enhancing error handling with specific error types

While the error handling has improved, consider catching specific error types for better error reporting and user feedback.

 try {
   // ... existing code ...
 } catch (error) {
-  console.error(error);
-  Toaster.error("Failed to send invitation");
+  console.error('Invitation creation failed:', error);
+  if (error instanceof FirebaseError) {
+    Toaster.error(`Failed to send invitation: ${error.message}`);
+  } else {
+    Toaster.error("Failed to send invitation. Please try again.");
+  }
 }

Line range hint 76-99: Consider extracting loading state styles

The inline styles for the loading state could be moved to the styles object for better maintainability.

+ // In your styles file
+ const stylesFn = (theme) => ({
+   // ... existing styles ...
+   loadingContainer: {
+     flex: 1,
+     justifyContent: "center",
+     alignItems: "center",
+     gap: 40,
+   },
+ });

- <View
-   style={{
-     flex: 1,
-     justifyContent: "center",
-     alignItems: "center",
-     gap: 40,
-   }}
- >
+ <View style={styles.loadingContainer}>

Line range hint 103-170: Consider optimizing FlatList performance

The FlatList could benefit from performance optimizations using React's useMemo and useCallback hooks.

+ const renderItem = useCallback(({ item }) => (
+   <InfluencerCard
+     type="invitation"
+     alreadyInvited={checkIfAlreadyInvited}
+     influencer={{
+       // ... existing props
+     }}
+     ToggleModal={() => {
+       setIsVisible(true);
+       setSelectedInfluencer({
+         collaborationID: props.pageID,
+         influencerID: item.id,
+       });
+     }}
+     ToggleMessageModal={() => {
+       setIsMessageModalVisible(true);
+       setSelectedInfluencer({
+         collaborationID: props.pageID,
+         influencerID: item.id,
+       });
+     }}
+   />
+ ), [props.pageID, checkIfAlreadyInvited]);

+ const ItemSeparator = useCallback(() => (
+   <View
+     style={{
+       height: 16,
+       backgroundColor: Colors(theme).aliceBlue,
+     }}
+   />
+ ), [theme]);

  <FlatList
    // ... other props ...
-   renderItem={({ item }) => (
-     <InfluencerCard ... />
-   )}
+   renderItem={renderItem}
-   ItemSeparatorComponent={() => (...)}
+   ItemSeparatorComponent={ItemSeparator}
  />
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 32b264d and 8830579.

📒 Files selected for processing (18)
  • app/(auth)/create-new-account.tsx (1 hunks)
  • app/(auth)/forget-password.tsx (0 hunks)
  • app/(auth)/login.tsx (0 hunks)
  • app/(main)/(screens)/collaboration-details/[pageID].tsx (1 hunks)
  • app/(main)/_layout.tsx (0 hunks)
  • app/(modal)/create-collaboration.tsx (8 hunks)
  • app/(onboarding)/onboarding-brand-preference.tsx (1 hunks)
  • app/(onboarding)/onboarding-get-started.tsx (4 hunks)
  • app/(onboarding)/onboarding-your-brand.tsx (5 hunks)
  • app/(public)/pre-signin.tsx (2 hunks)
  • components/collaboration/CollaborationCard.tsx (3 hunks)
  • components/collaboration/collaboration-details/ApplicationsTabContent.tsx (4 hunks)
  • components/collaboration/collaboration-details/InvitationsTabContent.tsx (5 hunks)
  • components/proposals/Applications.tsx (3 hunks)
  • components/ui/tag/index.tsx (1 hunks)
  • layouts/app-layout/index.tsx (2 hunks)
  • styles/empty-state/EmptyState.styles.tsx (2 hunks)
  • styles/tab1.styles.tsx (1 hunks)
💤 Files with no reviewable changes (3)
  • app/(auth)/forget-password.tsx
  • app/(auth)/login.tsx
  • app/(main)/_layout.tsx
🚧 Files skipped from review as they are similar to previous changes (7)
  • app/(auth)/create-new-account.tsx
  • app/(onboarding)/onboarding-brand-preference.tsx
  • app/(onboarding)/onboarding-get-started.tsx
  • app/(onboarding)/onboarding-your-brand.tsx
  • components/collaboration/CollaborationCard.tsx
  • components/proposals/Applications.tsx
  • components/ui/tag/index.tsx
🔇 Additional comments (15)
styles/empty-state/EmptyState.styles.tsx (2)

20-21: Past review comment is still applicable

The previous review comment about using responsive dimensions for the image remains valid.


26-26: LGTM! Consistent text alignment

The centered text alignment for both title and subtitle creates a visually balanced layout.

Also applies to: 30-30

layouts/app-layout/index.tsx (2)

7-7: LGTM!

Clean import of the toast notification library.


27-27: Verify removal of duplicate Toast instances.

The centralized Toast implementation looks good. However, let's verify that all other Toast instances have been properly removed to prevent duplicate notifications.

✅ Verification successful

Toast implementation is correctly centralized with no duplicates

The verification confirms there is only one instance of the react-native-toast-message Toast component in layouts/app-layout/index.tsx. All other toast-related imports refer to a different Toaster component from the shared UI components, which is used for triggering notifications rather than rendering the toast UI.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for any remaining Toast component instances that might cause duplicates
# Expected: Only one instance in app-layout/index.tsx

# Search for Toast imports
echo "Checking for Toast imports:"
rg "import.*Toast.*react-native-toast-message"

# Search for Toast component usage
echo -e "\nChecking for Toast component usage:"
rg "<Toast" --type tsx --type jsx

Length of output: 341


Script:

#!/bin/bash
# Let's try a different approach to search for Toast components without file type restrictions
echo "Checking for Toast component usage across all files:"
rg "<Toast"

# Also search for any potential Toast-related imports
echo -e "\nChecking for all Toast-related imports:"
rg "import.*Toast"

# Search for potential Toast references in JSX/TSX files
echo -e "\nChecking for Toast references in component files:"
fd -e tsx -e jsx -x cat {} | rg "Toast"

Length of output: 3536

app/(main)/(screens)/collaboration-details/[pageID].tsx (3)

1-6: LGTM! Imports are well-organized and necessary.

The imports are appropriate for the component's functionality, including navigation and UI components.


9-9: The isVisible state is still not properly toggled

The previous review comment about isVisible remains valid - there's no logic to set it to true.


17-19: Verify data fetching implementation in CollaborationDetails

The AI summary indicates that data fetching logic was removed from this component. Let's verify that CollaborationDetails properly handles this responsibility.

✅ Verification successful

Based on the search results, I can now generate the final response as we found the CollaborationDetails component implementation and its data fetching logic.

Data fetching is properly implemented in CollaborationDetails

The component at components/collaboration/collaboration-details/index.tsx correctly handles data fetching:

  • Implements fetchCollaboration function to retrieve collaboration data from Firestore
  • Uses useEffect hook to fetch data when pageID changes
  • Shows loading state during data fetch
  • Properly handles collaboration document retrieval using doc and getDoc from Firestore
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check the implementation of CollaborationDetails
ast-grep --pattern 'export default function CollaborationDetails($_) {
  $$$
}'

# Look for data fetching logic
rg -t typescript -t javascript "useEffect.*fetch.*collaboration" ./components/collaboration/collaboration-details

Length of output: 232


Script:

#!/bin/bash
# Search for CollaborationDetails component with broader file pattern
fd "CollaborationDetails" --type file

# Once we find the file, let's look for data fetching patterns
rg "useEffect|fetch|getCollaboration" --type-add 'ts:*.{ts,tsx}' --type ts -A 5

Length of output: 29662

styles/tab1.styles.tsx (1)

16-20: Verify visual consistency across screens

Since the button styling has been moved to use Paper's contained mode, let's verify the visual consistency across all screens using the skip button.

app/(public)/pre-signin.tsx (2)

1-1: LGTM! Import changes align with component updates

The import modifications correctly reflect the component's dependencies after migrating to react-native-paper's Button component.

Also applies to: 4-4


62-63: Verify skipButtonContainer styling implementation

Ensure that the skipButtonContainer style is properly defined in the tab1.styles.ts file for correct positioning of the Skip button.

✅ Verification successful

skipButtonContainer style is properly implemented

The skipButtonContainer style is correctly defined in styles/tab1.styles.tsx with appropriate positioning properties (absolute positioning, top: 10, right: 20) and is properly referenced in pre-signin.tsx.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for skipButtonContainer style definition
rg -l "skipButtonContainer.*{" 

Length of output: 140


Script:

#!/bin/bash
# Search for skipButtonContainer style definition with proper regex
rg "skipButtonContainer" -A 3

Length of output: 449

components/collaboration/collaboration-details/ApplicationsTabContent.tsx (1)

51-51: Previous error handling comment is still applicable.

components/collaboration/collaboration-details/InvitationsTabContent.tsx (1)

33-43: Good job implementing the useInfluencers hook!

The custom hook nicely encapsulates the influencer-related logic and state management, making the component cleaner and more maintainable.

app/(modal)/create-collaboration.tsx (3)

124-127: Reuse existing review comment

The issue of accessing array indices without validation was previously identified and is still applicable.


258-287: Well-structured UI improvements!

The introduction of the Select component and consistent layout spacing improves both code organization and user experience.


400-425: Reuse existing review comment

The security concern about link sanitization was previously identified and is still applicable.


const CollaborationDetailsScreen = () => {
const [isVisible, setIsVisible] = useState(false);
const pageID = useLocalSearchParams().pageID;

ghost Nov 21, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve pageID type safety and error handling

The current implementation could be more robust with proper type checking and error handling.

Consider this safer implementation:

- const pageID = useLocalSearchParams().pageID;
+ const { pageID } = useLocalSearchParams();
+ if (!pageID || typeof pageID !== 'string') {
+   return (
+     <AppLayout>
+       <ScreenHeader title="Error" />
+       <Text>Invalid collaboration ID</Text>
+     </AppLayout>
+   );
+ }

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

Comment on lines +20 to +28
<BottomSheetActions
cardId={pageID as string}
cardType="influencerCard"
isVisible={isVisible}
snapPointsRange={["20%", "50%"]}
onClose={() => {
setIsVisible(false);
}}
/>

ghost Nov 21, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codebase verification

"influencerCard" is a valid cardType, but constants should be shared

The value "influencerCard" is indeed a valid cardType as defined in the BottomSheetActionsProps interface. However, instead of creating local constants, these values should be shared across the codebase since they're used in multiple components:

  • components/explore-influencers/index.tsx
  • components/proposals/Invitations.tsx
  • app/(main)/(screens)/collaboration-details/[pageID].tsx
+ // Move to a shared constants file
+ export const BOTTOM_SHEET_CARD_TYPES = {
+   INFLUENCER_CARD: "influencerCard",
+   APPLICATION_CARD: "applicationCard",
+   INVITATION_CARD: "invitationCard",
+   ACTIVE_COLLAB: "activeCollab",
+ } as const;
+ 
+ export const BOTTOM_SHEET_SNAP_POINTS = {
+   SMALL: ["20%", "50%"] as const,
+   LARGE: ["50%", "90%"] as const,
+ } as const;

  <BottomSheetActions
    cardId={pageID}
-   cardType="influencerCard"
+   cardType={BOTTOM_SHEET_CARD_TYPES.INFLUENCER_CARD}
    isVisible={isVisible}
-   snapPointsRange={["20%", "50%"]}
+   snapPointsRange={BOTTOM_SHEET_SNAP_POINTS.SMALL}
    onClose={() => setIsVisible(false)}
  />
🔗 Analysis chain

Extract magic values into constants and verify cardType

The component contains hardcoded values that should be extracted into constants for better maintainability.

+ const SNAP_POINTS = ["20%", "50%"] as const;
+ const CARD_TYPE = "influencerCard" as const;

  <BottomSheetActions
    cardId={pageID}
-   cardType="influencerCard"
+   cardType={CARD_TYPE}
    isVisible={isVisible}
-   snapPointsRange={["20%", "50%"]}
+   snapPointsRange={SNAP_POINTS}
    onClose={() => setIsVisible(false)}
  />

Let's verify if "influencerCard" is a valid card type:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other occurrences of cardType to verify valid values
rg -t typescript -t javascript "cardType.*['\"]influencerCard['\"]" -A 2 -B 2

Length of output: 118


Script:

#!/bin/bash
# Search for BottomSheetActions component definition to understand cardType
ast-grep --pattern 'interface $_ {
  $$$
  cardType: $_
  $$$
}'

# Search for cardType prop usage
rg "cardType.*=" --type-add 'ts:*.{ts,tsx}' --type ts -A 2 -B 2

Length of output: 5247

import EmptyState from "@/components/ui/empty-state";

const CollaborationApplicationPage = (props: any) => {
const ApplicationsTabContent = (props: any) => {

ghost Nov 21, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Define proper TypeScript interface for props.

Using any type reduces type safety. Consider creating a proper interface for the props:

interface ApplicationsTabContentProps {
  pageID: string;
  collaboration: {
    // Add collaboration properties here
  };
}

const ApplicationsTabContent = (props: ApplicationsTabContentProps) => {

Comment on lines +81 to +84
if (!newLinkName || !newLinkUrl) {
Toaster.error("Please fill all fields");
return;
}

ghost Nov 21, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add URL validation

While the empty field check is good, the function should also validate that newLinkUrl is a valid URL to prevent invalid links.

Add URL validation:

 if (!newLinkName || !newLinkUrl) {
   Toaster.error("Please fill all fields");
   return;
 }
+const urlPattern = /^https?:\/\/.+/;
+if (!urlPattern.test(newLinkUrl)) {
+  Toaster.error("Please enter a valid URL starting with http:// or https://");
+  return;
+}
📝 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
if (!newLinkName || !newLinkUrl) {
Toaster.error("Please fill all fields");
return;
}
if (!newLinkName || !newLinkUrl) {
Toaster.error("Please fill all fields");
return;
}
const urlPattern = /^https?:\/\/.+/;
if (!urlPattern.test(newLinkUrl)) {
Toaster.error("Please enter a valid URL starting with http:// or https://");
return;
}

Comment on lines +37 to +44
const [promotionType, setPromotionType] = useState<SelectItem[]>([]);
const [collabType, setCollabType] = useState<SelectItem[]>([]);
const [platform, setPlatform] = useState([
{
label: "Instagram",
value: "Instagram",
},
]);

ghost Nov 21, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add validation for empty selections

While platform is initialized with a default value, promotionType and collabType start as empty arrays. This could lead to runtime errors when accessing index 0 during form submission.

Apply this diff to add default values:

- const [promotionType, setPromotionType] = useState<SelectItem[]>([]);
- const [collabType, setCollabType] = useState<SelectItem[]>([]);
+ const [promotionType, setPromotionType] = useState<SelectItem[]>([PROMOTION_TYPES[0]]);
+ const [collabType, setCollabType] = useState<SelectItem[]>([COLLAB_TYPES[0]]);

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

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (1)
app/(modal)/create-collaboration.tsx (1)

Line range hint 114-127: Convert budget values to numbers before saving

The budgetMin and budgetMax values are captured as strings from the TextInput components. To ensure correct data types in the database and prevent issues in calculations, convert these values to numbers before saving.

Apply this diff to convert the budget values to numbers:

 await addDoc(collabRef, {
   name: collaborationName,
   brandId: selectedBrand ? selectedBrand.id : "",
   managerId: AuthApp.currentUser?.uid,
   description: aboutCollab,
   timeStamp: Date.now(),
   budget: {
-    min: budgetMin,
-    max: budgetMax,
+    min: parseFloat(budgetMin),
+    max: parseFloat(budgetMax),
   },
   promotionType: promotionType[0].value,
   collaborationType: collabType[0].value,
   numberOfInfluencersNeeded: numInfluencers,
   platform: platform[0].value,
   location: {
     type: location,
     ...(location === "Physical" && {
       // existing code
     }),
   },
   externalLinks: links,
   status: "active",
 });

Additionally, add validation to ensure the budget inputs are valid numbers before submitting:

 if (
   !collaborationName ||
   !aboutCollab ||
   !budgetMin ||
   !budgetMax ||
   !promotionType ||
   !collabType ||
   !numInfluencers ||
   !platform ||
   !location
 ) {
   Toaster.error("Please fill all fields");
   return;
 }
+
+if (isNaN(parseFloat(budgetMin)) || isNaN(parseFloat(budgetMax))) {
+  Toaster.error("Please enter valid numeric values for the budget fields");
+  return;
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 8830579 and 1540a69.

📒 Files selected for processing (3)
  • app/(modal)/create-collaboration.tsx (8 hunks)
  • components/SearchComponent.tsx (3 hunks)
  • components/explore-influencers/index.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • components/SearchComponent.tsx
  • components/explore-influencers/index.tsx
🔇 Additional comments (4)
app/(modal)/create-collaboration.tsx (4)

37-44: Previous comment still applicable: Add validation for empty selections

While platform is initialized with a default value, promotionType and collabType start as empty arrays. This could lead to runtime errors when accessing index 0 during form submission.


81-84: Previous comment still applicable: Add URL validation

While the empty field check is good, the function should also validate that newLinkUrl is a valid URL to prevent invalid links.


124-127: Previous comment still applicable: Prevent potential runtime errors from empty selections

Accessing promotionType[0].value, collabType[0].value, and platform[0].value without verifying that the arrays are non-empty can lead to runtime errors if any array is empty. Ensure that each array contains at least one item before accessing its elements.


158-158: Verify compatibility of the gap property in ScrollView

The gap property used in contentContainerStyle may not be supported across all platforms or versions of React Native. This could lead to styling issues on some devices. Ensure that the gap property is compatible with your target platforms.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (1)
utils/url.ts (1)

Line range hint 17-29: Add URL encoding and proper array handling to queryParams

The current implementation has potential issues with special characters and array values in query parameters.

Consider this safer implementation:

 export const queryParams = (
   params: Partial<Record<string, string | string[]>>
 ) => {
-  let values: string[] = [];
-
-  Object.entries(params).map(([key, value]) => {
-    values.push(`${key}=${value}`);
-  });
-
-  return values.length === 0 ? "" : `?${values.join("&")}`;
+  const values = Object.entries(params)
+    .flatMap(([key, value]) => {
+      const encodedKey = encodeURIComponent(key);
+      if (Array.isArray(value)) {
+        return value.map(v => 
+          `${encodedKey}=${encodeURIComponent(v)}`
+        );
+      }
+      return [`${encodedKey}=${encodeURIComponent(value)}`];
+    });
+  return values.length === 0 ? "" : `?${values.join("&")}`;
 };

This enhancement:

  1. Properly encodes parameter keys and values
  2. Correctly handles array values by creating multiple parameters
  3. Uses functional programming patterns for cleaner code
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 1540a69 and 68763ec.

📒 Files selected for processing (4)
  • components/collaboration/collaboration-details/ApplicationsTabContent.tsx (4 hunks)
  • components/explore-influencers/index.tsx (3 hunks)
  • styles/InfluencerCard.styles.tsx (3 hunks)
  • utils/url.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • components/collaboration/collaboration-details/ApplicationsTabContent.tsx
  • components/explore-influencers/index.tsx
  • styles/InfluencerCard.styles.tsx
🔇 Additional comments (1)
utils/url.ts (1)

Line range hint 1-29: Verify usage patterns and security implications

Let's verify how these utilities are used across the codebase to ensure the changes won't introduce breaking changes.

✅ Verification successful

Let me try another verification approach with correct file type flags.


Let me try one final approach using more basic commands without file type restrictions.


URL utilities are used consistently and safely across the codebase

The verification reveals:

  • imageUrl is used consistently for image sources in UI components (InfluencerCard, EmptyState, Carousel, CollaborationHeader)
  • queryParams is used only once for route construction in the app layout
  • The ph:// protocol handling is properly isolated in the URL utility
  • No unsafe URL constructions or manipulations were found

The implementation and usage patterns are consistent with common security practices:

  • URL protocol checking is properly implemented
  • Image URLs are properly sanitized before usage
  • Query parameter construction is done safely
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check usage patterns of imageUrl and queryParams
echo "Checking imageUrl usage patterns..."
rg -A 2 "imageUrl\(" --type typescript

echo "\nChecking queryParams usage patterns..."
rg -A 2 "queryParams\(" --type typescript

echo "\nChecking for potentially unsafe URL handling..."
ast-grep --pattern 'image.startsWith("http")' --lang typescript

Length of output: 576


Script:

#!/bin/bash
# Check usage patterns with correct file extensions
echo "Checking imageUrl usage patterns..."
rg -A 2 "imageUrl\(" --type ts --type tsx

echo "\nChecking queryParams usage patterns..."
rg -A 2 "queryParams\(" --type ts --type tsx

# Check for URL construction and handling
echo "\nChecking URL construction patterns..."
rg -A 2 "require\(.*\.jpg\)" --type ts --type tsx

# Check for ph:// protocol usage
echo "\nChecking ph:// protocol usage..."
rg "ph://" --type ts --type tsx

Length of output: 626


Script:

#!/bin/bash
# Find all TypeScript/JavaScript files first
echo "Finding all TypeScript and JavaScript files..."
fd "\.(ts|tsx|js|jsx)$"

# Check usage patterns without file type restrictions
echo "\nChecking imageUrl usage patterns..."
rg "imageUrl\(" -A 2

echo "\nChecking queryParams usage patterns..."
rg "queryParams\(" -A 2

# Check URL patterns
echo "\nChecking URL construction patterns..."
rg "require\(.*\.jpg\)" -A 1

# Check protocol usage
echo "\nChecking protocol usage..."
rg "ph://" 

Length of output: 7841

Comment thread utils/url.ts
Comment on lines +1 to +15
export const imageUrl = (image: string | NodeRequire | undefined) => {
if (
image &&
typeof image === "string" &&
(image.startsWith("http") || image.startsWith("ph://"))
) {
return {
uri: image,
};
} else if (image) {
return image;
} else {
return require("@/assets/images/placeholder-image.jpg");
}
};

ghost Nov 22, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Handle protocol-relative URLs and improve URL validation

The current implementation doesn't handle protocol-relative URLs (starting with //) which are common in modern web applications. Also, the ph:// protocol handling needs validation.

Consider this enhancement:

 export const imageUrl = (image: string | NodeRequire | undefined) => {
   if (
     image &&
     typeof image === "string" &&
-    (image.startsWith("http") || image.startsWith("ph://"))
+    (image.startsWith("http") || image.startsWith("ph://") || 
+     (image.startsWith("//") && image.length > 2))
   ) {
+    // Convert protocol-relative URLs to https
+    const uri = image.startsWith("//") ? `https:${image}` : image;
     return {
-      uri: image,
+      uri,
     };
   } else if (image) {
     return image;
   } else {
     return require("@/assets/images/placeholder-image.jpg");
   }
 };
📝 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
export const imageUrl = (image: string | NodeRequire | undefined) => {
if (
image &&
typeof image === "string" &&
(image.startsWith("http") || image.startsWith("ph://"))
) {
return {
uri: image,
};
} else if (image) {
return image;
} else {
return require("@/assets/images/placeholder-image.jpg");
}
};
export const imageUrl = (image: string | NodeRequire | undefined) => {
if (
image &&
typeof image === "string" &&
(image.startsWith("http") || image.startsWith("ph://") ||
(image.startsWith("//") && image.length > 2))
) {
// Convert protocol-relative URLs to https
const uri = image.startsWith("//") ? `https:${image}` : image;
return {
uri,
};
} else if (image) {
return image;
} else {
return require("@/assets/images/placeholder-image.jpg");
}
};

@dev-rathore
dev-rathore merged commit cc9b00c into dev Nov 22, 2024
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.

2 participants