Skip to content

added integration - #11

Merged
Manan-Sharma-5 merged 2 commits into
devfrom
feature/firebase-flow-integration
Oct 30, 2024
Merged

Manan-Sharma-5 merged 2 commits into
devfrom
feature/firebase-flow-integration

Conversation

@Manan-Sharma-5

@Manan-Sharma-5 Manan-Sharma-5 commented Oct 30, 2024 •

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced theme adaptability for input fields in SignUp, Login, and Onboarding screens.
    • Introduced tabbed navigation in Collaboration Details for improved user experience.
    • New components for managing collaboration applications and invitations.
  • Bug Fixes

    • Improved data fetching logic for proposals and invitations, ensuring accurate display of information.
  • Style

    • Added and modified styles for better layout and modal interactions across various components.
  • Documentation

    • Updated context providers to streamline brand selection and improve data handling.

@coderabbitai

coderabbitai Bot commented Oct 30, 2024 •

Copy link
Copy Markdown

Walkthrough

This pull request introduces several modifications across multiple files, primarily focusing on enhancing the dynamic handling of UI components, particularly TextInput elements, by integrating a theme-based color system. New components for managing collaboration applications and invitations are added, alongside a tabbed interface for improved navigation within the CollaborationDetailsScreen. Additionally, context providers are updated, and various styling enhancements are implemented to improve the user interface.

Changes

File Path Change Summary
app/(auth)/create-new-account.tsx, app/(auth)/forget-password.tsx, app/(auth)/login.tsx Added import for Colors and updated TextInput theme properties to use dynamic colors based on the current theme.
app/(main)/(screens)/collaboration-details/[pageID].tsx Introduced activeTab state for tabbed navigation and modified rendering logic to conditionally display content.
app/(main)/(screens)/collaboration-details/applications.tsx Added CollaborationApplicationPage component to display influencer applications, with data fetching from Firestore.
app/(main)/(screens)/collaboration-details/invitations.tsx Added CollaborationInvitationPage component for managing collaboration invitations with Firestore integration.
app/_layout.tsx Added BrandContextProvider to the component hierarchy for improved context management.
app/modal.tsx Updated document creation to utilize selectedBrand.id and added a new status field in the collaboration document.
app/onboarding-brand-preference.tsx Modified handleSubmit to create a Firestore document with user preferences.
app/onboarding-get-started.tsx Updated handleSubmit to save brand survey responses in Firestore.
app/onboarding-your-brand.tsx Updated TextInput theme properties to use dynamic colors based on the current theme.
components/BottomSheetActions.tsx Expanded functionality to handle new card types and actions, enhancing user interaction.
components/InfluencerCard.tsx Introduced InfluencerCardPropsType for improved type safety and added conditional rendering for invitation status.
components/collaboration/CollaborationCard.tsx Added acceptedApplications property to display dynamic application counts.
components/collaboration/CollaborationDetails.tsx Implemented a tabbed interface for navigation between different collaboration views.
components/explore-influencers/index.tsx Added type prop to InfluencerCard and updated filtering logic for influencers.
components/proposals/Applications.tsx Refined proposal fetching logic to include brand context and enhance data structure.
components/proposals/Invitations.tsx Streamlined component to focus on proposals instead of invitations, updating state management accordingly.
contexts/brand-context.provider.tsx Improved code readability and consistency in the BrandContextProvider component.
styles/CollaborationDetails.styles.tsx Added new styles for modal components and improved layout styling for collaboration details.

Possibly related PRs

  • added firebase auth #7: Involves modifications to create-new-account.tsx, forget-password.tsx, and login.tsx, focusing on dynamic handling of input fields related to user authentication.
  • bug fixes #10: Updates in InfluencerCard.tsx include changes to media handling and user interaction features, potentially relating to overall user experience improvements.

Suggested reviewers

  • dev-rathore

Poem

🐰 In the garden where colors bloom,
The inputs now dance, dispelling gloom.
Tabs for navigation, oh what a sight,
Collaboration invites, taking flight!
With a hop and a skip, we code away,
Enhancing our app, come join the play! 🌼


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

🧹 Outside diff range and nitpick comments (37)
app/(main)/(screens)/collaboration-details/[pageID].tsx (1)

76-77: Add styles for the tabbed interface.

The styles object is empty but will be needed for the new UI components.

Add these styles:

 const styles = StyleSheet.create({
+  tabContainer: {
+    flexDirection: 'row',
+    paddingHorizontal: 16,
+    paddingVertical: 8,
+    gap: 8,
+  },
+  tab: {
+    borderRadius: 20,
+  },
 });
app/(auth)/forget-password.tsx (2)

57-57: Consider simplifying the theme prop structure

While the color implementation is correct, the theme prop structure could be more concise.

Consider this simplified version:

-        theme={{ colors: { primary: Colors(theme).text } }}
+        theme={{ colors: { primary: Colors(theme).text, onSurfaceVariant: Colors(theme).text } }}

Adding onSurfaceVariant will ensure consistent coloring of both the label and input text.


Line range hint 20-35: Enhance error handling and user feedback

The password reset implementation could benefit from some security and UX improvements:

Consider these enhancements:

  1. Add loading state during API call:
 const handleResetPassword = () => {
+  const [isLoading, setIsLoading] = React.useState(false);
   const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

   if (!emailRegex.test(email)) {
     alert("Please enter a valid email address");
     return;
   }

+  setIsLoading(true);
   sendPasswordResetEmail(AuthApp, email)
     .then(() => {
       Toaster.success("Password reset email sent successfully");
       router.navigate("/(auth)/login");
     })
     .catch((error) => {
-      Toaster.error(error.message);
+      // More specific error messages
+      const errorMessage = error.code === 'auth/user-not-found' 
+        ? 'No account found with this email'
+        : 'Failed to send reset email. Please try again.';
+      Toaster.error(errorMessage);
     })
+    .finally(() => setIsLoading(false));
 };

Then update the Button component:

 <Button
   mode="contained"
   onPress={() => handleResetPassword()}
   style={styles.button}
   labelStyle={styles.buttonText}
+  loading={isLoading}
+  disabled={isLoading || !email}
 >
   Reset Password
 </Button>
app/(auth)/login.tsx (1)

Line range hint 11-58: Consider implementing a dedicated theme abstraction

The current implementation directly uses the Colors utility for theming. Consider creating a dedicated theme hook or provider that:

  1. Centralizes theme logic and provides type-safe theme tokens
  2. Handles common component themes (like input fields)
  3. Ensures consistent contrast ratios and accessibility

This would make the theming system more maintainable and easier to update across the application.

Example approach:

// hooks/useAppTheme.ts
export const useAppTheme = () => {
  const theme = useTheme();
  return {
    input: {
      colors: {
        primary: Colors(theme).text,
        // other input-specific theme tokens
      }
    },
    // other component-specific theme configurations
  };
};
app/(auth)/create-new-account.tsx (1)

Add client-side validation before form submission

While the auth context has basic empty field checks and handles Firebase auth errors, the sign-up form should validate input before making the API call:

  1. Add password match validation:
if (password !== confirmPassword) {
  Toaster.error("Passwords do not match");
  return;
}
  1. Add password strength validation:
const passwordRegex = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/;
if (!passwordRegex.test(password)) {
  Toaster.error("Password must be at least 8 characters long and contain both letters and numbers");
  return;
}
  1. Add email format validation:
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
  Toaster.error("Please enter a valid email address");
  return;
}
  1. Add name validation:
if (name.trim().length < 2) {
  Toaster.error("Name must be at least 2 characters long");
  return;
}
🔗 Analysis chain

Line range hint 11-85: Add input validation for security

The form handles sensitive data but lacks visible input validation. Consider adding:

  1. Email format validation
  2. Password strength requirements
  3. Password match validation before submission
  4. Name field validation (prevent empty/special characters)

Let me search for any existing validation-related code or utilities in the authentication context.


Let me check the auth context provider to see if there's any existing validation logic.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if validation utilities already exist in the codebase
rg -g '!node_modules' -g '*.{ts,tsx}' -i 'validate|validation|validator'

Length of output: 118


Script:

#!/bin/bash
# Search for auth-related files and their content
fd -e ts -e tsx -e js -e jsx auth --exec rg -l 'validation|validator|validate|password|email'

# Search for any utility functions that might handle validation
fd -e ts -e tsx -e js -e jsx util --exec rg -l 'validation|validator|validate|password|email'

# Look for any existing form validation implementations
rg -g '*.{ts,tsx,js,jsx}' 'function.*validate|const.*validate'

Length of output: 293


Script:

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

# Look for any error handling or validation in auth-related files
rg -g '*.{ts,tsx}' -B 3 -A 3 'error|invalid|validate' contexts/

Length of output: 11436

components/collaboration/CollaborationCard.tsx (1)

Line range hint 25-116: Consider component optimization and separation of concerns

The JobCard component handles multiple card types and responsibilities. Consider these architectural improvements:

  1. Split into separate components per card type
  2. Memoize the component to prevent unnecessary re-renders
  3. Extract chip section into a separate component

Example implementation:

// ChipSection.tsx
const ChipSection = memo(({ applications, invitations, acceptedApplications }: ChipSectionProps) => (
  <View style={styles.chipRow}>
    <Chip style={styles.chip} mode="outlined">
      Applied: {applications?.toLocaleString() ?? 0}
    </Chip>
    {/* ... other chips ... */}
  </View>
));

// CollaborationCard.tsx
const JobCard = memo((props: CollaborationAdCardProps) => {
  // ... existing code ...
  return (
    <Card>
      {/* ... */}
      <ChipSection
        applications={props.applications}
        invitations={props.invitations}
        acceptedApplications={props.acceptedApplications}
      />
      {/* ... */}
    </Card>
  );
});
components/explore-influencers/index.tsx (2)

Line range hint 24-26: Remove or properly implement the commented influencer type filtering.

The commented-out isInfluencerTypeMatch creates an inconsistency where the UI still shows and accepts influencer type filters (via CollaborationFilter component) but the filtering logic ignores it. This could confuse users who try to filter by influencer type and see no effect.

Either:

  1. Remove the influencer type filtering completely (including from CollaborationFilter props), or
  2. Re-implement the filtering with proper feature flags.
- // const isInfluencerTypeMatch =
- //   currentInfluencerType === "All" ||
- //   influencer.influencerType === currentInfluencerType;

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

  return (
    isCollaborationTypeMatch &&
-   // isInfluencerTypeMatch &&
+   isInfluencerTypeMatch &&
    isFollowersRangeMatch &&

Also applies to: 52-53


Line range hint 17-23: Define constants for filter range values.

The code uses hardcoded magic numbers for range filters with inconsistent maximum values (1000000, 10000000, 100000). This makes maintenance difficult and could lead to confusion.

Consider defining constants for these values:

const FILTER_RANGES = {
  FOLLOWERS: {
    MIN: 0,
    MAX: 1_000_000
  },
  REACH: {
    MIN: 0,
    MAX: 1_000_000
  },
  ENGAGEMENT: {
    MIN: 0,
    MAX: 100_000
  }
} as const;

// Then use them in state initialization and CollaborationFilter props
const [currentFollowersRange, setCurrentFollowersRange] = useState([
  FILTER_RANGES.FOLLOWERS.MIN,
  FILTER_RANGES.FOLLOWERS.MAX
]);

Also applies to: 106-110

app/_layout.tsx (1)

68-72: Consider documenting context provider ordering requirements.

While the integration is correct, it would be beneficial to:

  1. Document the context hierarchy requirements in the codebase
  2. Add comments explaining why BrandContextProvider needs to wrap AutocompleteDropdownContextProvider (if there are specific dependencies)

Consider adding a comment above the context providers explaining the hierarchy:

              <CloudMessagingContextProvider>
+               {/* BrandContext wraps AutocompleteDropdown to ensure brand data is available for dropdowns */}
                <BrandContextProvider>
                  <AutocompleteDropdownContextProvider>
app/(main)/(screens)/collaboration-details/applications.tsx (1)

69-82: Extract duplicate styles to a common styles object.

The loading state view uses inline styles that are duplicated in the empty state view.

Consider extracting these styles:

const commonStyles = StyleSheet.create({
  centerContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    gap: 40,
  },
});

// Use in both loading and empty states
<View style={commonStyles.centerContainer}>
app/onboarding-brand-preference.tsx (4)

Line range hint 58-77: Add validation, loading state, and type safety to Firestore operation

The Firestore operation needs several improvements for robustness:

  1. The brandId from params should be validated before use
  2. Add a loading state during the async operation
  3. Add proper TypeScript types for the preferences structure
  4. Consider atomic updates to prevent race conditions

Here's a suggested implementation:

+ interface BrandPreferences {
+   promotionType: string[];
+   influencerCategory: string[];
+ }

- const handleSubmit = () => {
+ const handleSubmit = async () => {
+   const [isLoading, setIsLoading] = useState(false);
  try {
    const { brandId } = params;
-   if (brandId) {
+   if (!brandId || typeof brandId !== 'string') {
+     throw new Error('Invalid brand ID');
+   }
+   setIsLoading(true);
    const brandRef = doc(FirestoreDB, "brands", brandId);
-   setDoc(
+   await setDoc(
      brandRef,
      {
        preferences: {
          promotionType: Object.keys(promotionType).filter(
            (key) => promotionType[key]
          ),
          influencerCategory: Object.keys(influencerType).filter(
            (key) => influencerType[key]
          ),
        },
      } as { preferences: BrandPreferences },
      { merge: true }
    );
    router.push({
      pathname: "/onboarding-get-started",
      params: { brandId },
    });
  } catch (err) {
    console.error(err);
+   // Add proper error handling/user feedback
+   Alert.alert('Error', 'Failed to save preferences');
+ } finally {
+   setIsLoading(false);
  }
};

Line range hint 58-77: Enhance error handling and input validation

The current error handling is insufficient for a production environment:

  1. Add input validation to ensure at least one option is selected
  2. Provide user feedback for errors
  3. Consider retry logic for network failures

Add validation before submission:

const handleSubmit = async () => {
+ const hasSelectedPromotion = Object.values(promotionType).some(v => v);
+ const hasSelectedInfluencer = Object.values(influencerType).some(v => v);
+ 
+ if (!hasSelectedPromotion || !hasSelectedInfluencer) {
+   Alert.alert(
+     'Incomplete Selection',
+     'Please select at least one option from each category'
+   );
+   return;
+ }

  try {
    // ... existing code ...
  } catch (err) {
-   console.error(err);
+   console.error('Failed to save preferences:', err);
+   if (err instanceof FirebaseError) {
+     switch (err.code) {
+       case 'permission-denied':
+         Alert.alert('Error', 'You don\'t have permission to perform this action');
+         break;
+       case 'unavailable':
+         Alert.alert('Error', 'Service temporarily unavailable. Please try again');
+         break;
+       default:
+         Alert.alert('Error', 'Failed to save preferences. Please try again');
+     }
+   }
  }
};

Line range hint 58-77: Improve user feedback and interaction patterns

The current implementation lacks proper user feedback mechanisms:

  1. Add loading indicator during submission
  2. Show success feedback before navigation
  3. Add confirmation dialog for the skip button

Consider these UX improvements:

+ const [isSubmitting, setIsSubmitting] = useState(false);

const handleSignUp = () => {
+ Alert.alert(
+   'Skip Preferences',
+   'Are you sure you want to skip setting your preferences?',
+   [
+     { text: 'Cancel', style: 'cancel' },
+     { 
+       text: 'Skip', 
+       onPress: () => firebaseSignIn(AuthApp.currentUser?.uid || "") 
+     },
+   ]
+ );
- firebaseSignIn(AuthApp.currentUser?.uid || "");
};

const handleSubmit = async () => {
  try {
+   setIsSubmitting(true);
    // ... existing code ...
+   // Show success message
+   Toast.show({
+     type: 'success',
+     text1: 'Preferences saved successfully',
+     position: 'bottom',
+   });
    // Wait for animation
+   await new Promise(resolve => setTimeout(resolve, 1000));
    router.push({
      pathname: "/onboarding-get-started",
      params: { brandId },
    });
  } catch (err) {
    // ... error handling ...
  } finally {
+   setIsSubmitting(false);
  }
};

Update the submit button to show loading state:

 <Button
   mode="contained"
   onPress={handleSubmit}
+  loading={isSubmitting}
+  disabled={isSubmitting}
   buttonColor={Colors(theme).primary}
   labelStyle={styles.submitButtonLabel}
   contentStyle={styles.submitButtonContent}
 >
-  Submit
+  {isSubmitting ? 'Saving...' : 'Submit'}
 </Button>

Based on the gathered information from the executed scripts, I can now generate the final response:

Security measures needed for brand data modifications

The current implementation lacks proper security controls:

  1. The auth context shows user authentication is implemented, but there's no validation that the authenticated user owns the brand being modified
  2. The onboarding flow (onboarding-your-brand → onboarding-brand-preference → onboarding-get-started) doesn't verify brand ownership between steps
  3. Direct Firestore writes without security rules validation were found

Required changes:

  • Add ownership validation before allowing brand preference updates
  • Implement proper error handling for unauthorized modifications
  • Add server-side security rules to enforce user-brand relationship

Example implementation:

// Before setDoc, add validation
const brandDoc = await getDoc(brandRef);
if (!brandDoc.exists() || brandDoc.data().managerId !== AuthApp.currentUser?.uid) {
  throw new Error('Unauthorized brand modification');
}
🔗 Analysis chain

Line range hint 58-77: Implement security measures for data access

The current implementation has several security concerns:

  1. No verification that the user has permission to modify the specified brand
  2. Potential for unauthorized access through URL manipulation
  3. Missing rate limiting for submissions

Let's verify the security rules:

Consider implementing these security measures:

  1. Add a server-side check to verify brand ownership
  2. Implement rate limiting for submissions
  3. Add request validation middleware

Example security rules structure:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /brands/{brandId} {
      allow write: if request.auth != null 
        && request.auth.uid == resource.data.ownerId
        && request.time - resource.data.lastUpdate > duration.value(1, 'm');
    }
  }
}
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check Firestore security rules for brands collection
rg -A 10 "match /brands/{" 

Length of output: 122


Script:

#!/bin/bash
# Let's try different approaches to find security rules
# Look for firestore.rules or similar files
fd -e rules

# Search for security-related configurations
rg -i "firestore.*rules"

# Look for any firebase config files
fd -e json -x grep -l "firebase" {}

# Check for any security-related imports or configurations in the codebase
rg -i "firebase.*auth|security|rules"

Length of output: 2970


Script:

#!/bin/bash
# Let's check the auth context implementation and usage
cat utils/auth.ts
cat contexts/auth-context.provider.tsx

# Check if there's any auth middleware or protected routes
rg -i "middleware|protected.*route" -A 5

# Look for any auth checks around brand operations
ast-grep --pattern 'doc(FirestoreDB, "brands"'

Length of output: 40582


Script:

#!/bin/bash
# Let's check for any auth checks or route protection in the app
rg -B 5 -A 5 "onboarding-brand-preference|onboarding-get-started" app/

# Check for any brand-related operations and their auth context
ast-grep --pattern 'setDoc($_, { preferences: $_'

# Look for any route protection mechanisms
fd -e tsx -e ts -x grep -l "protect|auth.*route|require.*auth"

Length of output: 1425

app/onboarding-get-started.tsx (3)

Line range hint 46-60: Remove @ts-ignore and add proper type safety.

The @ts-ignore comment suppresses potentially important type checking. Instead, properly type the parameters and validate the brandId.

Apply this diff to improve type safety:

- //@ts-ignore
- const brandRef = doc(FirestoreDB, "brands", brandId);
+ if (typeof brandId !== 'string') {
+   throw new Error('Invalid brandId');
+ }
+ const brandRef = doc(FirestoreDB, "brands", brandId);

Line range hint 46-60: Add proper error handling and loading state.

The current implementation lacks proper error handling and user feedback. Consider adding loading state and error messages.

Here's a suggested implementation:

+ const [isSubmitting, setIsSubmitting] = useState(false);
+ const [error, setError] = useState<string | null>(null);

  const handleSubmit = async () => {
+   setIsSubmitting(true);
+   setError(null);
    try {
      const { brandId } = params;
      if (!brandId) {
+       throw new Error('Brand ID is required');
      }
+     if (!hearAboutUs || !useFor || !volumeOfCollaboration) {
+       throw new Error('Please fill in all fields');
+     }
      const brandRef = doc(FirestoreDB, "brands", brandId as string);
-     setDoc(
+     await setDoc(
        brandRef,
        {
          survey: {
            source: hearAboutUs,
            purpose: useFor,
            collaborationValue: volumeOfCollaboration,
          },
        },
        { merge: true }
      );
      router.push("/(main)/explore-influencers");
    } catch (error) {
-     console.log(error);
+     setError(error instanceof Error ? error.message : 'An error occurred');
+     // Consider showing an error toast or alert here
    } finally {
+     setIsSubmitting(false);
    }
  };

Line range hint 46-60: Consider implementing route constants.

The navigation path is hardcoded, which could lead to maintenance issues if routes change.

Create a routes constant file and reference it:

// constants/routes.ts
export const ROUTES = {
  EXPLORE_INFLUENCERS: '/(main)/explore-influencers'
} as const;

// Then in your component:
import { ROUTES } from '@/constants/routes';
// ...
router.push(ROUTES.EXPLORE_INFLUENCERS);
app/onboarding-your-brand.tsx (3)

Line range hint 29-39: Add file upload constraints and error handling

The current image picker implementation needs additional safeguards:

  1. File size limits to prevent excessive storage usage
  2. Stricter file type validation
  3. Optimized quality settings for performance

Consider implementing these constraints:

 const pickImage = async () => {
+  const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB limit
   let result = await ImagePicker.launchImageLibraryAsync({
     mediaTypes: ImagePicker.MediaTypeOptions.Images,
     allowsEditing: true,
     aspect: [4, 3],
-    quality: 1,
+    quality: 0.8,
   });
   if (!result.canceled) {
+    const { uri, fileSize } = result.assets[0];
+    if (fileSize && fileSize > MAX_FILE_SIZE) {
+      Toast.show({
+        type: 'error',
+        text1: 'Error',
+        text2: 'Image size should be less than 5MB'
+      });
+      return;
+    }
     setImage(result.assets[0].uri);
   }
 };

Line range hint 82-108: Enhance form submission with proper loading states and error handling

The form submission process needs improvements in several areas:

  1. Loading state management
  2. Comprehensive error handling
  3. Transaction safety for Firebase operations

Consider implementing these improvements:

+ const [isSubmitting, setIsSubmitting] = useState(false);

 const handleSubmit = async () => {
+  setIsSubmitting(true);
   try {
     const user = AuthApp.currentUser;
-    if (!brandName || !role || !industry || !website) {
+    if (!brandName?.trim() || !role?.trim() || !industry?.trim() || !website?.trim()) {
       Toaster.error("Please fill in all fields.");
       return;
     }
+    if (!website.match(/^https?:\/\/.+\..+$/)) {
+      Toaster.error("Please enter a valid website URL");
+      return;
+    }
     if (user) {
+      // Start a batch write
+      const batch = writeBatch(FirestoreDB);
       const colRef = collection(FirestoreDB, "brands");
       
       let brandData = {
         name: brandName,
         image: "",
         profile: {
           industry,
           website,
         },
+        createdAt: serverTimestamp(),
       };

       const docRef = await addDoc(colRef, brandData);
       const managerRef = doc(FirestoreDB, "brands", docRef.id, "members", user.uid);
-      await setDoc(managerRef, {
+      batch.set(managerRef, {
         managerId: user.uid,
         role,
+        joinedAt: serverTimestamp(),
       });
+      await batch.commit();
       
       router.navigate({
         pathname: "/onboarding-brand-preference",
         params: { brandId: docRef.id },
       });
     }
   } catch (error) {
-    console.error("Error creating brand: ", error);
+    Toaster.error("Failed to create brand. Please try again.");
+    console.error("Error creating brand:", error);
+  } finally {
+    setIsSubmitting(false);
   }
 };

Then update the submit button:

 <Button
   mode="contained"
   onPress={handleSubmit}
   style={styles.submitButton}
   buttonColor={Colors(theme).primary}
   textColor={Colors(theme).text}
+  disabled={isSubmitting}
 >
-  Submit
+  {isSubmitting ? 'Creating...' : 'Submit'}
 </Button>

Line range hint 146-186: Optimize autocomplete components for better performance

The current autocomplete implementation could benefit from performance optimizations:

  1. Debounced search
  2. Memoized filter results
  3. Virtual list rendering for large datasets

Consider these optimizations:

+ import { useMemo } from 'react';
+ import debounce from 'lodash/debounce';
+ import { VirtualizedList } from 'react-native';

 const OnboardingScreen = () => {
+  const debouncedFilterRoles = useMemo(
+    () =>
+      debounce((text: string) => {
+        const filtered = roles.filter((item) =>
+          item.toLowerCase().startsWith(text.toLowerCase())
+        );
+        setFilteredRoles(filtered);
+      }, 300),
+    []
+  );

   const filterRoles = useCallback((text: string) => {
     setRole(text);
-    const filtered = roles.filter((item) =>
-      item.toLowerCase().startsWith(text.toLowerCase())
-    );
-    setFilteredRoles(filtered);
+    debouncedFilterRoles(text);
     setActiveDropdown("role");
   }, []);

+  const renderItem = useCallback(
+    ({ item }: { item: string }) => (
+      <TouchableOpacity
+        onPress={() => {
+          setRole(item);
+          setFilteredRoles([]);
+          setActiveDropdown(null);
+        }}
+      >
+        <Text style={styles.itemText}>{item}</Text>
+      </TouchableOpacity>
+    ),
+    []
+  );

   // Similar optimizations for industry autocomplete

Also applies to: 188-213

components/collaboration/CollaborationDetails.tsx (1)

Line range hint 128-136: Document the reason for commented-out Apply Now button.

There's a commented-out Apply Now button without explanation. If this is temporary, add a TODO comment explaining why it's disabled and when it should be re-enabled.

components/InfluencerCard.tsx (1)

35-55: Consider stricter type definitions for numeric values.

The interface is well-structured, but some numeric properties allow string values which could lead to type-related issues:

followers: number | string;
reach: number | string;
rating: number | string;
jobsCompleted: number | string;
successRate: number | string;

Consider using only number type for these properties and handling the string-to-number conversion at the data layer instead. This ensures type safety throughout the component.

app/modal.tsx (4)

Line range hint 64-75: Refactor duplicate validation logic.

The form validation logic is duplicated between the "Next" button click handler and submitCollaboration function. This increases maintenance burden and risk of inconsistencies.

Extract validation into a separate function:

interface CollaborationForm {
  collaborationName: string;
  aboutCollab: string;
  budgetMin: string;
  budgetMax: string;
  promotionType: string;
  collabType: string;
  numInfluencers: number;
  platform: string;
  location: string;
}

const validateForm = (form: CollaborationForm): string | null => {
  if (!form.collaborationName || !form.aboutCollab || /* ... */) {
    return "Please fill all fields";
  }
  return null;
};

Then use it in both places:

const error = validateForm(formData);
if (error) {
  Toaster.error(error);
  return;
}

Also applies to: 196-209


Line range hint 76-105: Enhance submission handling with proper feedback.

The submission logic lacks proper success feedback and error handling.

Add success feedback and improve error handling:

 try {
   const docRef = await addDoc(collabRef, {
     // ... existing fields
   });
+  Toaster.success("Collaboration created successfully!");
+  // Consider navigation after success
+  navigation.goBack();
 } catch (error) {
-  console.error(error);
+  Toaster.error("Failed to create collaboration. Please try again.");
+  console.error("Create collaboration error:", error);
 }

Line range hint 167-174: Enhance selection UI and map interaction.

The current UI for platform selection and location picking could be improved for better user experience:

  1. Platform selection could use icons and more visual appeal
  2. Map selection lacks search capability and location context

Consider these improvements:

// For platform selection
interface Platform {
  value: string;
  label: string;
  icon: string;
}

const PLATFORMS: Platform[] = [
  { value: 'Instagram', label: 'Instagram', icon: 'instagram' },
  { value: 'YouTube', label: 'YouTube', icon: 'youtube' }
];

// Replace RadioButton.Group with custom component
<View style={styles.platformGrid}>
  {PLATFORMS.map(platform => (
    <TouchableOpacity
      key={platform.value}
      style={[
        styles.platformCard,
        platform.value === selectedPlatform && styles.platformCardSelected
      ]}
      onPress={() => setPlatform(platform.value)}
    >
      <IconButton icon={platform.icon} />
      <Text>{platform.label}</Text>
    </TouchableOpacity>
  ))}
</View>

For the map:

// Add search capability
<GooglePlacesAutocomplete
  placeholder="Search for location"
  onPress={(data, details) => {
    setMapRegion({
      latitude: details.geometry.location.lat,
      longitude: details.geometry.location.lng,
      latitudeDelta: 0.0922,
      longitudeDelta: 0.0421,
    });
  }}
/>

Also applies to: 235-246


Line range hint 76-105: Add loading state during submission.

The form submission lacks visual feedback during the async operation.

Add loading state:

 const [screen, setScreen] = useState(1);
+const [isSubmitting, setIsSubmitting] = useState(false);

 const submitCollaboration = async () => {
+  setIsSubmitting(true);
   try {
     // ... existing submission logic
   } catch (error) {
     // ... error handling
+  } finally {
+    setIsSubmitting(false);
   }
 };

 // Update submit button
-<Button mode="contained" onPress={() => submitCollaboration()}>
+<Button 
+  mode="contained" 
+  onPress={() => submitCollaboration()}
+  loading={isSubmitting}
+  disabled={isSubmitting}
+>
   Post
 </Button>
components/proposals/Invitations.tsx (4)

29-29: Remove the unused state variable 'notPendingProposals'

The state variable notPendingProposals is declared but not utilized elsewhere in the code. Removing it will help keep the codebase clean and maintainable.

Apply this diff to eliminate the unused variable:

-  const [notPendingProposals, setNotPendingProposals] = useState<number>();

112-114: Include 'selectedBrand' in the dependency array of useEffect

The fetchProposals function depends on selectedBrand. To ensure that proposals are fetched whenever the selected brand changes, include selectedBrand in the dependency array of useEffect.

Update the dependency array as follows:

 useEffect(() => {
   fetchProposals();
- }, [user]);
+ }, [user, selectedBrand]);

Line range hint 135-177: Update condition to check 'filteredProposals.length' instead of 'proposals.length'

The conditional rendering currently checks proposals.length === 0, but since you're displaying filteredProposals, the condition should reflect that to accurately display the "No Collaborations found" message when there are no inactive proposals.

Modify the condition as shown:

-      {proposals.length === 0 ? (
+      {filteredProposals.length === 0 ? (

Line range hint 213-219: Ensure 'selectedCollabId' is defined when rendering 'BottomSheetActions'

Passing an empty string as cardId may cause issues in BottomSheetActions. Ensure that selectedCollabId is not null or undefined when isVisible is true.

Update the conditional rendering to check for selectedCollabId:

-      {isVisible && (
+      {isVisible && selectedCollabId && (
         <BottomSheetActions
-          cardId={selectedCollabId || ""}
+          cardId={selectedCollabId}
           cardType="influencerCard"
           isVisible={isVisible}
           onClose={closeBottomSheet}
           snapPointsRange={["20%", "50%"]}
           key={selectedCollabId}
         />
       )}
components/proposals/Applications.tsx (1)

215-217: Adjust 'paddingBottom' for improved responsiveness

The paddingBottom: 100 in contentContainerStyle may not be sufficient on devices with different screen sizes or aspect ratios. Consider using responsive units or SafeAreaView to ensure consistent spacing across all devices.

components/BottomSheetActions.tsx (4)

77-79: Replace console.log with proper error handling or logging mechanism.

Using console.log for error output is not recommended in production code. Consider using a logging library or providing more user-friendly error feedback.

Apply this diff to use a toast notification for the error:

- console.log(error);
+ Toaster.error("An unexpected error occurred. Please try again.");

99-101: Replace console.log with proper error handling or logging mechanism.

In the handleRejectApplication function, replace console.log(error); with a more appropriate error handling approach.

Apply this diff:

- console.log(error);
+ Toaster.error("An unexpected error occurred. Please try again.");

115-117: Replace console.log with proper error handling or logging mechanism.

In the delistCollaboration function, consider replacing console.log(error); with a user-facing error message or a logging solution.

Apply this diff:

- console.log(error);
+ Toaster.error("An unexpected error occurred. Please try again.");

46-46: Remove unused state variable message if not needed.

The message state variable is declared but not utilized in the component. If it is unnecessary, consider removing it to keep the code clean. If it's intended for future use, ensure it is implemented appropriately.

Apply this diff to remove the unused state variable:

 const [isMessageModalVisible, setIsMessageModalVisible] =
   React.useState(false);
- const [message, setMessage] = React.useState("");
app/(main)/(screens)/collaboration-details/invitations.tsx (2)

128-133: Handle errors appropriately without using console.log in production code.

Using console.log for error logging is not recommended in production as it can expose sensitive information and isn't user-friendly. Consider displaying user-friendly error messages and logging errors using a monitoring service.

Replace console.log(error); with appropriate error handling:

try {
  await addDoc(invitationRef, invitationPayload);
  setIsMessageModalVisible(false);
  Toaster.success("Invitation sent successfully");
  fetchInfluencers();
  fetchInvitations();
} catch (error) {
- console.log(error);
  Toaster.error("Failed to send invitation");
  // Optionally, log error to a monitoring service
}

121-126: Simplify invitationPayload by using props.pageID directly for collaborationId.

Since collaborationId is the same as props.pageID, you can use it directly, reducing dependency on selectedInfluencer and preventing potential mismatches.

Refactor the payload:

const invitationPayload = {
  userId: selectedInfluencer.influencerID,
  managerId: AuthApp.currentUser?.uid,
- collaborationId: selectedInfluencer.collaborationID,
+ collaborationId: props.pageID,
  status: "pending",
  message: message,
};
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 8faf149 and 18dbf64.

📒 Files selected for processing (20)
  • app/(auth)/create-new-account.tsx (5 hunks)
  • app/(auth)/forget-password.tsx (2 hunks)
  • app/(auth)/login.tsx (3 hunks)
  • app/(main)/(screens)/collaboration-details/[pageID].tsx (3 hunks)
  • app/(main)/(screens)/collaboration-details/applications.tsx (1 hunks)
  • app/(main)/(screens)/collaboration-details/invitations.tsx (1 hunks)
  • app/_layout.tsx (2 hunks)
  • app/modal.tsx (4 hunks)
  • app/onboarding-brand-preference.tsx (1 hunks)
  • app/onboarding-get-started.tsx (1 hunks)
  • app/onboarding-your-brand.tsx (2 hunks)
  • components/BottomSheetActions.tsx (4 hunks)
  • components/InfluencerCard.tsx (4 hunks)
  • components/collaboration/CollaborationCard.tsx (2 hunks)
  • components/collaboration/CollaborationDetails.tsx (4 hunks)
  • components/explore-influencers/index.tsx (1 hunks)
  • components/proposals/Applications.tsx (8 hunks)
  • components/proposals/Invitations.tsx (7 hunks)
  • contexts/brand-context.provider.tsx (2 hunks)
  • styles/CollaborationDetails.styles.tsx (3 hunks)
🔇 Additional comments (27)
contexts/brand-context.provider.tsx (2)

3-3: LGTM! Import consolidation improves readability.


Line range hint 20-20: Verify context integration in consuming components.

Let's ensure that all components using useBrandContext are properly wrapped with BrandContextProvider.

✅ Verification successful

Let me verify the provider's implementation and its relationship with consumers.


Context integration is properly implemented

The verification shows that:

  • BrandContextProvider is correctly placed at the root level in both app/_layout.tsx and app/(main)/_layout.tsx
  • All components using useBrandContext hook import it from the correct path
  • The context hierarchy ensures that all consuming components are wrapped within the provider
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for useBrandContext usage
echo "Checking useBrandContext usage:"
rg "useBrandContext" -l

# Search for BrandContextProvider implementation
echo -e "\nChecking BrandContextProvider implementation:"
rg "BrandContextProvider" -l

Length of output: 544


Script:

#!/bin/bash
# Check BrandContextProvider implementation in app layouts
echo "Checking BrandContextProvider in layouts:"
rg "BrandContextProvider" app/_layout.tsx app/\(main\)/_layout.tsx -C 2

# Check if useBrandContext is properly exported and imported
echo -e "\nChecking context exports and imports:"
rg "export.*useBrandContext|import.*useBrandContext" contexts/brand-context.provider.tsx components/ui/brand-switcher/index.tsx components/drawer-layout/DrawerMenuContent.tsx components/menu/index.tsx components/proposals/Applications.tsx components/proposals/Invitations.tsx app/modal.tsx

Length of output: 2778

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

7-7: ⚠️ Potential issue

Unused imports detected.

The Chip and View components are imported but not utilized in the code. Based on the PR objectives and AI summary, these were likely intended for implementing the tabbed interface navigation.

Let's verify if these components are used elsewhere or if implementation is missing:

Also applies to: 9-9

✅ Verification successful

Imports are part of a cohesive tabbed interface implementation

The Chip and View imports in [pageID].tsx are actually part of a larger tabbed interface implementation across related files. Both components are actively used in the sibling files:

  • applications.tsx uses both View for layouts and Chip for UI elements
  • invitations.tsx uses View extensively for layout structuring

These imports are likely intended for the parent component to maintain consistency with its child components in the tabbed interface.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if these components are used in related files
rg -l "Chip|View" --type tsx

Length of output: 58


Script:

#!/bin/bash
# Check if these components are used in the file, showing context
rg "Chip|View" "app/(main)/(screens)/collaboration-details/[pageID].tsx" -C 2

# Search for usage in all files, without file extension filter
rg -l "Chip|View" 

Length of output: 2587


Script:

#!/bin/bash
# Let's check the actual content of the file to see if these imports are used
rg "Chip|View" "app/(main)/(screens)/collaboration-details/[pageID].tsx" -A 5 -B 5

# Also check the content of related files in the same directory
rg "Chip|View" "app/(main)/(screens)/collaboration-details/applications.tsx" -A 5 -B 5
rg "Chip|View" "app/(main)/(screens)/collaboration-details/invitations.tsx" -A 5 -B 5

Length of output: 3982

app/(auth)/forget-password.tsx (1)

11-11: LGTM: Colors import aligns with theme system integration

The import follows the project's convention and supports the theme-based color system integration.

app/(auth)/login.tsx (2)

11-11: LGTM: Colors import follows project conventions

The import is correctly placed and follows the project's path aliasing convention.


47-47: Verify contrast ratio for email input

While the dynamic theming is good, ensure that using Colors(theme).text as the primary color provides sufficient contrast against the input background for accessibility.

Consider implementing a dedicated theme token for input primary colors instead of using the text color directly. This would provide better semantic meaning and flexibility for future theme adjustments.

styles/CollaborationDetails.styles.tsx (2)

24-27: LGTM!

The contentContainer style follows React Native best practices for flexible layouts.


54-55: Verify gap property compatibility.

The gap property might not be supported in older versions of React Native. Consider using margin as a fallback.

app/(auth)/create-new-account.tsx (1)

9-9: LGTM: Colors import addition

The addition of the Colors import aligns with the theme-based color system implementation.

components/collaboration/CollaborationCard.tsx (1)

21-21: LGTM: Well-typed interface extension

The new optional property is properly typed and follows consistent naming conventions.

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

Line range hint 62-67: Avoid using array indices as React keys.

Using array indices as keys (key={index} and in keyExtractor) is an anti-pattern in React. This can cause issues with list item updates, reordering, and animations.

Use a unique identifier from the influencer data:

- keyExtractor={(item, index) => index.toString()}
+ keyExtractor={(item) => item.id.toString()}

  renderItem={({ item, index }) => (
    <InfluencerCard
-     key={index}
      type="explore"
      ToggleModal={ToggleModal}
      influencer={item}
    />
  )}

Let's verify if the influencer data has a unique identifier:

Also applies to: 69-69

✅ Verification successful

Let me check if there's a unique identifier field in the influencer type definition.


Let me try a different approach to find the type definition.


Let me try one final search to find type definitions.


Based on the search results, I can see that the influencer data structure in constants/Influencers.ts doesn't have a unique identifier field like id. However, each influencer has a unique handle property that can serve as a stable identifier. Here's my final response:

Use influencer handle as the key instead of array index

Since each influencer has a unique handle, use it as the key instead of the array index:

- key={index}
+ key={item.handle}

Also update the keyExtractor:

- keyExtractor={(item, index) => index.toString()}
+ keyExtractor={(item) => item.handle}

The handle is a more stable identifier than array indices and will help React properly track and update list items.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if influencers have unique IDs
ast-grep --pattern 'export const influencers = $_'

Length of output: 3453


Script:

#!/bin/bash
# Check TypeScript interface/type definition for influencer
ast-grep --pattern 'interface $_Influencer {
  $$$
}'

# Also check type definition
ast-grep --pattern 'type $_Influencer = {
  $$$
}'

Length of output: 108


Script:

#!/bin/bash
# Search for any type definitions in the codebase
rg -t typescript "type|interface" --context 2

# Also check if handle is used as a unique identifier anywhere
ast-grep --pattern 'handle: $_'

Length of output: 116


Script:

#!/bin/bash
# Search for files with .ts or .tsx extension containing type definitions
rg "type|interface" --type-add 'ts:*.{ts,tsx}' -t ts -C 2

# Also check if there are any files specifically for types/interfaces
fd -e ts -e tsx "types?"

Length of output: 25731

app/_layout.tsx (2)

25-25: LGTM! Import statement follows existing patterns.

The import statement for BrandContextProvider follows the established pattern of importing context providers from the @/contexts directory.


68-72: Verify context dependencies and component access patterns.

The integration of BrandContextProvider looks correct, but let's ensure proper context access patterns:

  1. Components using AutocompleteDropdownContext can now access BrandContext
  2. No circular dependencies exist between these contexts

Let's verify the context usage patterns:

✅ Verification successful

Context hierarchy is correctly implemented

Based on the verification results:

  1. The BrandContext is independent and doesn't import the AutocompleteDropdown context
  2. No components were found that simultaneously use both contexts
  3. BrandContext is widely used across the application for brand-related functionality
  4. No circular dependencies were detected between the contexts

The current nesting order in _layout.tsx is appropriate since:

  • BrandContext is a higher-level context that might be needed by any component
  • AutocompleteDropdownContext is more specific and its placement as a child makes sense
  • No evidence of interdependencies that would cause issues with the current hierarchy
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for components using both BrandContext and AutocompleteDropdown context
# to ensure the ordering is appropriate

# Search for files using both contexts
echo "Files using both contexts:"
rg -l "useBrandContext|BrandContext" | xargs rg -l "AutocompleteDropdown"

# Check for potential circular dependencies
echo -e "\nPotential circular dependencies in context definitions:"
rg -A 5 "import.*BrandContext" ./src/contexts/

Length of output: 384


Script:

#!/bin/bash
# Let's try a different approach to verify context dependencies

# First, let's locate the context files
echo "Searching for context files:"
fd -e ts -e tsx "Brand.*Context|Autocomplete.*Context"

# Then check their content and usage
echo -e "\nChecking BrandContext implementation:"
rg -A 5 "createContext.*Brand" 

echo -e "\nChecking AutocompleteDropdown context implementation:"
rg -A 5 "createContext.*Autocomplete"

# Look for any imports between these contexts
echo -e "\nChecking for potential circular imports:"
rg "import.*Brand.*Context.*from|import.*Autocomplete.*Context.*from"

# Check components using these contexts
echo -e "\nComponents using these contexts:"
rg -A 2 "use(Brand|AutocompleteDropdown)Context"

Length of output: 4938

app/onboarding-get-started.tsx (1)

Line range hint 46-60: Verify the impact of survey data structure changes.

The survey data structure might be used in other parts of the application. Let's verify its usage to ensure consistency.

✅ Verification successful

Let me gather more information about the survey structure usage by searching with a different approach.


Let me try one more search with different flags and patterns.


Survey data structure changes appear to be isolated to onboarding

Based on the codebase analysis, the survey structure is only used during the brand onboarding process in onboarding-get-started.tsx. While there are other brand document operations (reads and updates) throughout the codebase, none of them interact with the survey data structure. The changes to the survey object structure are self-contained and don't impact other parts of the application.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for references to the survey structure in the codebase
echo "Searching for survey field references..."
rg -l "survey\." --type typescript --type typescript-react

echo "\nSearching for Firestore brand document queries..."
ast-grep --pattern 'doc($_, "brands", $_)'

Length of output: 665


Script:

#!/bin/bash
# Search for survey field references with context
echo "Searching for survey field references with context..."
rg "survey[:\.]" -A 3 --type ts --type tsx

# Search for survey object structure definitions
echo "\nSearching for survey object structure definitions..."
ast-grep --pattern 'survey: {
  $$$
}'

# Search for survey type definitions
echo "\nSearching for survey type definitions..."
ast-grep --pattern 'type $_ = {
  survey: $_
}'

Length of output: 494


Script:

#!/bin/bash
# Search for survey-related patterns with ripgrep
echo "Searching for survey patterns..."
rg -U "survey.*[{:]" --type-add 'ts:*.{ts,tsx}' --type ts -C 3

# Search for brand document reads
echo "\nSearching for brand document reads..."
ast-grep --pattern 'getDoc($_)'

# Search for brand document updates
echo "\nSearching for brand document updates..."
ast-grep --pattern 'updateDoc($_, {
  $$$
})'

Length of output: 2132

app/onboarding-your-brand.tsx (1)

141-141: LGTM: Theme changes align with the standardization effort

The TextInput theme updates properly leverage the dynamic theme system, maintaining consistency across the application.

Also applies to: 214-214

components/collaboration/CollaborationDetails.tsx (2)

53-53: LGTM: Clean state management implementation.

The activeTab state is properly initialized with a default value that matches one of the available tabs.


19-20: Verify the existence of imported components.

The new imports reference components that handle applications and invitations. Let's verify these files exist in the codebase.

✅ Verification successful

Both imported components exist in the codebase at the expected locations

The imports are correctly referencing existing files:

  • app/(main)/(screens)/collaboration-details/applications.tsx
  • app/(main)/(screens)/collaboration-details/invitations.tsx
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify the existence of the imported component files
fd "applications.tsx|invitations.tsx" "app/(main)/(screens)/collaboration-details/"

Length of output: 204

components/InfluencerCard.tsx (2)

12-12: LGTM: Import addition aligns with new feature.

The addition of Chip from react-native-paper is appropriate for the new invitation status feature.


57-57: LGTM: Improved type safety with proper interface.

The change from any to a properly typed interface enhances code reliability and maintainability.

components/proposals/Invitations.tsx (2)

Line range hint 213-219: Verify consistency of 'cardType' between components

In JobCard, the cardType prop is set to "proposal", whereas in BottomSheetActions, it's set to "influencerCard". This inconsistency might lead to unexpected behavior. Confirm whether both components should use the same cardType.

Please check if the cardType values should be consistent between JobCard and BottomSheetActions.


190-190: ⚠️ Potential issue

Use a dynamic value for 'managerId' instead of a hardcoded string

The managerId prop is currently hardcoded as "managerId". If the managerId should reference a dynamic value, consider updating it to use data from your proposals.

Adjust the managerId prop:

-                  managerId="managerId"
+                  managerId={item.managerId}

Ensure that managerId is included in your Proposal interface and that it's being fetched and passed correctly.

Likely invalid or redundant comment.

components/proposals/Applications.tsx (5)

23-24: Imports added appropriately

The imports for useBrandContext and Toast are correctly added.


116-119: Efficient use of 'useMemo' for memoization

The use of useMemo to compute filteredProposals is appropriate and helps prevent unnecessary recalculations when proposals change.


136-143: Verify the placement of the '' component

The <Toast /> component is typically placed once, preferably at the root of your application, to handle toast messages globally. Placing it within a specific component may lead to multiple instances or unexpected behavior. Consider moving <Toast /> to a higher-level component if not already done.


209-212: Ensure 'JobCard' component handles new props correctly

Additional props applications, invitations, acceptedApplications, and status are being passed to the JobCard component. Verify that JobCard is updated to accept and properly utilize these new props without causing runtime errors.


232-232: Update 'cardType' prop aligns with new card types

Changing cardType to "activeCollab" aligns with the updated card types in BottomSheetActions. Ensure that the BottomSheetActions component correctly handles this new cardType.

components/BottomSheetActions.tsx (1)

198-202: Ensure 'View Profile' action is implemented correctly.

The onPress handler for "View Profile" only calls handleClose(); without navigating to the profile screen. Confirm that this action should navigate the user to the appropriate profile view.

#!/bin/bash
# Description: Verify that 'View Profile' navigates to the correct screen.

# Test: Search for navigation logic in 'View Profile' actions. Expect: Code that routes to the profile screen.

rg -A 5 "onPress.*View Profile"

Comment on lines +47 to +54
...(brandDoc.data() as Brand),
id: brandDoc.id,
});
}

setBrands(brandsWithManagerId);

setSelectedBrand(brandsWithManagerId[0]);

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 safety checks for brand selection logic.

The current implementation might cause issues:

  1. Setting selectedBrand unconditionally could override user selections
  2. Accessing brandsWithManagerId[0] without checking array length could lead to undefined assignments

Consider adding safety checks:

- setBrands(brandsWithManagerId);
- setSelectedBrand(brandsWithManagerId[0]);
+ setBrands(brandsWithManagerId);
+ if (!selectedBrand && brandsWithManagerId.length > 0) {
+   setSelectedBrand(brandsWithManagerId[0]);
+ }
📝 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
...(brandDoc.data() as Brand),
id: brandDoc.id,
});
}
setBrands(brandsWithManagerId);
setSelectedBrand(brandsWithManagerId[0]);
...(brandDoc.data() as Brand),
id: brandDoc.id,
});
}
setBrands(brandsWithManagerId);
if (!selectedBrand && brandsWithManagerId.length > 0) {
setSelectedBrand(brandsWithManagerId[0]);
}

Comment on lines +33 to +42
const brandsCollection = collection(FirestoreDB, "brands");
const brandsWithManagerId: Brand[] = [];

const unsubscribe = onSnapshot(brandsCollection, (brandsSnapshot) => {
const brandsWithManagerId: Brand[] = [];

brandsSnapshot.docs.forEach((brandDoc) => {
const membersCollection = collection(brandDoc.ref, 'members');
const membersQuery = query(membersCollection, where('managerId', '==', manager?.id));
const membersCollection = collection(brandDoc.ref, "members");
const membersQuery = query(
membersCollection,
where("managerId", "==", manager?.id)
);

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

Consider restructuring nested subscriptions to prevent memory leaks.

The current implementation has several potential issues:

  1. The shared brandsWithManagerId array could lead to race conditions with multiple async callbacks
  2. Nested onSnapshot subscriptions could cause memory leaks
  3. Inner subscriptions aren't properly cleaned up when the outer subscription reruns

Consider refactoring to use a single subscription with a compound query:

- const brandsCollection = collection(FirestoreDB, "brands");
- const brandsWithManagerId: Brand[] = [];
- 
- const unsubscribe = onSnapshot(brandsCollection, (brandsSnapshot) => {
-   brandsSnapshot.docs.forEach((brandDoc) => {
-     const membersCollection = collection(brandDoc.ref, "members");
-     const membersQuery = query(
-       membersCollection,
-       where("managerId", "==", manager?.id)
-     );
+ const brandsQuery = query(
+   collection(FirestoreDB, "brands"),
+   where(`members.${manager?.id}`, "!=", null)
+ );
+ 
+ const unsubscribe = onSnapshot(brandsQuery, (snapshot) => {
+   const brandsWithManagerId = snapshot.docs.map(doc => ({
+     ...(doc.data() as Brand),
+     id: doc.id
+   }));
+   setBrands(brandsWithManagerId);
+   setSelectedBrand(brandsWithManagerId[0]);
+ });

Committable suggestion was skipped due to low confidence.

); // 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.

⚠️ Potential issue

Incomplete tab implementation.

While the activeTab state is added, several critical pieces are missing:

  1. Tab switching mechanism
  2. Other tabs mentioned in the summary (Applications, Invitations)
  3. Validation for tab states

Consider implementing the complete tabbed interface:

 const [activeTab, setActiveTab] = useState("Overview");
+const tabs = ["Overview", "Applications", "Invitations"] as const;
+type TabType = typeof tabs[number];
+
+const handleTabChange = (tab: TabType) => {
+  setActiveTab(tab);
+};

Committable suggestion was skipped due to low confidence.

Comment on lines +64 to +71
<AppLayout>
{activeTab === "Overview" && (
<CollaborationPage
collaborationDetail={collaboration}
pageID={pageID}
/>
)}
</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 component structure and error handling.

The current implementation only handles the "Overview" tab case. Consider implementing a more robust tabbed interface with proper error boundaries.

Consider this structure:

 <AppLayout>
+  <View style={styles.tabContainer}>
+    {tabs.map((tab) => (
+      <Chip
+        key={tab}
+        selected={activeTab === tab}
+        onPress={() => handleTabChange(tab)}
+        style={styles.tab}
+      >
+        {tab}
+      </Chip>
+    ))}
+  </View>
   {activeTab === "Overview" && (
     <CollaborationPage
       collaborationDetail={collaboration}
       pageID={pageID}
     />
   )}
+  {activeTab === "Applications" && (
+    <CollaborationApplicationPage collaborationId={pageID} />
+  )}
+  {activeTab === "Invitations" && (
+    <CollaborationInvitationPage collaborationId={pageID} />
+  )}
 </AppLayout>

Committable suggestion was skipped due to low confidence.

Comment thread app/(auth)/login.tsx
mode="outlined"
style={styles.input}
theme={{ colors: { primary: "#000" } }}
theme={{ colors: { primary: Colors(theme).text } }}

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 extracting the theme configuration

The theme configuration is duplicated between email and password inputs. This could be extracted to improve maintainability.

Consider creating a shared input theme configuration:

+ const inputTheme = { colors: { primary: Colors(theme).text } };

  // Then use it in both inputs:
  <TextInput
    // ...other props
-   theme={{ colors: { primary: Colors(theme).text } }}
+   theme={inputTheme}
  />

Committable suggestion was skipped due to low confidence.

Comment on lines +128 to +133
await addDoc(invitationRef, invitationPayload).then(() => {
setIsMessageModalVisible(false);
Toaster.success("Invitation sent successfully");
fetchInfluencers();
fetchInvitations();
});

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 mixing await and .then(); use await consistently for clarity and proper error handling.

Combining await with .then() can make the code less readable and may lead to unhandled exceptions. Since you're already within an async function and using try...catch, use await consistently for asynchronous operations.

Refactor the code as follows:

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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await addDoc(invitationRef, invitationPayload).then(() => {
setIsMessageModalVisible(false);
Toaster.success("Invitation sent successfully");
fetchInfluencers();
fetchInvitations();
});
await addDoc(invitationRef, invitationPayload);
setIsMessageModalVisible(false);
Toaster.success("Invitation sent successfully");
fetchInfluencers();
fetchInvitations();

Comment on lines +223 to +231
<Button
title="Send Invitation"
onPress={handleInviteCollaboration}
/>
<Button
title="Cancel"
color="red"
onPress={() => setIsMessageModalVisible(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

Use Button from react-native-paper for consistent theming and styling.

Currently, you're using Button from react-native, which may not align with the theming provided by react-native-paper. Switching to Button from react-native-paper ensures consistent UI components throughout the app.

Import Button from react-native-paper:

import {
  Text,
  Chip,
  Appbar,
  Card,
  Paragraph,
  IconButton,
  ActivityIndicator,
+ Button,
} from "react-native-paper";

Update your buttons:

- <Button
-   title="Send Invitation"
-   onPress={handleInviteCollaboration}
- />
+ <Button mode="contained" onPress={handleInviteCollaboration}>
+   Send Invitation
+ </Button>

- <Button
-   title="Cancel"
-   color="red"
-   onPress={() => setIsMessageModalVisible(false)}
- />
+ <Button mode="text" onPress={() => setIsMessageModalVisible(false)}>
+   Cancel
+ </Button>
📝 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
<Button
title="Send Invitation"
onPress={handleInviteCollaboration}
/>
<Button
title="Cancel"
color="red"
onPress={() => setIsMessageModalVisible(false)}
/>
<Button mode="contained" onPress={handleInviteCollaboration}>
Send Invitation
</Button>
<Button mode="text" onPress={() => setIsMessageModalVisible(false)}>
Cancel
</Button>

Comment on lines +107 to +110
useEffect(() => {
fetchInvitations();
fetchInfluencers();
}, []);

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 the loading state reflects all data fetching operations.

Currently, setLoading(false) is called after fetchInvitations(), but fetchInfluencers() may still be in progress. This can lead to rendering incomplete data. Modify the code to wait for both fetches to complete before updating the loading state.

Refactor the useEffect hook:

useEffect(() => {
-   fetchInvitations();
-   fetchInfluencers();
+   const fetchData = async () => {
+     await Promise.all([fetchInvitations(), fetchInfluencers()]);
+     setLoading(false);
+   };
+   fetchData();
}, []);

Then, remove setLoading(false); from fetchInvitations():

- } finally {
-   setLoading(false);
- }

Committable suggestion was skipped due to low confidence.

Comment on lines +90 to +105
const filterInfluencers = () => {
if (alreadyInvited.length === 0) return influencers;
return influencers.map((influencer: any) => {
if (alreadyInvited.includes(influencer.id)) {
return {
...influencer,
alreadyInvited: true,
};
} else {
return {
...influencer,
alreadyInvited: 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

Optimize filterInfluencers by memoizing the result with useMemo.

The filterInfluencers function executes on every render, which can impact performance, especially with large datasets. Use useMemo to memoize the filtered influencers list, recalculating it only when influencers or alreadyInvited change.

Implement useMemo:

+ import { useMemo } from "react";

+ const filteredInfluencers = useMemo(() => {
    if (alreadyInvited.length === 0) return influencers;
    return influencers.map((influencer: Influencer) => ({
      ...influencer,
      alreadyInvited: alreadyInvited.includes(influencer.id),
    }));
+ }, [influencers, alreadyInvited]);

Update the FlatList data prop:

<FlatList
-   data={filterInfluencers()}
+   data={filteredInfluencers}

Committable suggestion was skipped due to low confidence.

Comment on lines +223 to +226
<Button
title="Send Invitation"
onPress={handleInviteCollaboration}
/>

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

Validate user input in the invitation message to prevent potential security issues.

The message input is directly stored in Firestore and could potentially contain malicious content. Implement validation or sanitization to prevent injection attacks or storing unintended data.

Consider adding input validation:

onPress={handleInviteCollaboration}
+ disabled={!message.trim()}

And in handleInviteCollaboration, sanitize the message:

const invitationPayload = {
  // ...
- message: message,
+ message: sanitizeInput(message),
};

Implement the sanitizeInput function to remove or escape harmful content.

Committable suggestion was skipped due to low confidence.

@Manan-Sharma-5
Manan-Sharma-5 merged commit dca1f0b into dev Oct 30, 2024
This was referenced Nov 26, 2024
This was referenced Dec 9, 2024
rahul-sinha1908 added a commit that referenced this pull request Oct 28, 2026
shared-constants #11, shared-libs #83 and shared-uis #84 are merged, so the
pointers move off their feature branches and onto dev. The earlier bump was to
branch commits only so this PR could be reviewed whole; left there, the
pointers would break the moment those branches were deleted.

  shared-constants → f9bc03b
  shared-libs      → 4d64fb4
  shared-uis       → e294276

Typecheck re-run against the merged submodules: clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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