From 95f71161c4a2db2283cc87b0646442cccc438506 Mon Sep 17 00:00:00 2001 From: latiah Date: Wed, 3 Jul 2024 16:26:41 +0200 Subject: [PATCH] feat: chat, calls history implementation --- app/Appointments/index.tsx | 4 + app/chat-history/VideoCall.tsx | 72 +++---- app/chat-history/VideoRecord.tsx | 47 +++-- app/chat-history/chatHistory.tsx | 55 +++-- app/chat-history/singleCall.tsx | 54 +++-- app/chat-history/voiceCalls.tsx | 73 +++---- app/doctor-appointments/_layout.tsx | 11 +- app/doctor-appointments/add-card.tsx | 135 ------------- app/doctor-appointments/patient-details.tsx | 188 ++++++++++++------ app/doctor-appointments/payments.tsx | 99 --------- app/doctor-appointments/review-summary.tsx | 74 ++++++- app/doctor-appointments/select-package.tsx | 8 +- components/cards/DoctorVideo.tsx | 2 +- .../cards/doctors/MinimalDoctorCard.tsx | 3 +- components/chats/voiceCard.tsx | 4 +- package-lock.json | 144 +++++++++----- package.json | 5 +- 17 files changed, 455 insertions(+), 523 deletions(-) delete mode 100644 app/doctor-appointments/add-card.tsx delete mode 100644 app/doctor-appointments/payments.tsx diff --git a/app/Appointments/index.tsx b/app/Appointments/index.tsx index d3c7d677..8b432497 100644 --- a/app/Appointments/index.tsx +++ b/app/Appointments/index.tsx @@ -78,12 +78,16 @@ if(error){ }else{ setAppointmentData(data); console.log("fetched data:"); + setTimeout(()=>{ + router.push("/(tabs)/appointment") + },0) }} catch(error){ console.log(error); } } useEffect(()=>{ + fetchAppointment(); },[]) diff --git a/app/chat-history/VideoCall.tsx b/app/chat-history/VideoCall.tsx index bca03a5b..a978c42a 100644 --- a/app/chat-history/VideoCall.tsx +++ b/app/chat-history/VideoCall.tsx @@ -1,64 +1,46 @@ -import React from "react"; +import React, {useState, useEffect} from "react"; import { View, ScrollView } from "react-native"; import DoctorVideo from "@/components/cards/DoctorVideo"; import { useRouter } from "expo-router"; -import { DoctorCard } from "./types"; // Import the types - +import { DoctorCard } from "./types"; +import { supabase } from "../supabase"; const VideoCall = () => { const router = useRouter(); + const [appointment, setAppointment]=useState([]); - const docCards: DoctorCard[] = [ - { - name: "Dr. Randy Wigham", - callDay: "Wednesday", - callTime: "1:00 PM", - images: require("../../assets/doctors/doc2.png"), - }, - { - name: "Dr. Jenny Watson", - callDay: "Wednesday", - callTime: "1:00 PM", - images: require("../../assets/doctors/doc3.png"), - }, - { - name: "Dr. Raul Zirkind", - callDay: "Wednesday", - callTime: "1:00 PM", - images: require("../../assets/doctors/doc1.png"), - }, - { - name: "Dr. Elijah Baranick", - callDay: "Wednesday", - callTime: "1:00 PM", - images: require("../../assets/doctors/doc2.png"), - }, - { - name: "Dr. Stephen Shute", - callDay: "Wednesday", - callTime: "1:00 PM", - images: require("../../assets/doctors/doc5.png"), - }, - ]; - - const handlePress = (doctor: DoctorCard) => { + useEffect(()=>{ + const fetchAppointments=async()=>{ + const {data, error}=await supabase.auth.getUser(); + if(error) throw error; + const userId=data?.user?.id; + const {data:AppointmentData, error:Error}=await supabase.from("appointment").select("*, doctor(name,image)").eq("patient_id", userId).eq("package","Video Call") + if(Error) throw Error + if(AppointmentData){ + setAppointment(AppointmentData); + } + } + fetchAppointments(); + },[]) + + const handlePress = (appointmentId:any) => { router.push({ pathname: "/chat-history/VideoRecord", - params: { doctor: JSON.stringify(doctor) }, + params: {appointmentId}, }); }; return ( - {docCards.map((doctor, index) => ( + {appointment.map((appointments, index) => ( handlePress(doctor)} - doctorName={doctor.name} - doctorImage={doctor.images} - callType="Video Call" - callDay={doctor.callDay} - callTime={doctor.callTime} + onPress={() => handlePress(appointments.id)} + doctorName={appointments.doctor.name} + doctorImage={appointments.doctor.image} + callType={appointments.package} + callDay={appointments.appointment_date} + callTime={appointments.appointment_time.slice(0,5)} isVideoCallScreen={false} /> ))} diff --git a/app/chat-history/VideoRecord.tsx b/app/chat-history/VideoRecord.tsx index a11bc7ce..f6317a02 100644 --- a/app/chat-history/VideoRecord.tsx +++ b/app/chat-history/VideoRecord.tsx @@ -14,20 +14,22 @@ import PlayButton from "@/components/cards/PlayButton"; import { useRouter, useLocalSearchParams } from "expo-router"; import { SvgXml } from "react-native-svg"; import AsyncStorage from "@react-native-async-storage/async-storage"; - -import { DoctorCard } from "./types"; import { moreTransparent } from "@/assets/icons/more"; import { back } from "@/assets/icons/userprofile/icons"; +import { supabase } from "../supabase"; const VideoRecord: React.FC = () => { const router = useRouter(); - const { doctor: doctorString } = useLocalSearchParams(); - const doctor: DoctorCard = doctorString - ? JSON.parse(doctorString as string) - : null; - + + +const {appointmentId}=useLocalSearchParams(); const [modalVisible, setModalVisible] = useState(false); const [videoDuration, setVideoDuration] = useState(0); + const[ name, setName]=useState(""); + const[date, setDate]=useState(""); + const[time, setTime]=useState(""); + const[packageType, setPackage]=useState(""); + const[image, setImage]=useState(""); useEffect(() => { const fetchDuration = async () => { @@ -40,9 +42,7 @@ const VideoRecord: React.FC = () => { fetchDuration(); }, []); - if (!doctor) { - return null; - } + const handlePress = () => { router.push("/chat-history/PlayRecord"); @@ -64,7 +64,7 @@ const VideoRecord: React.FC = () => { setModalVisible(false); }; - const { name, images, callDay, callTime } = doctor; + const formatTime = (timeInSeconds: number) => { const hours = Math.floor(timeInSeconds / 3600); @@ -79,6 +79,21 @@ const VideoRecord: React.FC = () => { return `${minutes}:${String(seconds).padStart(2, "0")} minutes`; } }; + useEffect(()=>{ + const fetchAppointments=async()=>{ + const {data:AppointmentData, error:Error}=await supabase.from("appointment").select("*, doctor(name,image)").eq("id",appointmentId).single(); + if(Error) throw Error + if(AppointmentData){ + setName(AppointmentData.doctor.name); + setPackage(AppointmentData.package); + setImage(AppointmentData.doctor.image); + setTime(AppointmentData.appointment_time); + setDate(AppointmentData.appointment_date); + + } + } + fetchAppointments(); + },[appointmentId]) return ( @@ -89,12 +104,12 @@ const VideoRecord: React.FC = () => { - ([]); const handleDocName = (doc: string) => { setDocname(doc); setIsModalVisible(true); }; - const renderItems = ({ item }: { item: MessagesType }) => { + + useEffect(()=>{ +const fetchAppointments=async()=>{ +const {data, error}=await supabase.auth.getUser(); +if(error) throw error; +const userId=data?.user?.id; +const {data:AppointmentData, error:Error}=await supabase.from("appointment").select("*, doctor(name,image)").eq("patient_id", userId).eq("package","Messaging") +if(Error) throw Error +if(AppointmentData){ +setAppointment(AppointmentData); +} +} +fetchAppointments(); + },[]) + const renderHistory = (appointment: any, index:number) => { return ( - + - + handleDocName(item.name)} + onPress={() => handleDocName(appointment.doctor.name)} > - {item.name} + {appointment.doctor.name} - {item.message} + ..... - {item.date} + {appointment.appointment_date} - {item.time} + {appointment.appointment_time.slice(0,5)} @@ -125,7 +140,7 @@ export default function Chathistory() { - + /> */} - + /> */} {/* 16:03 PM @@ -249,16 +264,12 @@ export default function Chathistory() { - {selected === "Messages" && ( - + + {appointment.map((appointmentItem,index) => renderHistory(appointmentItem,index))} + )} - +{selected==="Calls"&&} {selected === "Videos" && } diff --git a/app/chat-history/singleCall.tsx b/app/chat-history/singleCall.tsx index 10cf37da..e9e68204 100644 --- a/app/chat-history/singleCall.tsx +++ b/app/chat-history/singleCall.tsx @@ -9,9 +9,10 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { play } from "@/assets/icons/playBtn"; import { download } from '@/assets/icons/download'; import { deleteRed } from '@/assets/icons/delete'; -import { router } from 'expo-router'; +import { router, useLocalSearchParams } from 'expo-router'; import { AVPlaybackSource, AVPlaybackStatus, AVPlaybackStatusSuccess, Audio } from "expo-av"; import { Sound } from 'expo-av/build/Audio'; +import { supabase } from '../supabase'; const { width } = Dimensions.get('window'); const WAVEFORM_ELEMENTS_COUNT = 50; @@ -23,18 +24,26 @@ export default function SingleCall() { const [duration, setDuration] = useState(1); const [sound, setSound] = useState(null); const progressAnim = useRef(new Animated.Value(0)).current; + const { appointmentId } = useLocalSearchParams(); const intervalRef = useRef(null); const [waveformHeights, setWaveformHeights] = useState([]); - + const [appointment, setAppointment] = useState([]); + const [name, setName] = useState(""); + const [date, setDate] = useState(""); + const [time, setTime] = useState(""); + const [packageType, setPackage] = useState(""); + const [image, setImage] = useState(""); const generateWaveformHeights = () => { + return Array.from({ length: WAVEFORM_ELEMENTS_COUNT }, () => Math.random() * 50 + 10); }; - const handleLoad= async()=>{ + const handleLoad = async () => { const { sound } = await Audio.Sound.createAsync(require('@/assets/Travis-Mafia.mp3')); + setSound(sound); await sound.loadAsync(require('@/assets/Travis-Mafia.mp3')); const status = await sound.getStatusAsync(); - if (status.isLoaded){ + if (status.isLoaded) { setDuration(status.durationMillis); } } @@ -80,12 +89,12 @@ export default function SingleCall() { const formatTime = (millis: number) => { const minutes = Math.floor(millis / 60 / 1000); - const seconds= Math.round(((millis/60/1000)-minutes)*60); - return seconds<10? `${minutes} minutes and 0${seconds} seconds`:`${minutes} minutes and ${seconds} seconds`;; + const seconds = Math.round(((millis / 60 / 1000) - minutes) * 60); + return seconds < 10 ? `${minutes} minutes and 0${seconds} seconds` : `${minutes} minutes and ${seconds} seconds`;; } - useEffect(()=>{ + useEffect(() => { handleLoad(); - },[]) + }, []) useEffect(() => { intervalRef.current = setInterval(() => { handleProgress(); @@ -111,6 +120,20 @@ export default function SingleCall() { setWaveformHeights(generateWaveformHeights()); }, [sound]); + useEffect(() => { + const fetchAppointments = async () => { + const { data: AppointmentData, error: Error } = await supabase.from("appointment").select("*, doctor(name,image)").eq("id", appointmentId).single() + if (AppointmentData) { + setName(AppointmentData.doctor.name); + setPackage(AppointmentData.package); + setImage(AppointmentData.doctor.image); + setTime(AppointmentData.appointment_time); + setDate(AppointmentData.appointment_date); + } + } + fetchAppointments(); + }, [appointmentId]) + return ( <> @@ -138,13 +161,12 @@ export default function SingleCall() { setVisible(!visible)} /> - @@ -160,13 +182,13 @@ export default function SingleCall() { {waveformHeights.map((height, index) => { const barWidth = width * 0.75 / WAVEFORM_ELEMENTS_COUNT; const barProgress = progressAnim.interpolate({ - inputRange: [0, (index ) / WAVEFORM_ELEMENTS_COUNT * width], + inputRange: [0, (index) / WAVEFORM_ELEMENTS_COUNT * width], outputRange: [0, 1], extrapolate: 'clamp', }); const backgroundColor = barProgress.interpolate({ inputRange: [0, 1], - outputRange: ['#E9F0FF','#246BFD'] + outputRange: ['#E9F0FF', '#246BFD'] }); return ( { +export default function VoiceCalls() { + const[appointment, setAppointment]=useState([]); + + useEffect(()=>{ + const fetchAppointments=async()=>{ + const {data, error}=await supabase.auth.getUser(); + if(error) throw error; + const userId=data?.user?.id; + const {data:AppointmentData, error:Error}=await supabase.from("appointment").select("*, doctor(name,image)").eq("patient_id", userId).eq("package","Voice Call") + if(Error) throw Error + if(AppointmentData){ + setAppointment(AppointmentData); + } + } + fetchAppointments(); + },[]) + const renderItems = (appointment: any, index:number) => { return ( <> router.push('chat-history/singleCall')} + key={index} + name={appointment.doctor.name} + type={appointment.package} + date={appointment.appointment_date} + time={appointment.appointment_time.slice(0,5)} + image={appointment.doctor.image} + onPress={()=>router.push({pathname:'chat-history/singleCall', params:{appointmentId:appointment.id}})} /> ) } - const image = require('@/assets/doctors/doc1.png'); return ( <> - + {appointment.map((appointmentItem,index) => renderItems(appointmentItem,index))} ) } \ No newline at end of file diff --git a/app/doctor-appointments/_layout.tsx b/app/doctor-appointments/_layout.tsx index 2a2fdb40..d220e36d 100644 --- a/app/doctor-appointments/_layout.tsx +++ b/app/doctor-appointments/_layout.tsx @@ -19,16 +19,7 @@ export default function DoctorAppointments() { }, }} > - + { - return value - .split(" ") - .map((value, index) => { - return value - .split("") - .map((char, index) => { - if (index % 4 === 0 && index !== 0) return ` ${char}`; - return char; - }) - .join(""); - }) - .join(" "); - }; - - const formatExpirationDate = (value: string) => { - const date = new Date(value) || new Date(); - return date.toLocaleDateString("en-RW", { - day: "2-digit", - month: "2-digit", - year: "2-digit", - }); - }; - return ( - - - - - - - - - - - Mocard - - - - - .... .... .... .... - - - - - Card Holder name - - ... .... - - - - Expiry Date - - .../.... - - - - - - - - Card Name - - - - Card Number - - - - - Expiry Date - - - - CVV - - - - - - { - router.back(); - router.setParams({ added: "1" }); - }} - > - Add - - - - ); -} diff --git a/app/doctor-appointments/patient-details.tsx b/app/doctor-appointments/patient-details.tsx index 2569a8b9..fcfca7df 100644 --- a/app/doctor-appointments/patient-details.tsx +++ b/app/doctor-appointments/patient-details.tsx @@ -2,8 +2,11 @@ import { TextInput } from "@/components/Input"; import { NavigationHeader } from "@/components/NavigationHeader"; import { Text } from "@/components/ThemedText"; import { Select } from "@/components/select"; -import { router } from "expo-router"; -import { useState , useEffect} from "react"; +import { router, useLocalSearchParams } from "expo-router"; +import { useState, useEffect } from "react"; +import { calendar } from "@/assets/icons/userprofile/icons"; +import { SvgXml } from "react-native-svg"; +import Touchable from "@/components/common/touchable"; import { KeyboardAvoidingView, ScrollView, @@ -11,18 +14,29 @@ import { View, useWindowDimensions, } from "react-native"; -import { supabase } from "../supabase"; +import { + back, + edit, + emailIcon, + selector, +} from "../../assets/icons/userprofile/icons"; +import { SelectList } from "react-native-dropdown-select-list"; +import { supabase } from "../supabase"; +import DateTimePickerModal from "react-native-modal-datetime-picker"; export default function PatientDetailsScreen() { const dimensions = useWindowDimensions(); const [names, setNames] = useState(""); const [gender, setGender] = useState(""); - const [age, setAge] = useState(""); + const [dob, setDob] = useState(null); const [problem, setProblem] = useState(""); - const [error, setFetchError]=useState(null); + const [value, setValue] = useState(""); + const [error, setFetchError] = useState(null); const [user, setUser] = useState(null); - const[patientDetails, setPatientDetails]=useState(null); - + const [patientDetails, setPatientDetails] = useState(null); + const { date, time, packageTitle, packageDuration, packagePrice, packageIntervals, doctorId } = useLocalSearchParams<{ date: any, time: any, packageDuration: any, packageTitle: any, packagePrice: any, doctorId: any, packageIntervals: any }>(); + const [open, setOpen] = useState(false); + useEffect(() => { const fetchPatientDetails = async () => { const { data: userData, error: userError } = await supabase.auth.getUser(); @@ -44,90 +58,138 @@ export default function PatientDetailsScreen() { setPatientDetails(patientData); setNames(patientData.full_name); setGender(patientData.gender); - setAge(patientData.age); - setProblem(patientData.problem) + setProblem(patientData.problem); + setDob(new Date(patientData.date_of_birth)); } } }; fetchPatientDetails(); }, []); + const SubmitPatientData = async () => { + if (!names || !gender || !dob || !problem) { + setFetchError("Please fill out all fields."); + return; + } + const { data, error } = await supabase.from("patient") + .update( + [{ + full_name: names, + gender: value, + problem: problem, + date_of_birth: dob.toISOString(), + }], + ).eq("id", user?.id); + + + + if (error) { + console.log(error); + } + console.log("data sent in the database"); + console.log(data); + router.push({ + pathname:"/doctor-appointments/review-summary", + params: { date, time, packageTitle, packageDuration, packagePrice, packageIntervals, doctorId } + }); + } - const genders = [ - { key: 1, value: "Male" }, - { key: 2, value: "Female" }, - ]; - - const ageArray = Array.from({ length: 100 }, (_, i) => { - return { key: i, value: i + 1 + " years" } - }); - + const data = [ + { key: "1", value: "Male" }, + { key: "2", value: "Female" }, + { key: "3", value: "prefer not to say" }, + ]; return ( - - - - - Full Name - - - - - - Gender - a.value === age + " years")} - maxHeight={150} + + + Gender + + setValue(val)} + data={data} + save="value" + defaultOption={{ key: 0, value: "Gender" }} + dropdownTextStyles={{ + fontFamily: "UrbanistRegular", + }} + inputStyles={{ + fontFamily: "UrbanistRegular", + color: value ? "black" : "grey", + fontSize: 16, + }} + boxStyles={{ + borderColor: "transparent", + backgroundColor: "#FAFAFA", + }} + arrowicon={ + + } + /> + + + + - - - - - Write Your Problem - - - + setOpen(true)}> + + + + Write Your Problem + + + + {error} { - router.push("/doctor-appointments/review-summary") - }} + onPress={SubmitPatientData} > Next + { + setDob(selectedDate); + setOpen(false); + }} + onCancel={() => setOpen(false)} + /> ); } diff --git a/app/doctor-appointments/payments.tsx b/app/doctor-appointments/payments.tsx deleted file mode 100644 index ed38f036..00000000 --- a/app/doctor-appointments/payments.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { appleIcon } from "@/assets/icons/apple"; -import { googleIcon } from "@/assets/icons/google"; -import { mastercardColorIcon } from "@/assets/icons/mastercard"; -import { paypalIcon } from "@/assets/icons/paypal"; -import { scanIcon } from "@/assets/icons/scan"; -import { NavigationHeader } from "@/components/NavigationHeader"; -import { Text } from "@/components/ThemedText"; -import { Radio } from "@/components/radio"; -import { router, useLocalSearchParams } from "expo-router"; -import { useState } from "react"; -import { ScrollView, TouchableOpacity, View } from "react-native"; -import { SvgXml } from "react-native-svg"; - -export default function PaymentsScreen() { - const [selected, setSelected] = useState(0); - const paymentMethods = [ - { - name: "PayPal", - icon: paypalIcon, - }, - { - name: "Google Pay", - icon: googleIcon, - }, - { - name: "Apple Pay", - icon: appleIcon, - }, - { - name: "4679", - icon: mastercardColorIcon, - custom: true, - }, - ]; - - const { added } = useLocalSearchParams<{ added: string }>(); - - return ( - - - - - - - - - Select the payment method you want to use. - - - {paymentMethods.map((method, index) => ( - setSelected(index)} - > - - {method.custom && ( - - ... ... ... ...{" "} - - )} - - {method.name} - - - - ))} - - - { - router.push("/doctor-appointments/add-card"); - }} - > - - Add New Card - - - - - - { - router.push("/doctor-appointments/review-summary"); - }} - > - Next - - - - ); -} diff --git a/app/doctor-appointments/review-summary.tsx b/app/doctor-appointments/review-summary.tsx index 806f3755..74970162 100644 --- a/app/doctor-appointments/review-summary.tsx +++ b/app/doctor-appointments/review-summary.tsx @@ -10,6 +10,12 @@ import { Image, Modal, ScrollView, TouchableOpacity, View, ActivityIndicator } f import { SvgXml } from "react-native-svg"; import { useSelector } from "react-redux"; import { supabase } from "../supabase"; +import { PayWithFlutterwave } from 'flutterwave-react-native'; +interface RedirectParams { + status: 'successful' | 'cancelled'; + transaction_id?: string; + tx_ref: string; + } export default function ReviewSummaryScreen() { @@ -18,12 +24,33 @@ export default function ReviewSummaryScreen() { const [failedModal, setFailedModal] = useState(failed == "1") const [loading, setLoading] = useState(false); const[appointmentStatus, setStatus]=useState("Booked"); - const numericPrice = parseFloat(packagePrice.replace("$", "")); + const[patientDetails, setPatientDetails]=useState([]); + const[email, setEmail]=useState(""); + const[name, setNames]=useState(""); + const[phonenumber, setPhoneNumber]=useState(""); + const numericPrice = parseFloat(packagePrice.replace("rwf", "")); const total = packageIntervals * numericPrice; - const fullAmount = `$${total}` + const fullAmount = `${total} rwf` const [doctor, setDoctor] = useState(null); + const flutterKey = process.env.EXPO_PUBLIC_FLUTTERWAVE_PUBLIC_KEY ?? ""; const reviews = useSelector((state: RootState) => state.doctors.reviews).slice(0, 2); + +useEffect(()=>{ + const fetchPatientDetails=async()=>{ + const { data: userData, error: userError } = await supabase.auth.getUser(); + if (userError) throw userError; + const userId = userData?.user?.id; + const {data, error}=await supabase.from("patient").select("*").eq("id",userId).single() + if(error) throw error + if(data){ + setNames(data.full_name); + setEmail(data.email); + setPhoneNumber(data.phone); + } + } + fetchPatientDetails(); +},[]) useEffect(() => { const fetchDoctor = async () => { @@ -53,6 +80,7 @@ const bookAppointment = async () => { const { data: userData, error: userError } = await supabase.auth.getUser(); if (userError) throw userError; const userId = userData?.user?.id; + const {data, error } = await supabase .from("appointment") .insert([ @@ -78,6 +106,23 @@ const bookAppointment = async () => { console.error("Error inserting data:", error); } } + const handleOnRedirect = (data: RedirectParams) => { + console.log('Redirect data:', data); + if (data.status === 'successful') { + bookAppointment(); + } else { + alert('Payment failed or cancelled. Please try again.'); + } + }; + + const generateTransactionRef = (length: number) => { + let result = ''; + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + for (let i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * characters.length)); + } + return `flw_tx_ref_${result}`; + }; return ( <> { {fullAmount} - + {/* @@ -197,9 +242,26 @@ const bookAppointment = async () => { Change - + */} + + + - + {/* { @@ -216,7 +278,7 @@ const bookAppointment = async () => { Next )} - + */} ) } \ No newline at end of file diff --git a/app/doctor-appointments/select-package.tsx b/app/doctor-appointments/select-package.tsx index d473fd78..cb8c3772 100644 --- a/app/doctor-appointments/select-package.tsx +++ b/app/doctor-appointments/select-package.tsx @@ -22,7 +22,7 @@ export default function SelectPackageScreen() { key: "1", title: "Messaging", description: "Chat messages with doctor", - price: "$20", + price: "20 rwf", period: "30 mins", icon: chatIcon, }, @@ -30,7 +30,7 @@ export default function SelectPackageScreen() { key: "2", title: "Voice Call", description: "Voice call with doctor", - price: "$40", + price: "40 rwf", period: "30 mins", icon: callIcon, }, @@ -38,7 +38,7 @@ export default function SelectPackageScreen() { key: "3", title: "Video Call", description: "Video call with doctor", - price: "$60", + price: "60 rwf", period: "30 mins", icon: videoIcon, }, @@ -60,7 +60,7 @@ const submitPackage=()=>{ const packageDuration=SelectedTimeDuration?.value; const packageIntervals=SelectedTimeDuration?.intervals; router.push({ - pathname:"/doctor-appointments/review-summary", + pathname:"/doctor-appointments/patient-details", params:{date, time, packageTitle, packageDuration, packagePrice, packageIntervals, doctorId } }) } diff --git a/components/cards/DoctorVideo.tsx b/components/cards/DoctorVideo.tsx index 086bd55e..409257ef 100644 --- a/components/cards/DoctorVideo.tsx +++ b/components/cards/DoctorVideo.tsx @@ -23,7 +23,7 @@ export default function DoctorCard(props: DocCardProps) { - + {props.doctorName} diff --git a/components/cards/doctors/MinimalDoctorCard.tsx b/components/cards/doctors/MinimalDoctorCard.tsx index 9f91548e..5921ce59 100644 --- a/components/cards/doctors/MinimalDoctorCard.tsx +++ b/components/cards/doctors/MinimalDoctorCard.tsx @@ -10,8 +10,7 @@ interface SimpleDoctorCardProps{ } export function MinimalDoctorCard(doctor: SimpleDoctorCardProps) { - console.log("Doctor Image:", doctor.image); - // const imageUri = typeof doctor.images === 'string' ? doctor.images : '../../assets/doctors/doc2.png'; + return ( diff --git a/components/chats/voiceCard.tsx b/components/chats/voiceCard.tsx index ad2fc2af..90708a6f 100644 --- a/components/chats/voiceCard.tsx +++ b/components/chats/voiceCard.tsx @@ -16,7 +16,7 @@ export type CallsType = { time: string; date: string; icon?: any; - image: ImageSourcePropType; + image: any; onPress?: () => void; }; @@ -25,7 +25,7 @@ export default function CallsCard(props: CallsType) { - + diff --git a/package-lock.json b/package-lock.json index 97564ca0..bdefdf32 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,6 +37,8 @@ "expo-updates": "~0.25.17", "expo-video": "^1.1.10", "expo-web-browser": "~13.0.3", + "flutterwave-react-native": "^1.0.4", + "flutterwave-react-v3": "^1.3.2", "formik": "^2.4.6", "jest": "^29.7.0", "libphonenumber-js": "^1.11.4", @@ -62,6 +64,7 @@ "react-native-url-polyfill": "^2.0.0", "react-native-vector-icons": "^10.1.0", "react-native-web": "~0.19.10", + "react-native-webview": "13.8.6", "react-redux": "^9.1.2", "redux": "^5.0.1", "toggle-switch-react-native": "^3.3.0", @@ -10037,7 +10040,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "devOptional": true, "engines": { "node": ">= 10" } @@ -10214,7 +10216,6 @@ "version": "20.0.1", "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", - "dev": true, "dependencies": { "@types/node": "*", "@types/tough-cookie": "*", @@ -10304,8 +10305,7 @@ "node_modules/@types/tough-cookie": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", - "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", - "dev": true + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==" }, "node_modules/@types/use-sync-external-store": { "version": "0.0.3", @@ -10381,8 +10381,7 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "deprecated": "Use your platform's native atob() and btoa() methods instead", - "dev": true + "deprecated": "Use your platform's native atob() and btoa() methods instead" }, "node_modules/abort-controller": { "version": "3.0.0", @@ -10422,7 +10421,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", - "dev": true, "dependencies": { "acorn": "^8.1.0", "acorn-walk": "^8.0.2" @@ -10822,6 +10820,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, "node_modules/babel-core": { "version": "7.0.0-bridge.0", "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz", @@ -12218,14 +12224,12 @@ "node_modules/cssom": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", - "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", - "dev": true + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==" }, "node_modules/cssstyle": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dev": true, "dependencies": { "cssom": "~0.3.6" }, @@ -12236,8 +12240,7 @@ "node_modules/cssstyle/node_modules/cssom": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" }, "node_modules/csstype": { "version": "3.1.3", @@ -12261,7 +12264,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", - "dev": true, "dependencies": { "abab": "^2.0.6", "whatwg-mimetype": "^3.0.0", @@ -12275,7 +12277,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", - "dev": true, "dependencies": { "punycode": "^2.1.1" }, @@ -12287,7 +12288,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true, "engines": { "node": ">=12" } @@ -12296,7 +12296,6 @@ "version": "11.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", - "dev": true, "dependencies": { "tr46": "^3.0.0", "webidl-conversions": "^7.0.0" @@ -12402,8 +12401,7 @@ "node_modules/decimal.js": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", - "dev": true + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" }, "node_modules/decode-uri-component": { "version": "0.2.2", @@ -12711,7 +12709,6 @@ "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", "deprecated": "Use your platform's native DOMException instead", - "dev": true, "dependencies": { "webidl-conversions": "^7.0.0" }, @@ -12723,7 +12720,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true, "engines": { "node": ">=12" } @@ -13881,7 +13877,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "dev": true, "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", @@ -13902,7 +13897,6 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "optional": true, "engines": { "node": ">=0.10.0" @@ -13924,7 +13918,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, "engines": { "node": ">=4.0" } @@ -14915,6 +14908,51 @@ "node": ">=0.4.0" } }, + "node_modules/flutterwave-react-native": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/flutterwave-react-native/-/flutterwave-react-native-1.0.4.tgz", + "integrity": "sha512-UdTbzliCigqSreUiyFfcxaq8+3TK09rEpxM5PxcFrY2zJ1KYcUFsX01JxhEhYX0iLlqKJYiWMAtv2PNDvTGFMQ==", + "dependencies": { + "prop-types": "^15.6.2", + "react-native-webview": ">=6.0.2" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/flutterwave-react-v3": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/flutterwave-react-v3/-/flutterwave-react-v3-1.3.2.tgz", + "integrity": "sha512-kFDNUQddrLsV1V4NkjP7qJ8byPWrAawHnagsYjW9dBGy3GyLzxgMUgZHW4/zYAK2DzMBrAmvT7jIKSTes8lx/g==", + "dependencies": { + "axios": "^0.21.1", + "jest-environment-jsdom": "^29.5.0" + }, + "peerDependencies": { + "react": "^15.0.0 || ^18.0.0", + "react-dom": "^15.0.0 || ^18.0.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/fontfaceobserver": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/fontfaceobserver/-/fontfaceobserver-2.3.0.tgz", @@ -15579,7 +15617,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", - "dev": true, "dependencies": { "whatwg-encoding": "^2.0.0" }, @@ -15647,7 +15684,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "devOptional": true, "dependencies": { "@tootallnate/once": "2", "agent-base": "6", @@ -15694,7 +15730,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -16292,8 +16327,7 @@ "node_modules/is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" }, "node_modules/is-regex": { "version": "1.1.4", @@ -17523,7 +17557,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", - "dev": true, "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", @@ -19140,7 +19173,6 @@ "version": "20.0.3", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", - "dev": true, "dependencies": { "abab": "^2.0.6", "acorn": "^8.8.1", @@ -19185,7 +19217,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -19199,7 +19230,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", - "dev": true, "dependencies": { "punycode": "^2.1.1" }, @@ -19211,7 +19241,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true, "engines": { "node": ">=12" } @@ -19220,7 +19249,6 @@ "version": "11.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", - "dev": true, "dependencies": { "tr46": "^3.0.0", "webidl-conversions": "^7.0.0" @@ -20895,8 +20923,7 @@ "node_modules/nwsapi": { "version": "2.2.9", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.9.tgz", - "integrity": "sha512-2f3F0SEEer8bBu0dsNCFF50N0cTThV1nWFYcEYFZttdW0lDAoybv9cQoK7X7/68Z89S7FoRrVjP1LPX4XRf9vg==", - "dev": true + "integrity": "sha512-2f3F0SEEer8bBu0dsNCFF50N0cTThV1nWFYcEYFZttdW0lDAoybv9cQoK7X7/68Z89S7FoRrVjP1LPX4XRf9vg==" }, "node_modules/ob1": { "version": "0.80.9", @@ -21348,7 +21375,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dev": true, "dependencies": { "entities": "^4.4.0" }, @@ -22003,8 +22029,7 @@ "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" }, "node_modules/pump": { "version": "3.0.0", @@ -22076,8 +22101,7 @@ "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" }, "node_modules/queue": { "version": "6.0.2", @@ -22548,6 +22572,27 @@ "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" }, + "node_modules/react-native-webview": { + "version": "13.8.6", + "resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.8.6.tgz", + "integrity": "sha512-jtZ9OgB2AN6rhDwto6dNL3PtOtl/SI4VN93pZEPbMLvRjqHfxiUrilGllL5fKAXq5Ry5FJyfUi82A4Ii8olZ7A==", + "dependencies": { + "escape-string-regexp": "2.0.0", + "invariant": "2.2.4" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/react-native-webview/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "engines": { + "node": ">=8" + } + }, "node_modules/react-native-windows": { "version": "0.74.9", "resolved": "https://registry.npmjs.org/react-native-windows/-/react-native-windows-0.74.9.tgz", @@ -23958,8 +24003,7 @@ "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, "node_modules/reselect": { "version": "5.1.0", @@ -24136,7 +24180,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "dev": true, "dependencies": { "xmlchars": "^2.2.0" }, @@ -25037,8 +25080,7 @@ "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" }, "node_modules/tailwindcss": { "version": "3.3.2", @@ -25382,7 +25424,6 @@ "version": "4.1.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", - "dev": true, "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", @@ -25397,7 +25438,6 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true, "engines": { "node": ">= 4.0.0" } @@ -25816,7 +25856,6 @@ "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, "dependencies": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" @@ -25963,7 +26002,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", - "dev": true, "dependencies": { "xml-name-validator": "^4.0.0" }, @@ -26020,7 +26058,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", - "dev": true, "dependencies": { "iconv-lite": "0.6.3" }, @@ -26037,7 +26074,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", - "dev": true, "engines": { "node": ">=12" } @@ -26339,7 +26375,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", - "dev": true, "engines": { "node": ">=12" } @@ -26412,8 +26447,7 @@ "node_modules/xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" }, "node_modules/xpath": { "version": "0.0.27", diff --git a/package.json b/package.json index 4d38f3de..338ff4b3 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "date-fns": "^3.6.0", "eas-cli": "^10.0.2", "expo": "~51.0.14", + "expo-apple-authentication": "~6.4.1", "expo-auth-session": "~5.5.2", "expo-av": "^14.0.5", "expo-camera": "~15.0.11", @@ -42,6 +43,8 @@ "expo-updates": "~0.25.17", "expo-video": "^1.1.10", "expo-web-browser": "~13.0.3", + "flutterwave-react-native": "^1.0.4", + "flutterwave-react-v3": "^1.3.2", "formik": "^2.4.6", "jest": "^29.7.0", "libphonenumber-js": "^1.11.4", @@ -72,7 +75,7 @@ "toggle-switch-react-native": "^3.3.0", "uuid": "^10.0.0", "world-countries": "^5.0.0", - "expo-apple-authentication": "~6.4.1" + "react-native-webview": "13.8.6" }, "devDependencies": { "@babel/core": "^7.20.0",