Repository navigation
added integration - #11
Conversation
WalkthroughThis pull request introduces several modifications across multiple files, primarily focusing on enhancing the dynamic handling of UI components, particularly Changes
Possibly related PRs
Suggested reviewers
Poem
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? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
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:
- 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:
- Centralizes theme logic and provides type-safe theme tokens
- Handles common component themes (like input fields)
- 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:
- Add password match validation:
if (password !== confirmPassword) {
Toaster.error("Passwords do not match");
return;
}- 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;
}- Add email format validation:
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
Toaster.error("Please enter a valid email address");
return;
}- 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:
- Email format validation
- Password strength requirements
- Password match validation before submission
- 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:
- Split into separate components per card type
- Memoize the component to prevent unnecessary re-renders
- 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:
- Remove the influencer type filtering completely (including from CollaborationFilter props), or
- 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:
- Document the context hierarchy requirements in the codebase
- 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:
- The
brandIdfrom params should be validated before use - Add a loading state during the async operation
- Add proper TypeScript types for the preferences structure
- 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:
- Add input validation to ensure at least one option is selected
- Provide user feedback for errors
- 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:
- Add loading indicator during submission
- Show success feedback before navigation
- 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:
- The auth context shows user authentication is implemented, but there's no validation that the authenticated user owns the brand being modified
- The onboarding flow (
onboarding-your-brand→onboarding-brand-preference→onboarding-get-started) doesn't verify brand ownership between steps - 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:
- No verification that the user has permission to modify the specified brand
- Potential for unauthorized access through URL manipulation
- Missing rate limiting for submissions
Let's verify the security rules:
Consider implementing these security measures:
- Add a server-side check to verify brand ownership
- Implement rate limiting for submissions
- 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:
- File size limits to prevent excessive storage usage
- Stricter file type validation
- 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:
- Loading state management
- Comprehensive error handling
- 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:
- Debounced search
- Memoized filter results
- 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 autocompleteAlso 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:
- Platform selection could use icons and more visual appeal
- 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
📒 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:
BrandContextProvideris correctly placed at the root level in bothapp/_layout.tsxandapp/(main)/_layout.tsx- All components using
useBrandContexthook 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:
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.tsxuses bothViewfor layouts andChipfor UI elementsinvitations.tsxusesViewextensively 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:
- Components using AutocompleteDropdownContext can now access BrandContext
- 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:
- The BrandContext is independent and doesn't import the AutocompleteDropdown context
- No components were found that simultaneously use both contexts
- BrandContext is widely used across the application for brand-related functionality
- 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.tsxapp/(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:
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"| ...(brandDoc.data() as Brand), | ||
| id: brandDoc.id, | ||
| }); | ||
| } | ||
|
|
||
| setBrands(brandsWithManagerId); | ||
|
|
||
| setSelectedBrand(brandsWithManagerId[0]); |
There was a problem hiding this comment.
Add safety checks for brand selection logic.
The current implementation might cause issues:
- Setting
selectedBrandunconditionally could override user selections - 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.
| ...(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]); | |
| } |
| 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) | ||
| ); |
There was a problem hiding this comment.
Consider restructuring nested subscriptions to prevent memory leaks.
The current implementation has several potential issues:
- The shared
brandsWithManagerIdarray could lead to race conditions with multiple async callbacks - Nested
onSnapshotsubscriptions could cause memory leaks - 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"); |
There was a problem hiding this comment.
Incomplete tab implementation.
While the activeTab state is added, several critical pieces are missing:
- Tab switching mechanism
- Other tabs mentioned in the summary (Applications, Invitations)
- 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.
| <AppLayout> | ||
| {activeTab === "Overview" && ( | ||
| <CollaborationPage | ||
| collaborationDetail={collaboration} | ||
| pageID={pageID} | ||
| /> | ||
| )} | ||
| </AppLayout> |
There was a problem hiding this comment.
🛠️ 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.
| mode="outlined" | ||
| style={styles.input} | ||
| theme={{ colors: { primary: "#000" } }} | ||
| theme={{ colors: { primary: Colors(theme).text } }} |
There was a problem hiding this comment.
🛠️ 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.
| await addDoc(invitationRef, invitationPayload).then(() => { | ||
| setIsMessageModalVisible(false); | ||
| Toaster.success("Invitation sent successfully"); | ||
| fetchInfluencers(); | ||
| fetchInvitations(); | ||
| }); |
There was a problem hiding this comment.
🛠️ 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.
| 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(); |
| <Button | ||
| title="Send Invitation" | ||
| onPress={handleInviteCollaboration} | ||
| /> | ||
| <Button | ||
| title="Cancel" | ||
| color="red" | ||
| onPress={() => setIsMessageModalVisible(false)} | ||
| /> |
There was a problem hiding this comment.
🛠️ 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.
| <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> |
| useEffect(() => { | ||
| fetchInvitations(); | ||
| fetchInfluencers(); | ||
| }, []); |
There was a problem hiding this comment.
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.
| 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, | ||
| }; | ||
| } | ||
| }); | ||
| }; |
There was a problem hiding this comment.
🛠️ 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.
| <Button | ||
| title="Send Invitation" | ||
| onPress={handleInviteCollaboration} | ||
| /> |
There was a problem hiding this comment.
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.
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>
Summary by CodeRabbit
Release Notes
New Features
Bug Fixes
Style
Documentation