Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
46 changes: 35 additions & 11 deletions app/(main)/(screens)/(menu)/brand-profile.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
import React, { useRef, useState } from "react";
import { Pressable } from "react-native";
import { ActivityIndicator, Pressable } from "react-native";
import Toast from "react-native-toast-message";

import { Brand } from "@/types/Brand";
import { Text } from "@/components/theme/Themed";
import { useAuthContext, useFirebaseStorageContext } from "@/contexts";
import { useAuthContext, useAWSContext } from "@/contexts";
import { useBrandContext } from "@/contexts/brand-context.provider";
import AppLayout from "@/layouts/app-layout";
import BrandProfile from "@/components/brand-profile";
import ScreenHeader from "@/components/ui/screen-header";
import Toaster from "@/shared-uis/components/toaster/Toaster";
import Colors from "@/constants/Colors";
import { useTheme } from "@react-navigation/native";

const BrandProfileScreen = () => {
const [isSaving, setIsSaving] = useState(false);

const {
updateBrand,
selectedBrand,
setSelectedBrand,
} = useBrandContext();
const {
uploadImageBytes,
} = useFirebaseStorageContext();
const { uploadFileUri } = useAWSContext();

const theme = useTheme();

const {
manager: user,
Expand All @@ -38,12 +42,16 @@ const BrandProfileScreen = () => {
Toaster.error('Brand name is required');
}

setIsSaving(true);
let imageUrl = "";
if (brandData.image !== brandImage.current) {
const blob = await fetch(
brandData.image as string,
).then((res) => res.blob());
imageUrl = await uploadImageBytes(blob, `brands/${brandData.name}-${Date.now()}`);
if (brandData.image && brandData.image !== brandImage.current) {
const uploadedImage = await uploadFileUri({
id: brandData.image,
localUri: brandData.image,
uri: brandData.image,
type: "image",
});
imageUrl = uploadedImage.imageUrl;
Comment thread
dev-rathore marked this conversation as resolved.
Resolved
}

await updateBrand(
Expand All @@ -57,6 +65,8 @@ const BrandProfileScreen = () => {
setSelectedBrand(brandData as Brand);
}).catch((error) => {
Toaster.error('Error saving preferences');
}).finally(() => {
setIsSaving(false);
});
}

Expand All @@ -68,8 +78,22 @@ const BrandProfileScreen = () => {
rightActionButton={
<Pressable
onPress={handleSave}
style={{ padding: 10 }}
style={{
padding: 10,
justifyContent: "center",
alignItems: "center",
gap: 8,
flexDirection: "row",
}}
>
{
isSaving && (
<ActivityIndicator
size="small"
color={Colors(theme).primary}
/>
)
}
Comment thread
dev-rathore marked this conversation as resolved.
Resolved
<Text>Save</Text>
</Pressable>
}
Expand Down
17 changes: 10 additions & 7 deletions app/(main)/(screens)/(onboarding)/onboarding-your-brand.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { addDoc, collection, doc, setDoc } from "firebase/firestore";
import { Brand } from "@/types/Brand";
import { FirestoreDB } from "@/utils/firestore";
import { useBrandContext } from "@/contexts/brand-context.provider";
import { useAuthContext, useFirebaseStorageContext } from "@/contexts";
import { useAuthContext, useAWSContext } from "@/contexts";
import AppLayout from "@/layouts/app-layout";
import BrandProfile from "@/components/brand-profile";
import fnStyles from "@/styles/onboarding/brand.styles";
Expand Down Expand Up @@ -38,10 +38,11 @@ const OnboardingScreen = () => {
const theme = useTheme();
const styles = fnStyles(theme);
const { firstBrand } = useLocalSearchParams();
const { uploadImageBytes } = useFirebaseStorageContext();
const { uploadFileUri } = useAWSContext();
Comment thread
dev-rathore marked this conversation as resolved.
Resolved
const { setSelectedBrand } = useBrandContext();
const { manager: user } = useAuthContext();


const handleCreateBrand = async () => {
setIsSubmitting(true);
if (!user) {
Expand All @@ -59,11 +60,13 @@ const OnboardingScreen = () => {

let imageUrl = "";
if (brandData.image) {
const blob = await fetch(brandData.image).then((res) => res.blob());
imageUrl = await uploadImageBytes(
blob,
`brands/${brandData.name}-${Date.now()}`
);
const uploadedImage = await uploadFileUri({
id: brandData.image,
localUri: brandData.image,
uri: brandData.image,
type: "image",
});
imageUrl = uploadedImage.imageUrl;
Comment thread
dev-rathore marked this conversation as resolved.
Resolved
}

if (user) {
Expand Down
88 changes: 49 additions & 39 deletions components/card/collaboration-details/application-card.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import { Platform, Pressable } from 'react-native';
import Button from '../../ui/button';
import Carousel from '@/shared-uis/components/carousel/carousel';
import { useTheme } from '@react-navigation/native';
Expand All @@ -13,21 +14,21 @@ import { CardFooter } from '@/components/ui/card/secondary/card-footer';
import { processRawAttachment } from '@/utils/attachments';
import { Attachment } from '@/shared-libs/firestore/trendly-pro/constants/attachment';
import { InfluencerApplication } from '@/types/Collaboration';
import { formatDistanceToNow } from "date-fns";
import { convertToKUnits } from '@/utils/conversion';
import { formatTimeToNow } from '@/utils/date';

interface ApplicationCardProps {
acceptApplication: () => void;
bottomSheetAction?: () => void;
data: InfluencerApplication;
headerLeftAction?: () => void;
headerRightAction?: () => void;
profileModalAction?: () => void;
}

export const ApplicationCard: React.FC<ApplicationCardProps> = ({
acceptApplication,
bottomSheetAction,
data,
headerLeftAction,
headerRightAction,
profileModalAction,
}) => {
const theme = useTheme();

Expand All @@ -37,47 +38,56 @@ export const ApplicationCard: React.FC<ApplicationCardProps> = ({
avatar={data.influencer.profileImage || ''}
handle={data.influencer.socials?.[0]}
isVerified={data.influencer.isVerified}
leftAction={headerLeftAction}
leftAction={profileModalAction}
name={data.influencer.name}
rightAction={headerRightAction}
timestamp={formatDistanceToNow(data.application.timeStamp, { addSuffix: true })}
rightAction={bottomSheetAction}
timestamp={formatTimeToNow(data.application.timeStamp)}
/>
<Carousel
containerHeight={data.application?.attachments?.length === 1 ? (Platform.OS === 'web' ? 560 : 288) : undefined}
data={data.application.attachments.map((attachment: Attachment) => processRawAttachment(attachment))}
theme={theme}
/>
<CardActions
metrics={{
followers: data.influencer.backend?.followers || 0,
reach: data.influencer.backend?.reach || 0,
rating: data.influencer.backend?.rating || 0,
}}
action={
<Button
mode="outlined"
size="small"
onPress={acceptApplication}
>
<FontAwesomeIcon
color={Colors(theme).primary}
icon={faCheck}
size={12}
style={{
marginRight: 6,
marginTop: -2,
<Pressable
onPress={profileModalAction}
>
<CardActions
metrics={{
followers: data.influencer.backend?.followers || 0,
reach: data.influencer.backend?.reach || 0,
rating: data.influencer.backend?.rating || 0,
}}
action={
<Button
mode="outlined"
size="small"
onPress={() => {
if (data.application.status === 'pending') {
acceptApplication();
}
Comment thread
dev-rathore marked this conversation as resolved.
Resolved
}}
/>
Accept
</Button>
}
/>
<CardDescription
text={data.application.message}
/>
<CardFooter
quote={convertToKUnits(Number(data.application.quotation)) as string}
timeline={new Date(data.application.timeline).toLocaleDateString('en-US')}
/>
>
<FontAwesomeIcon
color={Colors(theme).primary}
icon={faCheck}
size={12}
style={{
marginRight: 6,
marginTop: -2,
}}
/>
{data.application.status === 'pending' ? 'Accept' : 'Accepted'}
</Button>
}
/>
<CardDescription
text={data.application.message}
/>
<CardFooter
quote={convertToKUnits(Number(data.application.quotation)) as string}
timeline={new Date(data.application.timeline).toLocaleDateString('en-US')}
/>
</Pressable>
</Card>
);
};
2 changes: 2 additions & 0 deletions components/card/collaboration-details/invitation-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome';
import { faCheck, faPlus } from '@fortawesome/free-solid-svg-icons';
import Colors from '@/constants/Colors';
import useInvitation from '@/hooks/use-invitation';
import { Platform } from 'react-native';

interface InvitationCardProps {
checkIfAlreadyInvited: (influencerId: string) => Promise<boolean>;
Expand Down Expand Up @@ -47,6 +48,7 @@ const InvitationCard: React.FC<InvitationCardProps> = ({
rightAction={headerRightAction}
/>
<Carousel
containerHeight={data.profile?.attachments?.length === 1 ? (Platform.OS === 'web' ? 560 : 288) : undefined}
data={data.profile?.attachments?.map((attachment) => processRawAttachment(attachment)) || []}
theme={theme}
/>
Expand Down
90 changes: 54 additions & 36 deletions components/card/profile-modal/application-card.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';

import { Card } from '@/components/ui/card/tertiary';
import { CardHeader } from '@/components/ui/card/tertiary/card-header';
Expand All @@ -15,8 +15,8 @@ import { useTheme } from '@react-navigation/native';

interface ApplicationCardProps {
data: Application;
onAccept: () => void;
onReject: () => void;
onAccept: () => Promise<void>;
onReject: () => Promise<void>;
questions: {
question: string;
answer: string;
Expand All @@ -30,6 +30,8 @@ export const ApplicationCard: React.FC<ApplicationCardProps> = ({
questions,
}) => {
const theme = useTheme();
const [acceptingApplication, setAcceptingApplication] = useState(false);
const [rejectingApplication, setRejectingApplication] = useState(false);

return (
<Card>
Expand Down Expand Up @@ -60,39 +62,55 @@ export const ApplicationCard: React.FC<ApplicationCardProps> = ({
/>
)
}
<CardFooter
footerActions={
<View
style={{
backgroundColor: Colors(theme).transparent,
flexDirection: 'row',
gap: 12,
justifyContent: 'center',
}}
>
<Button
customStyles={{
borderColor: Colors(theme).primary,
}}
textColor={Colors(theme).primary}
mode="outlined"
onPress={onReject}
size="small"
>
Reject Application
</Button>
<Button
customStyles={{
backgroundColor: Colors(theme).primary,
}}
onPress={onAccept}
size="small"
>
Accept Application
</Button>
</View>
}
/>
{
data?.status === 'pending' && (
<CardFooter
footerActions={
<View
style={{
backgroundColor: Colors(theme).transparent,
flexDirection: 'row',
gap: 12,
justifyContent: 'center',
}}
>
<Button
customStyles={{
borderColor: Colors(theme).primary,
}}
textColor={Colors(theme).primary}
loading={rejectingApplication}
mode="outlined"
onPress={() => {
setRejectingApplication(true);
onReject().then(() => {
setRejectingApplication(false);
})
}}
Comment thread
dev-rathore marked this conversation as resolved.
Resolved
size="small"
>
Reject Application
</Button>
<Button
customStyles={{
backgroundColor: Colors(theme).primary,
}}
onPress={() => {
setAcceptingApplication(true);
onAccept().then(() => {
setAcceptingApplication(false);
});
}}
loading={acceptingApplication}
size="small"
>
Accept Application
</Button>
</View>
}
/>
)
}
</Card>
);
}
Loading