Skip to content
Merged

Dev #65

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
0775b96
pushed new contract screens
Manan-Sharma-5 Jan 2, 2025
d31e768
fixed member container
Manan-Sharma-5 Jan 2, 2025
3094cb1
Redesign application and invitation cards (#51)
dev-rathore Jan 2, 2025
e0ffc7d
Merge branch 'dev' into feature/brand-contract-screen
Manan-Sharma-5 Jan 3, 2025
d065e1b
Merge pull request #52 from idivarts/feature/brand-contract-screen
Manan-Sharma-5 Jan 3, 2025
7f946c4
added keyboard view
Manan-Sharma-5 Jan 5, 2025
6d9fbc0
fixes
Manan-Sharma-5 Jan 5, 2025
523cd99
added underline in file attachment
Manan-Sharma-5 Jan 5, 2025
117bcb9
ui-feedback
Manan-Sharma-5 Jan 6, 2025
bcea307
added work
Manan-Sharma-5 Jan 6, 2025
977fd79
minor changes
Manan-Sharma-5 Jan 7, 2025
6090e90
Merge pull request #54 from idivarts/feature/brand-contract-screen
Manan-Sharma-5 Jan 7, 2025
e8f4328
Repurpose messaging screen (#53)
dev-rathore Jan 7, 2025
da85095
pushed a minor change
Manan-Sharma-5 Jan 7, 2025
87738a8
Merge pull request #55 from idivarts/feature/brand-contract-screen
Manan-Sharma-5 Jan 7, 2025
def5024
fix: accept application (#56)
dev-rathore Jan 7, 2025
ff10920
ui fix
Manan-Sharma-5 Jan 7, 2025
122bd9b
Merge pull request #58 from idivarts/feature/brand-contract-screen
Manan-Sharma-5 Jan 7, 2025
d75c626
Redirection fixes (#59)
dev-rathore Jan 8, 2025
96be749
Merge branch 'master' into dev
dev-rathore Jan 8, 2025
71f9778
Redirection changes (#61)
dev-rathore Jan 9, 2025
12d301f
Merge branch 'master' into dev
dev-rathore Jan 9, 2025
f7177f1
image component and other fixes
Manan-Sharma-5 Jan 9, 2025
3a7ff25
No brands protected screen implementation (#64)
dev-rathore Jan 10, 2025
ba9d885
Merge branch 'master' into dev
dev-rathore Jan 10, 2025
b7c40c0
Merge branch 'dev' into feature/image-component
Manan-Sharma-5 Jan 10, 2025
8725456
Merge pull request #63 from idivarts/feature/image-component
Manan-Sharma-5 Jan 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 17 additions & 14 deletions app/(main)/(drawer)/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,30 @@ import { Drawer } from "expo-router/drawer";
import DrawerMenuContent from "@/components/drawer-layout/DrawerMenuContent";
import BackButton from "@/components/ui/back-button/BackButton";
import { useBreakpoints } from "@/hooks";
import { BrandProtectedScreen } from "@/layouts/protected";

const DrawerLayout = () => {
const { xl } = useBreakpoints();

return (
<Drawer
backBehavior="history"
drawerContent={() => <DrawerMenuContent />}
screenOptions={{
drawerType: xl ? "permanent" : "slide",
headerShown: false,
headerLeft: () => xl ? null : <BackButton />,
}}
>
<Drawer.Screen
name="(tabs)"
options={{
<BrandProtectedScreen>
<Drawer
backBehavior="history"
drawerContent={() => <DrawerMenuContent />}
screenOptions={{
drawerType: xl ? "permanent" : "slide",
headerShown: false,
headerLeft: () => xl ? null : <BackButton />,
}}
/>
</Drawer>
>
<Drawer.Screen
name="(tabs)"
options={{
headerShown: false,
}}
/>
</Drawer>
</BrandProtectedScreen>
);
};

Expand Down
65 changes: 33 additions & 32 deletions app/(main)/(screens)/(onboarding)/onboarding-your-brand.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import React, { useState } from "react";
import {
View,
} from "react-native";
import { View } from "react-native";
import { Button } from "react-native-paper";
import { router, useLocalSearchParams } from "expo-router";
import { useTheme } from "@react-navigation/native";
Expand Down Expand Up @@ -38,34 +36,34 @@ const OnboardingScreen = () => {
const [isSubmitting, setIsSubmitting] = useState(false);
const theme = useTheme();
const styles = fnStyles(theme);
const {
firstBrand,
} = useLocalSearchParams();
const {
uploadImageBytes,
} = useFirebaseStorageContext();
const {
setSelectedBrand,
} = useBrandContext();
const {
manager: user,
} = useAuthContext();
const { firstBrand } = useLocalSearchParams();
const { uploadImageBytes } = useFirebaseStorageContext();
const { setSelectedBrand } = useBrandContext();
const { manager: user } = useAuthContext();

const handleCreateBrand = async () => {
setIsSubmitting(true);
if (!user) {
Toaster.error("User not found");
setIsSubmitting(false);
return;
}

if (!brandData.name) {

Toaster.error('Brand name is required');
setIsSubmitting(false);

return;
}

let imageUrl = "";
if (brandData.image) {
const blob = await fetch(brandData.image).then((res) => res.blob());
imageUrl = await uploadImageBytes(blob, `brands/${brandData.name}-${Date.now()}`);
imageUrl = await uploadImageBytes(
blob,
`brands/${brandData.name}-${Date.now()}`
);
Comment on lines 62 to +66

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling for image upload

The image upload lacks proper error handling and cleanup.

     let imageUrl = "";
     if (brandData.image) {
-      const blob = await fetch(brandData.image).then((res) => res.blob());
-      imageUrl = await uploadImageBytes(
-        blob,
-        `brands/${brandData.name}-${Date.now()}`
-      );
+      try {
+        const response = await fetch(brandData.image);
+        const blob = await response.blob();
+        imageUrl = await uploadImageBytes(
+          blob,
+          `brands/${brandData.name}-${Date.now()}`
+        );
+      } catch (error) {
+        setIsSubmitting(false);
+        Toaster.error("Failed to upload brand image");
+        return;
+      }
     }
📝 Committable suggestion

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

Suggested change
const blob = await fetch(brandData.image).then((res) => res.blob());
imageUrl = await uploadImageBytes(blob, `brands/${brandData.name}-${Date.now()}`);
imageUrl = await uploadImageBytes(
blob,
`brands/${brandData.name}-${Date.now()}`
);
let imageUrl = "";
if (brandData.image) {
try {
const response = await fetch(brandData.image);
const blob = await response.blob();
imageUrl = await uploadImageBytes(
blob,
`brands/${brandData.name}-${Date.now()}`
);
} catch (error) {
setIsSubmitting(false);
Toaster.error("Failed to upload brand image");
return;
}
}

}

if (user) {
Expand All @@ -88,23 +86,26 @@ const OnboardingScreen = () => {

await setDoc(managerRef, {
managerId: user.id,
role,
}).then(() => {
router.replace({
pathname: "/onboarding-get-started",
params: {
brandId: docRef.id,
firstBrand: firstBrand === "true" ? "true" : "false",
},
role: "Manager",
})
.then(() => {
router.replace({
pathname: "/onboarding-get-started",
params: {
brandId: docRef.id,
firstBrand: firstBrand === "true" ? "true" : "false",
},
});
setSelectedBrand(brandData as Brand);
})
.catch((error) => {
Toaster.error("Error creating brand");
})
.finally(() => {
setIsSubmitting(false);
Comment on lines +89 to +105

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Improve error handling and role management

The implementation has several issues:

  1. Hardcoded role value
  2. Potential race condition in state updates
  3. Incomplete error handling
+const BRAND_ROLES = {
+  MANAGER: "Manager",
+} as const;

       await setDoc(managerRef, {
         managerId: user.id,
-        role: "Manager",
+        role: BRAND_ROLES.MANAGER,
       })
         .then(() => {
+          setSelectedBrand(brandData as Brand);
           router.replace({
             pathname: "/onboarding-get-started",
             params: {
               brandId: docRef.id,
               firstBrand: firstBrand === "true" ? "true" : "false",
             },
           });
-          setSelectedBrand(brandData as Brand);
         })
         .catch((error) => {
-          Toaster.error("Error creating brand");
+          Toaster.error(`Error creating brand: ${error.message}`);
         })
         .finally(() => {
           setIsSubmitting(false);
         });
📝 Committable suggestion

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

Suggested change
role: "Manager",
})
.then(() => {
router.replace({
pathname: "/onboarding-get-started",
params: {
brandId: docRef.id,
firstBrand: firstBrand === "true" ? "true" : "false",
},
});
setSelectedBrand(brandData as Brand);
})
.catch((error) => {
Toaster.error("Error creating brand");
})
.finally(() => {
setIsSubmitting(false);
const BRAND_ROLES = {
MANAGER: "Manager",
} as const;
role: BRAND_ROLES.MANAGER,
})
.then(() => {
setSelectedBrand(brandData as Brand);
router.replace({
pathname: "/onboarding-get-started",
params: {
brandId: docRef.id,
firstBrand: firstBrand === "true" ? "true" : "false",
},
});
})
.catch((error) => {
Toaster.error(`Error creating brand: ${error.message}`);
})
.finally(() => {
setIsSubmitting(false);

});
setSelectedBrand(brandData as Brand);
}).catch((error) => {
Toaster.error("Error creating brand");
}).finally(() => {
setIsSubmitting(false);
});
}
}
};

return (
<AppLayout>
Expand All @@ -129,7 +130,7 @@ const OnboardingScreen = () => {
type="create"
/>
</View>
</AppLayout >
</AppLayout>
);
};

Expand Down
19 changes: 17 additions & 2 deletions app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
import { View } from "@/components/theme/Themed"
import { Image } from "react-native";
import { View } from "@/components/theme/Themed";

const Index = () => {
return (
<View></View>
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}}
>
<Image
source={require("@/assets/images/splash.png")}
style={{
width: '100%',
height: '100%',
}}
/>
Comment on lines +13 to +19

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add accessibility and error handling to Image component

The Image component should include accessibility props and error handling for better user experience.

 <Image
   source={require("@/assets/images/splash.png")}
+  accessibilityLabel="Splash screen"
+  accessibilityRole="image"
+  onError={(e) => console.error('Error loading splash image:', e.nativeEvent.error)}
   style={{
     width: '100%',
     height: '100%',
   }}
+  resizeMode="contain"
 />
📝 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
<Image
source={require("@/assets/images/splash.png")}
style={{
width: '100%',
height: '100%',
}}
/>
<Image
source={require("@/assets/images/splash.png")}
accessibilityLabel="Splash screen"
accessibilityRole="image"
onError={(e) => console.error('Error loading splash image:', e.nativeEvent.error)}
style={{
width: '100%',
height: '100%',
}}
resizeMode="contain"
/>

</View>
);
};

Expand Down
54 changes: 32 additions & 22 deletions components/InfluencerCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,15 @@ import { useTheme } from "@react-navigation/native";
import Colors from "@/constants/Colors";
import { convertToKUnits } from "@/utils/conversion";
import { FontAwesomeIcon } from "@fortawesome/react-native-fontawesome";
import { faEllipsis, faPeopleRoof, faChartLine, faFaceSmile, faComment, faCheck, faPlus } from "@fortawesome/free-solid-svg-icons";
import {
faEllipsis,
faPeopleRoof,
faChartLine,
faFaceSmile,
faComment,
faCheck,
faPlus,
} from "@fortawesome/free-solid-svg-icons";
import { imageUrl } from "@/utils/url";
import Tag from "./ui/tag";
import { User } from "@/types/User";
Expand All @@ -23,6 +31,7 @@ import { truncateText } from "@/utils/text";
import { MediaItem } from "@/shared-uis/components/carousel/render-media-item";
import Carousel from "@/shared-uis/components/carousel/carousel";
import AssetPreviewModal from "@/shared-uis/components/carousel/asset-preview-modal";
import ImageComponent from "@/shared-uis/components/image-component";

interface InfluencerCardPropsType {
alreadyInvited?: (influencerId: string) => Promise<boolean>;
Expand All @@ -48,7 +57,7 @@ const InfluencerCard = (props: InfluencerCardPropsType) => {
const onImagePress = (data: MediaItem) => {
setPreviewImageUrl(data.url);
setPreviewImage(true);
}
};

useEffect(() => {
if (props?.alreadyInvited) {
Expand All @@ -60,26 +69,21 @@ const InfluencerCard = (props: InfluencerCardPropsType) => {

return (
<>
<Card
style={styles.card}
mode="contained"
>
<View
style={[
styles.header,
]}
>
<Card style={styles.card} mode="contained">
<View style={[styles.header]}>
<Pressable
onPress={() => {
if (props.openProfile) {
props.openProfile(influencer);
}
}}
>
<Avatar.Image
size={50}
source={imageUrl(influencer.profileImage)}

<ImageComponent
size="small"
url={influencer.profileImage || ""}
initials={influencer.name}
altText="Image"
shape="circle"
/>
</Pressable>
<Pressable
Expand All @@ -91,7 +95,9 @@ const InfluencerCard = (props: InfluencerCardPropsType) => {
}}
>
<Text style={styles.name}>{influencer.name}</Text>
<Text style={styles.handle}>{influencer.socials?.[0] || 'influencer-handle'}</Text>
<Text style={styles.handle}>
{influencer.socials?.[0] || "influencer-handle"}
</Text>
</Pressable>
{props.type === "invitation" &&
(isInvited ? (
Expand Down Expand Up @@ -141,14 +147,16 @@ const InfluencerCard = (props: InfluencerCardPropsType) => {
</View>

<Carousel
data={influencer.profile?.attachments?.map((attachment) => processRawAttachment(attachment)) || []}
data={
influencer.profile?.attachments?.map((attachment) =>
processRawAttachment(attachment)
) || []
}
onImagePress={onImagePress}
theme={theme}
/>

<View
style={styles.content}
>
<View style={styles.content}>
<View style={styles.stats}>
<View style={styles.statsContainer}>
<View style={styles.statItem}>
Expand Down Expand Up @@ -203,8 +211,10 @@ const InfluencerCard = (props: InfluencerCardPropsType) => {
contentWidth={screenWidth}
source={{
html:
truncateText(influencer?.profile?.content?.about as string, 160) ||
"<p>No content available.</p>",
truncateText(
influencer?.profile?.content?.about as string,
160
) || "<p>No content available.</p>",
}}
defaultTextProps={{
style: {
Expand Down
9 changes: 8 additions & 1 deletion components/brand-profile/members-card/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ManagerCard } from "@/components/preferences";
import { View, Text } from "@/components/theme/Themed";
import Colors from "@/constants/Colors";
import { IManagers } from "@/shared-libs/firestore/trendly-pro/models/managers";
import ImageComponent from "@/shared-uis/components/image-component";
import { imageUrl } from "@/utils/url";
import { faEllipsisH } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-native-fontawesome";
Expand Down Expand Up @@ -46,7 +47,13 @@ const MembersCard: FC<MembersCardProps> = ({ manager, cardType, action }) => {
gap: 10,
}}
>
<Avatar.Image size={40} source={imageUrl(manager.profileImage)} />
<ImageComponent
size="small"
shape="circle"
initials={manager.name}
url={manager.profileImage || ""}
altText="Image"
/>
<View>
<Text style={{ fontSize: 16 }}>{manager.name}</Text>
<Text style={{ fontSize: 16 }}>{manager.email}</Text>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-native-fontawesome";
import Colors from "@/constants/Colors";
import { faEllipsisH } from "@fortawesome/free-solid-svg-icons";
import { useTheme } from "@react-navigation/native";
import ImageComponent from "@/shared-uis/components/image-component";

interface CollaborationHeaderProps {
collabName: string;
Expand Down Expand Up @@ -47,8 +48,11 @@ const CollaborationHeader: FC<CollaborationHeaderProps> = ({
gap: 8,
}}
>
<Image
source={{ uri: brandImage }}
<ImageComponent
url={brandImage}
altText="Image"
shape="circle"
size="small"
style={{
width: 40,
height: 40,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import BrandModal from "./modal/BrandModal";
import ManagerModal from "./modal/ManagerModal";
import { PromotionType } from "@/shared-libs/firestore/trendly-pro/constants/promotion-type";
import ViewCollaborationMap from "@/components/view-collaboration/ViewCollaborationMap";
import ImageComponent from "@/shared-uis/components/image-component";

interface CollaborationDetailsContentProps {
collaboration: CollaborationDetail;
Expand Down Expand Up @@ -186,8 +187,11 @@ const OverviewTabContent = (props: CollaborationDetailsContentProps) => {
flexGrow: 1,
}}
>
<Image
source={{ uri: props.collaboration.logo }}
<ImageComponent
url={props.collaboration.logo}
altText="Brand Logo"
shape="square"
size="small"
style={{ width: 40, height: 40, borderRadius: 5 }}
/>
<View style={{ flex: 1 }}>
Expand Down Expand Up @@ -442,12 +446,12 @@ const OverviewTabContent = (props: CollaborationDetailsContentProps) => {
gap: 10,
}}
>
<Image
source={
!managerDetails?.profileImage
? { uri: PLACEHOLDER_IMAGE }
: { uri: managerDetails?.profileImage }
}
<ImageComponent
url={managerDetails?.profileImage}
size="small"
altText="Manager Profile Image"
initials={managerDetails?.name}
initialsSize={16}
style={{
width: 40,
height: 40,
Expand Down
Loading