diff --git a/functions/main.py b/functions/main.py
index 81ba54d..af1ee72 100644
--- a/functions/main.py
+++ b/functions/main.py
@@ -1,9 +1,5 @@
-# Welcome to Cloud Functions for Firebase for Python!
-# To get started, simply uncomment the below code or create your own.
-# Deploy with `firebase deploy`
-
from firebase_admin import initialize_app
-from firebase_functions import https_fn
+from firebase_functions import https_fn, options
from langchain.chains.query_constructor.base import AttributeInfo
from langchain.retrievers.self_query.base import SelfQueryRetriever
from langchain_astradb import AstraDBVectorStore
@@ -16,16 +12,15 @@
# Initialize embeddings and vector store
-def initialize_vector_store(api_key, token):
- embeddings = OpenAIEmbeddings(api_key=api_key)
- vstore = AstraDBVectorStore(
+def initialize_vector_store(token):
+ embeddings = OpenAIEmbeddings(api_key='')
+ return AstraDBVectorStore(
embedding=embeddings,
collection_name='test_collection_2',
api_endpoint='',
token=token,
namespace='test',
)
- return vstore
# Metadata field info
@@ -140,22 +135,16 @@ def initialize_vector_store(api_key, token):
# Create a function to get response from the chain
-def get_health_ai_response(question):
- api_key = ''
+def get_health_ai_response(question, llm):
token = ''
-
- vstore = initialize_vector_store(api_key, token)
-
- llm = ChatOpenAI(api_key=api_key, temperature=0)
+ vector_store = initialize_vector_store(token)
retriever = SelfQueryRetriever.from_llm(
llm=llm,
- vectorstore=vstore,
+ vectorstore=vector_store,
document_content_description=document_content_description,
metadata_field_info=metadata_field_info,
document_contents='',
)
-
- # Prompt Template for Health AI Agent
health_ai_template = """
You are a health AI agent equipped with access to diverse sources of health data,
including research articles, nutritional information, medical archives, and more.
@@ -171,28 +160,48 @@ def get_health_ai_response(question):
YOUR ANSWER:
"""
-
- # Create a ChatPromptTemplate instance from the template
health_ai_prompt = ChatPromptTemplate.from_template(health_ai_template)
-
- # Integration example:
- # In your retrieval and generation pipeline, integrate this prompt template
- # Replace 'retriever' and 'llm' with appropriate retrieval and language models
-
chain = (
{'context': retriever, 'question': RunnablePassthrough()}
| health_ai_prompt
| llm
| StrOutputParser()
)
-
response = chain.invoke(question)
return response
-@https_fn.on_request()
-def on_request_example(req: https_fn.Request) -> https_fn.Response:
- response = get_health_ai_response(
- 'What are the key points of the nutrition article on healthy eating?'
- )
- return https_fn.Response([response])
+def get_response_from_llm(query, llm):
+ api_key = ''
+ llm_model = None
+ if llm == 'gpt-4':
+ llm_model = ChatOpenAI(api_key=api_key, temperature=0)
+ if llm == 'gpt-3.5-turbo-instruct':
+ llm_model = ChatOpenAI(name='gpt-3.5-turbo-instruct', api_key=api_key, temperature=0)
+ if llm_model is not None:
+ response = get_health_ai_response(query, llm_model)
+ return response
+ else:
+ return 'Model Not Found'
+
+
+@https_fn.on_request(cors=options.CorsOptions(cors_origins=['*'], cors_methods=['get', 'post']))
+def get_response_url(req: https_fn.Request) -> https_fn.Response:
+ query = req.get_json().get('query', '')
+ llms = req.get_json().get('llms', ['gpt-4'])
+ responses = []
+ for llm in llms:
+ response = get_response_from_llm(query, llm)
+ responses.append(response)
+ return https_fn.Response(responses)
+
+
+@https_fn.on_call()
+def get_response(req: https_fn.CallableRequest):
+ query = req.data.get('query', '')
+ llms = req.data.get('llms', ['gpt-4'])
+ responses = []
+ for llm in llms:
+ response = get_response_from_llm(query, llm)
+ responses.append(response)
+ return response
diff --git a/package.json b/package.json
index a3177d9..90f047c 100644
--- a/package.json
+++ b/package.json
@@ -28,6 +28,7 @@
"@react-native-firebase/app": "^20.1.0",
"@react-native-firebase/auth": "^20.1.0",
"@react-native-firebase/dynamic-links": "^20.1.0",
+ "@react-native-firebase/functions": "^20.1.0",
"@react-native-google-signin/google-signin": "^12.2.0",
"@react-native-voice/voice": "^3.2.4",
"@react-navigation/drawer": "^7.0.0-alpha.21",
@@ -57,6 +58,7 @@
"react-native-fs": "^2.20.0",
"react-native-gesture-handler": "~2.16.1",
"react-native-keyboard-aware-scroll-view": "^0.9.5",
+ "react-native-markdown-display": "^7.0.2",
"react-native-paper": "^5.12.3",
"react-native-reanimated": "~3.10.1",
"react-native-safe-area-context": "4.10.1",
diff --git a/src/frontend/components/FirebaseProvider/index.tsx b/src/frontend/components/FirebaseProvider/index.tsx
index caf8662..cac290d 100644
--- a/src/frontend/components/FirebaseProvider/index.tsx
+++ b/src/frontend/components/FirebaseProvider/index.tsx
@@ -1,10 +1,11 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import Constants from 'expo-constants';
-import { type FirebaseOptions, getApps, initializeApp } from 'firebase/app';
+import { initializeApp } from 'firebase/app';
import { initializeAuth as getAuth, getReactNativePersistence as store } from 'firebase/auth';
import { getFirestore } from 'firebase/firestore';
-import { type ReactNode, useMemo } from 'react';
-import { AuthProvider, FirebaseAppProvider, FirestoreProvider } from 'reactfire';
+import { getFunctions } from 'firebase/functions';
+import { type ReactNode, useEffect, useMemo } from 'react';
+import { AuthProvider, FirebaseAppProvider, FirestoreProvider, FunctionsProvider } from 'reactfire';
type FirebaseProviderProps = {
children: ReactNode;
@@ -16,6 +17,7 @@ export function FirebaseProvider(props: FirebaseProviderProps) {
const fireApp = useMemo(() => initializeApp(Constants.expoConfig?.extra?.firebase), []);
const fireAuth = useMemo(() => getAuth(fireApp, { persistence: store(AsyncStorage) }), [fireApp]);
const fireStore = useMemo(() => getFirestore(fireApp, 'ailixir-users'), [fireApp]);
+ const fireFunction = useMemo(() => getFunctions(fireApp), [fireApp]);
return (
- {children}
+
+ {children}
+
);
diff --git a/src/frontend/components/Header/index.tsx b/src/frontend/components/Header/index.tsx
index 06102ea..bc7d55f 100644
--- a/src/frontend/components/Header/index.tsx
+++ b/src/frontend/components/Header/index.tsx
@@ -1,26 +1,21 @@
import type { DrawerHeaderProps } from '@react-navigation/drawer';
-import { DrawerActions, type RouteProp, useRoute } from '@react-navigation/native';
+import { DrawerActions } from '@react-navigation/native';
import * as Clipboard from 'expo-clipboard';
-import * as FileSystem from 'expo-file-system';
import * as MediaLibrary from 'expo-media-library';
import React, { useEffect } from 'react';
import { Alert, Platform, Pressable, View } from 'react-native';
import RNFS from 'react-native-fs';
import { IconButton, Surface, Text, useTheme } from 'react-native-paper';
-import { useActiveChatId, useGetChat, useLLMs } from 'src/frontend/hooks';
+import { useActiveChatId, useGetChat } from 'src/frontend/hooks';
import { AilixirLogo } from 'src/frontend/icons';
-import type { MainDrawerParams } from 'src/frontend/routes/MainRoutes';
-import { styles } from 'src/frontend/screens/ChatUI/style';
import { DropdownMenu } from '../DropdownMenu';
import { Style } from './style';
export function Header(props: DrawerHeaderProps) {
const { colors } = useTheme();
const { navigation } = props;
-
- const { activeChatId, setActiveChatId } = useActiveChatId();
- const { activeLLMs, toggleLLM } = useLLMs(activeChatId || 'default');
- const { chat, status, error } = useGetChat(activeChatId);
+ const { activeChatId } = useActiveChatId();
+ const { chat } = useGetChat(activeChatId);
// Determine if the button should be disabled
const isButtonDisabled = chat === undefined;
diff --git a/src/frontend/components/RenderChat/index.tsx b/src/frontend/components/RenderChat/index.tsx
new file mode 100644
index 0000000..02957cb
--- /dev/null
+++ b/src/frontend/components/RenderChat/index.tsx
@@ -0,0 +1,43 @@
+import React from 'react';
+import { Text, View } from 'react-native';
+import Markdown from 'react-native-markdown-display';
+import { ActivityIndicator, Avatar, useTheme } from 'react-native-paper';
+import { useUser } from 'reactfire';
+import { useActiveChatId, useGetChat } from 'src/frontend/hooks';
+import type { conversationMessage } from 'src/frontend/types';
+import { Style } from './style';
+
+export function RenderChat() {
+ const { activeChatId } = useActiveChatId();
+ const { chat, status } = useGetChat(activeChatId);
+ const { colors } = useTheme();
+ const { data: user } = useUser();
+
+ if (status === 'loading') return ;
+
+ return (
+ <>
+ {chat?.conversation.map((item: conversationMessage) => {
+ const { type, message } = item;
+ return (
+
+
+
+
+ {type === 'AI' ? 'AiLixir' : user?.displayName || 'User'}
+
+
+ {message}
+
+ );
+ })}
+ >
+ );
+}
diff --git a/src/frontend/components/RenderChat/style.ts b/src/frontend/components/RenderChat/style.ts
new file mode 100644
index 0000000..0aba4c9
--- /dev/null
+++ b/src/frontend/components/RenderChat/style.ts
@@ -0,0 +1,11 @@
+import { StyleSheet } from "react-native";
+
+export const Style = StyleSheet.create({
+ bubble: {
+ maxWidth: '80%',
+ borderRadius: 4,
+ padding: 8,
+ display: 'flex',
+ flexDirection: 'column'
+ }
+})
\ No newline at end of file
diff --git a/src/frontend/components/index.ts b/src/frontend/components/index.ts
index f43a5c9..082ab0f 100644
--- a/src/frontend/components/index.ts
+++ b/src/frontend/components/index.ts
@@ -8,3 +8,4 @@ export * from './DropdownMenu';
export * from './Header';
export * from './ActiveChatProvider';
export * from './ChatBubble';
+export * from './RenderChat';
diff --git a/src/frontend/hooks/index.ts b/src/frontend/hooks/index.ts
index 8661445..e8363a4 100644
--- a/src/frontend/hooks/index.ts
+++ b/src/frontend/hooks/index.ts
@@ -6,3 +6,4 @@ export * from './useLLMs';
export * from './useActiveChatId';
export * from './useCreateChat';
export * from './useLLMsTypes';
+export * from './useGetResponse';
diff --git a/src/frontend/hooks/useGetResponse.tsx b/src/frontend/hooks/useGetResponse.tsx
new file mode 100644
index 0000000..26f1367
--- /dev/null
+++ b/src/frontend/hooks/useGetResponse.tsx
@@ -0,0 +1,7 @@
+import { httpsCallable } from 'firebase/functions';
+import { useFunctions } from 'reactfire';
+
+export function useGetResponse() {
+ const fireFunction = useFunctions();
+ return httpsCallable(fireFunction, 'get_response');
+}
diff --git a/src/frontend/screens/ChatUI/index.tsx b/src/frontend/screens/ChatUI/index.tsx
index 095e151..8db3f5d 100644
--- a/src/frontend/screens/ChatUI/index.tsx
+++ b/src/frontend/screens/ChatUI/index.tsx
@@ -3,79 +3,54 @@ import Voice, {
type SpeechStartEvent,
type SpeechRecognizedEvent
} from '@react-native-voice/voice';
-import { type RouteProp, useNavigation, useRoute } from '@react-navigation/native';
-import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
-import Constants from 'expo-constants';
-import * as Speech from 'expo-speech';
-import { Timestamp } from 'firebase/firestore';
+import type { FirebaseError } from 'firebase/app';
+import { FieldValue, arrayUnion } from 'firebase/firestore';
import React from 'react';
-import { useCallback, useState } from 'react';
+import { useState } from 'react';
import { useEffect, useRef } from 'react';
-import { ScrollView, Text, TextInput, View } from 'react-native';
+import { ScrollView, TextInput, View } from 'react-native';
import { Keyboard } from 'react-native';
import { Vibration } from 'react-native';
-import { ActivityIndicator, Button, IconButton } from 'react-native-paper';
+import { ActivityIndicator, IconButton } from 'react-native-paper';
import { useTheme } from 'react-native-paper';
-import { ChatBubble } from 'src/frontend/components';
-import { Screens, getLLMResponse } from 'src/frontend/helpers';
+import { RenderChat } from 'src/frontend/components';
import {
- LLM_MODELS,
useActiveChatId,
useCreateChat,
useGetChat,
- useLLMs,
+ useGetResponse,
useUpdateChat
} from 'src/frontend/hooks';
-import type { AppRoutesParams } from 'src/frontend/routes';
-import type { MainDrawerParams } from 'src/frontend/routes/MainRoutes';
-import type { Chat, conversationMessage } from 'src/frontend/types';
import { styles } from './style';
export type ChatUiProps = {
chatId: string;
};
-export function ChatUI(/*props: ChatUiProps*/) {
+export function ChatUI() {
const { colors } = useTheme();
const scrollViewRef = useRef(null);
-
- const { createChat, isCreating } = useCreateChat();
const { activeChatId, setActiveChatId } = useActiveChatId();
- const { chat, status, error } = useGetChat(activeChatId);
+ const { chat } = useGetChat(activeChatId);
const [isRecording, setIsRecording] = useState(false);
-
- const { activeLLMs: LLMs, toggleLLM } = useLLMs(activeChatId);
- const [responses, setResponses] = useState([]);
- //for chatbubble
- const [responseIndex, setResponseIndex] = useState(0);
-
const [text, setText] = useState('');
- const {
- updateChat,
- isUpdating,
- error: updateError,
- isSuccess: updatedChatSuccessfully
- } = useUpdateChat(activeChatId || '');
-
const [recognized, setRecognized] = useState('');
const [started, setStarted] = useState('');
const [results, setResults] = useState([]);
-
- //const {LLMResponse, isGenerating, LLMResponseError} = useGetLLMResponse('');
const [isSendButtonDisabled, setSendButtonDisabled] = useState(false);
- //const isSendButtonDisabled = false;
+ const getResponse = useGetResponse();
+ const { updateChat } = useUpdateChat(activeChatId);
+ const { createChat } = useCreateChat();
+ const [isChatTitleUpdated, setIsChatTitleUpdated] = useState(false);
// ------------- Keyboard and scrolling -------------
-
useEffect(() => {
const keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', () => {
scrollViewRef.current?.scrollToEnd({ animated: true });
});
-
const keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', () => {
scrollViewRef.current?.scrollToEnd({ animated: true });
});
-
return () => {
keyboardDidShowListener.remove();
keyboardDidHideListener.remove();
@@ -86,145 +61,7 @@ export function ChatUI(/*props: ChatUiProps*/) {
scrollViewRef.current?.scrollToEnd({ animated: true });
}, [chat?.conversation.length]);
- useEffect(() => {
- renderMessages();
- }, [chat?.conversation, activeChatId, responseIndex]);
-
- // ------------- End keyboard and scrolling -------------
-
- // ------------- Render Chat from firebase -------------
-
- const renderMessages = () => {
- if (status === 'loading' || isCreating) return ;
- if (chat === undefined)
- return (
-
- Write a message to begin.
-
- );
-
- let i = 0;
- try {
- return chat?.conversation.map((message, index) => (
- //ChatBubble(message, (chat.id + (i++).toString()), colors, responseIndex, setResponseIndex)
-
- ));
- } catch (error) {
- return ;
- }
- };
-
- // ------------- End render Chat from firebase -------------
-
- // ------------- Sending new message to firebase -------------
-
- async function sendMessage() {
- // Create new Chat
- if (chat === undefined && text.trim()) {
- try {
- setSendButtonDisabled(true);
-
- const msg: conversationMessage = { user: text };
- const newChatData: Chat = {
- title: text,
- model: [LLM_MODELS[0].key],
- conversation: [msg],
- createdAt: Timestamp.now()
- };
- setText('');
-
- const result = await createChat(newChatData);
- if (!result) throw new Error('Failed to create new chat');
-
- const { id: newId, chat: newChat } = result;
- setActiveChatId(newId);
-
- await getLLMResponseAndUpdateFirestore(newChat.model[0], newChat); //TODO: receive answers from multiple LLMS
- } catch (error) {
- console.error('Error: ', error);
- } finally {
- setSendButtonDisabled(false);
- }
- // Send user message in existing chat
- } else if (chat?.id && text.trim()) {
- try {
- setSendButtonDisabled(true);
-
- const msg: conversationMessage = { user: text };
- const updatedConversation = [...chat.conversation, msg];
- await updateChat({ conversation: updatedConversation });
- //chat?.conversation.push(msg);
- setText('');
-
- getLLMResponseAndUpdateFirestore(getActiveLLMs(LLMs)[0], {
- ...chat,
- conversation: updatedConversation
- }); //TODO: receive answers from multiple LLMS
- } catch (error) {
- console.error('Error updating existing chat:', error);
- } finally {
- setSendButtonDisabled(false);
- }
- }
- }
-
- function getActiveLLMs(LLMs: { [key: string]: { name: string; active: boolean } }) {
- const activeLLMList = [];
- for (const [key, value] of Object.entries(LLMs)) {
- if (value.active) {
- activeLLMList.push(key);
- }
- }
- return activeLLMList;
- }
-
- async function getLLMResponseAndUpdateFirestore(model: string, currentChat: Chat) {
- if (currentChat === undefined || !currentChat.id) {
- console.error('Trying to save LLM response but chat is undefined or has no id.');
- return;
- }
-
- // Retry updating chat in case of failure TODO: currently doesnt work
- const retryUpdate = async (updateData: Partial, maxRetries = 5) => {
- for (let i = 0; i < maxRetries; i++) {
- updateChat(updateData);
-
- while (isUpdating) {
- await new Promise((resolve) => setTimeout(resolve, 500));
- }
-
- if (updatedChatSuccessfully) return;
-
- if (i === maxRetries - 1) {
- throw new Error(`Failed to update chat after ${maxRetries} attempts`);
- }
-
- await new Promise((resolve) => setTimeout(resolve, 1000)); // wait 1 second before retrying
- }
- };
-
- try {
- // smooth chat UI displays loading bubble until LLM response is received
- await retryUpdate({
- id: currentChat.id,
- conversation: [...currentChat.conversation, { loading: 'loading' }]
- });
-
- const response = await getLLMResponse(model, currentChat.conversation);
- const msg: conversationMessage = { [model]: response };
- const updatedConversation = [...currentChat.conversation, msg];
-
- await retryUpdate({ id: currentChat.id, conversation: updatedConversation });
- //await updateChat({ id: currentChat.id, conversation: updatedConversation });
- } catch (error) {
- console.error('Error in getLLMResponseAndUpdateFirestore: ', error);
- }
- }
-
- // ------------- End sending new message to firebase -------------
-
// ------------- Voice Recognition Setup -------------
-
useEffect(() => {
Voice.onSpeechStart = onSpeechStart;
Voice.onSpeechRecognized = onSpeechRecognized;
@@ -273,6 +110,40 @@ export function ChatUI(/*props: ChatUiProps*/) {
setIsRecording(false); // Reset recording state
};
+ useEffect(() => {
+ const create = async () => {
+ const { id } = await createChat({
+ title: 'NewChat',
+ model: ['gpt-4'],
+ conversation: []
+ });
+ setActiveChatId(id || '');
+ };
+ if (activeChatId === 'default') create();
+ }, [activeChatId]);
+
+ async function sendMessage() {
+ setSendButtonDisabled(true);
+ setText('');
+ try {
+ if (!isChatTitleUpdated) {
+ updateChat({ title: text });
+ setIsChatTitleUpdated(true);
+ }
+ } catch (error) {
+ console.error(error);
+ }
+ try {
+ await updateChat({ conversation: arrayUnion({ type: 'USER', message: text }) });
+ const { data } = await getResponse({ query: text, llms: ['gpt-4'] });
+ await updateChat({ conversation: arrayUnion({ type: 'AI', message: data }) });
+ } catch (error) {
+ console.error(error as FirebaseError);
+ } finally {
+ setSendButtonDisabled(false);
+ }
+ }
+
// ------------- End Voice Recognition Setup -------------
return (
@@ -282,7 +153,8 @@ export function ChatUI(/*props: ChatUiProps*/) {
contentContainerStyle={styles.scrollViewContent}
ref={scrollViewRef}
>
- {renderMessages()}
+
+ {isSendButtonDisabled && }
+ conversation: any;
};
export type User = {
diff --git a/yarn.lock b/yarn.lock
index 50a976d..054b6ec 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2372,6 +2372,11 @@
resolved "https://registry.yarnpkg.com/@react-native-firebase/dynamic-links/-/dynamic-links-20.1.0.tgz#831821e821fdbb527fe62c613852e9aa54604c0e"
integrity sha512-7pStvtln1UI1D76hvtl2uPG+RZaPE/+kDI6v4JrUba4rDxUx8n4UWX/4B7/8zrY+JXcdUiRk2KMnD3k0lPmnLA==
+"@react-native-firebase/functions@^20.1.0":
+ version "20.1.0"
+ resolved "https://registry.yarnpkg.com/@react-native-firebase/functions/-/functions-20.1.0.tgz#717932c0d57787c21fe997e2808e5a3caa2bcbf2"
+ integrity sha512-A0z3kVnbDKLkHEze4sY0iZpZ6qiUKNFbxwXjCP8AVPwanIyjQhea2o6yMxn5gMcY4W4CAihp6H5qrHu63lx7ig==
+
"@react-native-google-signin/google-signin@^12.2.0":
version "12.2.0"
resolved "https://registry.yarnpkg.com/@react-native-google-signin/google-signin/-/google-signin-12.2.0.tgz#1a5e646915f882e8beea7815ffd4ca0ca30d577b"
@@ -3291,6 +3296,11 @@ camelcase@^6.2.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
+camelize@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.1.tgz#89b7e16884056331a35d6b5ad064332c91daa6c3"
+ integrity sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==
+
caniuse-lite@^1.0.30001587:
version "1.0.30001625"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001625.tgz#ead1b155ea691d6a87938754d3cb119c24465b03"
@@ -3627,6 +3637,11 @@ crypto-random-string@^2.0.0:
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==
+css-color-keywords@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05"
+ integrity sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==
+
css-in-js-utils@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz#640ae6a33646d401fc720c54fc61c42cd76ae2bb"
@@ -3645,6 +3660,15 @@ css-select@^5.1.0:
domutils "^3.0.1"
nth-check "^2.0.1"
+css-to-react-native@^3.0.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-3.2.0.tgz#cdd8099f71024e149e4f6fe17a7d46ecd55f1e32"
+ integrity sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==
+ dependencies:
+ camelize "^1.0.0"
+ css-color-keywords "^1.0.0"
+ postcss-value-parser "^4.0.2"
+
css-tree@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d"
@@ -3919,6 +3943,11 @@ entities@^4.2.0:
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
+entities@~2.0.0:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f"
+ integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==
+
env-editor@^0.4.1:
version "0.4.2"
resolved "https://registry.yarnpkg.com/env-editor/-/env-editor-0.4.2.tgz#4e76568d0bd8f5c2b6d314a9412c8fe9aa3ae861"
@@ -5593,6 +5622,13 @@ lines-and-columns@^1.1.6:
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
+linkify-it@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.2.0.tgz#e3b54697e78bf915c70a38acd78fd09e0058b1cf"
+ integrity sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==
+ dependencies:
+ uc.micro "^1.0.1"
+
locate-path@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
@@ -5710,6 +5746,17 @@ makeerror@1.0.12:
dependencies:
tmpl "1.0.5"
+markdown-it@^10.0.0:
+ version "10.0.0"
+ resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-10.0.0.tgz#abfc64f141b1722d663402044e43927f1f50a8dc"
+ integrity sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==
+ dependencies:
+ argparse "^1.0.7"
+ entities "~2.0.0"
+ linkify-it "^2.0.0"
+ mdurl "^1.0.1"
+ uc.micro "^1.0.5"
+
marky@^1.2.2:
version "1.2.5"
resolved "https://registry.yarnpkg.com/marky/-/marky-1.2.5.tgz#55796b688cbd72390d2d399eaaf1832c9413e3c0"
@@ -5750,6 +5797,11 @@ mdn-data@2.0.14:
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
+mdurl@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
+ integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==
+
memoize-one@^5.0.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
@@ -6557,7 +6609,7 @@ possible-typed-array-names@^1.0.0:
resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f"
integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==
-postcss-value-parser@^4.2.0:
+postcss-value-parser@^4.0.2, postcss-value-parser@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
@@ -6632,7 +6684,7 @@ prompts@^2.3.2, prompts@^2.4.2:
kleur "^3.0.3"
sisteransi "^1.0.5"
-prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
+prop-types@^15.5.10, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@@ -6779,6 +6831,13 @@ react-native-element-dropdown@^2.12.0:
dependencies:
lodash "^4.17.21"
+react-native-fit-image@^1.5.5:
+ version "1.5.5"
+ resolved "https://registry.yarnpkg.com/react-native-fit-image/-/react-native-fit-image-1.5.5.tgz#c660d1ad74b9dcaa1cba27a0d9c23837e000226c"
+ integrity sha512-Wl3Vq2DQzxgsWKuW4USfck9zS7YzhvLNPpkwUUCF90bL32e1a0zOVQ3WsJILJOwzmPdHfzZmWasiiAUNBkhNkg==
+ dependencies:
+ prop-types "^15.5.10"
+
react-native-fs@^2.20.0:
version "2.20.0"
resolved "https://registry.yarnpkg.com/react-native-fs/-/react-native-fs-2.20.0.tgz#05a9362b473bfc0910772c0acbb73a78dbc810f6"
@@ -6811,6 +6870,16 @@ react-native-keyboard-aware-scroll-view@^0.9.5:
prop-types "^15.6.2"
react-native-iphone-x-helper "^1.0.3"
+react-native-markdown-display@^7.0.2:
+ version "7.0.2"
+ resolved "https://registry.yarnpkg.com/react-native-markdown-display/-/react-native-markdown-display-7.0.2.tgz#b6584cec8d6670c0141fb8780bc2f0710188a4c2"
+ integrity sha512-Mn4wotMvMfLAwbX/huMLt202W5DsdpMO/kblk+6eUs55S57VVNni1gzZCh5qpznYLjIQELNh50VIozEfY6fvaQ==
+ dependencies:
+ css-to-react-native "^3.0.0"
+ markdown-it "^10.0.0"
+ prop-types "^15.7.2"
+ react-native-fit-image "^1.5.5"
+
react-native-paper@^5.12.3:
version "5.12.3"
resolved "https://registry.yarnpkg.com/react-native-paper/-/react-native-paper-5.12.3.tgz#d583119722ebbfbb7fe40400181d63748cca3683"
@@ -8010,6 +8079,11 @@ ua-parser-js@^1.0.35:
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.38.tgz#66bb0c4c0e322fe48edfe6d446df6042e62f25e2"
integrity sha512-Aq5ppTOfvrCMgAPneW1HfWj66Xi7XL+/mIy996R1/CLS/rcyJQm6QZdsKrUeivDFQ+Oc9Wyuwor8Ze8peEoUoQ==
+uc.micro@^1.0.1, uc.micro@^1.0.5:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
+ integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
+
unbox-primitive@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"