diff --git a/ApplicationRoot.js b/ApplicationRoot.js index 2572a1ec..ce2acd7e 100644 --- a/ApplicationRoot.js +++ b/ApplicationRoot.js @@ -1,7 +1,7 @@ -import React, { useState, useCallback, useEffect} from 'react'; +import React, { useState, useCallback, useEffect } from 'react'; import { StyleSheet, View, TouchableOpacity, Linking, Platform, Animated } from 'react-native'; import AboutUsScreen from './screens/AboutUsScreen'; -import ChatSessionScreen from './screens/ChatSessionScreen'; +import HomeSearchScreen from './screens/HomeSearchScreen.js'; import ChatSessionHistoriesScreen from './screens/ChatSessionHistoriesScreen'; import MyPalettesScreen from './screens/MyPalettesScreen.js'; import Colors from './constants/Styles'; @@ -10,6 +10,7 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack'; import ColorDetailsScreen from './screens/ColorDetailScreen'; import PalettesScreen from './screens/PalettesScreen'; import SavePaletteScreen from './screens/SavePaletteScreen'; +import ChatSessionScreen from './screens/ChatSessionScreen'; import ColorListScreen from './screens/ColorListScreen'; import PaletteViewScreen from './screens/PaletteViewScreen.js'; import PaletteEditScreen from './screens/PaletteEditScreen.js'; @@ -29,7 +30,7 @@ import UserProfile from './screens/UserProfileScreen.js'; import useApplicationStore from './hooks/useApplicationStore.js'; import ExplorePaletteScreen from './screens/ExplorePaletteScreen.js'; import { notifyMessage } from './libs/Helpers.js'; -import FlashMessage from "react-native-flash-message"; +import FlashMessage from 'react-native-flash-message'; import ShareMenu from 'react-native-share-menu'; import Color from 'pigment/full'; import { logEvent } from './libs/Helpers.js'; @@ -43,11 +44,11 @@ export default function App() { const { loadInitPaletteFromStore } = applicationState; const [isMenuOpen, setMenu] = useState(false); const navigationRef = useNavigationContainerRef(); - useIAPConnection(function(error) { + useIAPConnection(function (error) { if (error) { // TODO: figure out a better way to handle this error and show user a way to retry, ask for help or continue. - notifyMessage("Error during purchase initialization. Purchase might not work. Please retry"); - } + notifyMessage('Error during purchase initialization. Purchase might not work. Please retry'); + } loadInitPaletteFromStore(); // Still load the palettes. Specially simulator will always face this issue. }); @@ -84,7 +85,7 @@ export default function App() { }); } catch (error) { notifyMessage('Error parsing url: ' + error.message); - navigationRef.navigate(ROUTE_NAMES.HOME); + navigationRef.navigate(ROUTE_NAMES.HOME_SEARCH); } }, [navigationRef] @@ -165,12 +166,22 @@ export default function App() { height: HEADER_HEIGHT } }}> + { return { headerLeft: hamburgerMenuIcon, - headerTitleContainerStyle: { left: 40 }, + headerTitleContainerStyle: { left: 40 }, title: t('My Palettes') }; }} @@ -189,7 +200,7 @@ export default function App() { options={{ title: t('About us') }} component={AboutUsScreen} /> - + - + ); @@ -248,7 +259,7 @@ const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', - flexDirection: 'row', + flexDirection: 'row' }, sideMenuIcon: { fontSize: 25, diff --git a/components/GridActionButton.js b/components/GridActionButton.js index fb9a8108..732ba513 100644 --- a/components/GridActionButton.js +++ b/components/GridActionButton.js @@ -8,7 +8,7 @@ import { Text, Image, TouchableOpacity, - Platform, + Platform } from 'react-native'; import Color from 'pigment/full'; import RNColorThief from 'react-native-color-thief'; @@ -30,8 +30,7 @@ import useApplicationStore from '../hooks/useApplicationStore'; import MultiColorView from './MultiColorView'; import { ROUTE_NAMES } from '../libs/constants'; - -const GridActionButton = ({ navigation}) => { +const GridActionButton = ({ navigation }) => { const { t } = useTranslation(); const [pickImageLoading, setPickImageLoading] = useState(false); const [isColorPickerVisible, setIsColorPickerVisible] = React.useState(false); @@ -174,38 +173,38 @@ const GridActionButton = ({ navigation}) => { logEvent('chat_session_action_button'); props.navigation.reset({ index: 0, - routes: [{ name: ROUTE_NAMES.CHAT_SESSION }], + routes: [{ name: ROUTE_NAMES.HOME_SEARCH }] }); } }, - Platform.OS == 'android' ? ( - pro.plan !== 'starter' - ? { - id: 6, - icon: , - text1: t('Create palette'), - text2: t(' manually'), - onPress: () => { - logEvent('create_new_palette'); - navigation.navigate('SavePalette'); + Platform.OS == 'android' + ? pro.plan !== 'starter' + ? { + id: 6, + icon: , + text1: t('Create palette'), + text2: t(' manually'), + onPress: () => { + logEvent('create_new_palette'); + navigation.navigate('SavePalette'); + } } - } - : { - id: 7, - icon: , - text1: t('Unlock'), - text2: t('Pro'), - onPress: () => { - logEvent('home_screen_pro_button'); - navigation.navigate('ProVersion'); + : { + id: 7, + icon: , + text1: t('Unlock'), + text2: t('Pro'), + onPress: () => { + logEvent('home_screen_pro_button'); + navigation.navigate('ProVersion'); + } } - }) : null + : null ].filter(Boolean) ]; return ( <> - + { setSelectedImage(null); setAutomaticColors([]); }}> - {pickImageLoading && - - } + {pickImageLoading && ( + + )} { setIsImagePickerModalVisible(false); @@ -256,22 +255,23 @@ const GridActionButton = ({ navigation}) => { {t('Next')} - {Platform.OS == 'android' && (<> - - {t('OR')} - - - - {t('Pick colors Manually')} - - + {Platform.OS == 'android' && ( + <> + + + {t('OR')} + + + + {t('Pick colors Manually')} + + )} - @@ -319,7 +319,7 @@ const styles = { height: 120 }, imageExtractedColorPreview: { - margin: 16, + margin: 16 }, colorPickerModalContent: { backgroundColor: 'white', @@ -333,7 +333,7 @@ const styles = { previewImage: { height: 150, resizeMode: 'contain', - marginHorizontal: 20, + marginHorizontal: 20 }, colorPreviewContainer: { display: 'flex', @@ -349,7 +349,7 @@ const styles = { }, pickColorsButton: { alignItems: 'center', - marginVertical: 8, + marginVertical: 8 }, pickColorsButtonText: { fontSize: 18, diff --git a/components/HamburgerMenu.js b/components/HamburgerMenu.js index be94b2c3..20079c7a 100644 --- a/components/HamburgerMenu.js +++ b/components/HamburgerMenu.js @@ -57,14 +57,14 @@ const HamburgerMenu = (props) => { - { logEvent('home'); props.toggleSideMenu(); props.navigation.reset({ index: 0, - routes: [{ name: ROUTE_NAMES.HOME }], + routes: [{ name: ROUTE_NAMES.HOME_SEARCH }] }); }}> @@ -100,20 +100,21 @@ const HamburgerMenu = (props) => { {t('Explore Palettes')} - {Platform.OS == 'ios' && { - logEvent('hm_palette_library'); - navigate('PaletteLibrary'); - }}> - - - + {Platform.OS == 'ios' && ( + { + logEvent('hm_palette_library'); + navigate('PaletteLibrary'); + }}> + + + + + {t('Palette Library')} - {t('Palette Library')} - - - } + + )} { { : 'https://apps.apple.com/app/id1596763657?action=write-review' } icon={}> - {t('Rate us on ' + (Platform.OS == 'android' ? 'PlayStore' : "AppStore"))} + {t('Rate us on ' + (Platform.OS == 'android' ? 'PlayStore' : 'AppStore'))} } { {t('About us')} - { Platform.OS == 'android' && // right now histories requires login. That's why enabling it in only android. + {Platform.OS == 'android' && ( // right now histories requires login. That's why enabling it in only android. { @@ -166,8 +167,8 @@ const HamburgerMenu = (props) => { {t('Huehive AI chat history')} - } - {(Platform.OS == 'android' && !userData) && ( + )} + {Platform.OS == 'android' && !userData && ( { @@ -183,7 +184,7 @@ const HamburgerMenu = (props) => { )} - {(Platform.OS == 'android') && ( + {Platform.OS == 'android' && ( { diff --git a/hooks/useChatSession.js b/hooks/useChatSession.js index 17685aec..e0dc79cf 100644 --- a/hooks/useChatSession.js +++ b/hooks/useChatSession.js @@ -12,14 +12,16 @@ const useChatSession = (initialMessages) => { const interval = setInterval(async () => { try { const messageResponse = await getChatSession(chatSession.data.id, latestMessage.id); + console.log('Test it ', latestMessage.id); if (messageResponse.data.length > 0) { + console.log(messageResponse.data); clearInterval(interval); setMessages((prevMessages) => [...prevMessages, ...messageResponse.data]); setIsLoading(false); } } catch (error) { console.error('Error fetching chat session updates', error); - setError(error); + setError(error.toString()); sendClientError('fetch_new_message', error.message); clearInterval(interval); } @@ -38,7 +40,7 @@ const useChatSession = (initialMessages) => { return chatSession; } catch (error) { console.error('Error creating chat session', error); - setError(error); + setError(error.toString()); sendClientError('create_session', error.message); throw error; } finally { diff --git a/libs/constants.js b/libs/constants.js index 342e36b8..92bb8da5 100644 --- a/libs/constants.js +++ b/libs/constants.js @@ -1,7 +1,7 @@ export const ROUTE_NAMES = { MY_PALETTES: 'MyPalettes', - HOME: 'ChatSession', ABOUT_US: 'AboutUs', + HOME_SEARCH: 'HomeSearch', CHAT_SESSION: 'ChatSession', CHAT_SESSION_HISTORIES: 'ChatSessionHistories', COLOR_DETAILS: 'ColorDetails', @@ -16,6 +16,6 @@ export const ROUTE_NAMES = { export const NUMBER_OF_COLORS_PRO_COUNT = 10; export const PRIVATE_ROUTES = new Set([ - ROUTE_NAMES.CHAT_SESSION, + ROUTE_NAMES.HOME_SEARCH, ROUTE_NAMES.CHAT_SESSION_HISTORIES ]); diff --git a/screens/ChatSessionScreen.js b/screens/ChatSessionScreen.js index ecc9df0d..4b7d43fd 100644 --- a/screens/ChatSessionScreen.js +++ b/screens/ChatSessionScreen.js @@ -7,9 +7,9 @@ import { TextInput, ActivityIndicator, Text, - ImageBackground, - Platform, + ImageBackground } from 'react-native'; +import FontAwesome from 'react-native-vector-icons/FontAwesome'; import Colors from '../constants/Styles'; import React, { useState, useEffect, useRef } from 'react'; import { material } from 'react-native-typography'; @@ -18,8 +18,6 @@ import ChatCard from '../components/ChatCard'; import CromaButton from '../components/CromaButton'; import useChatSession from '../hooks/useChatSession'; import useApplicationStore from '../hooks/useApplicationStore'; -import GridActionButton from '../components/GridActionButton'; -import AdBanner from '../components/AdBanner'; // Import the new AdBanner component const bgImage = require('../assets/images/colorful_background.jpg'); @@ -32,16 +30,17 @@ const ChatSessionScreen = (props) => { useChatSession(route.params?.messages); useEffect(() => { - logEvent('chat_session_screen'); + logEvent('chat_session_follow_up_screen'); + handleSendMessage(route.params.userQuery); }, []); - const handleSendMessage = async () => { + const handleSendMessage = async (userQuery) => { const message = { chat_session: { chat_session_type: 'color_palette', messages_attributes: [ { - message: inputText, + message: userQuery || inputText, sender_type: 'user' } ] @@ -58,13 +57,6 @@ const ChatSessionScreen = (props) => { scrollViewRef.current.scrollToEnd({ animated: true }); }; - useEffect(() => { - let interval; - return () => { - clearInterval(interval); - }; - }, [messages]); - function showUnlockPro() { return pro.plan === 'starter' && messages.length >= 2; } @@ -84,42 +76,15 @@ const ChatSessionScreen = (props) => { ref={scrollViewRef} style={styles.chat_container} showsVerticalScrollIndicator={false}> - {messages.length === 0 ? ( - - - Welcome to HueHive AI! - - - Start by generating a color palette for your next project. - - - - - Generate - - - - ) : ( - messages.map((message, index) => ( - - )) - )} + {messages.map((message, index) => ( + + ))} + {error && ( @@ -128,9 +93,9 @@ const ChatSessionScreen = (props) => { {error} )} - - - {!showUnlockPro() && messages.length > 0 && ( + + {/* {!showUnlockPro() && messages.length > 0 && ( */} + {true && ( { /> { + handleSendMessage(); + }} style={ - isLoading || inputText.trim() === '' ? styles.disableSendButton : styles.sendButton + isLoading || inputText.trim() === '' + ? styles.disableSendButton + : styles.sendButton }> Send )} - + {showUnlockPro() && ( { )} )} - - { messages.length < 2 && Platform.OS == 'android' && } - - ); }; @@ -209,33 +174,46 @@ const styles = StyleSheet.create({ marginBottom: 20 }, searchInputContainer: { - flexDirection: 'row', + flexDirection: 'column', alignItems: 'center', - justifyContent: 'center' + justifyContent: 'center', + width: '100%', + gap: 15, + marginTop: 10 }, input: { - flex: 1, - marginRight: 12, - paddingVertical: 8, - paddingHorizontal: 12, borderWidth: 1, borderColor: 'gray', borderRadius: 8, - height: 40 + flex: 1, + height: 40, + fontSize: 16 }, generateButton: { - padding: 8, + padding: 10, + paddingLeft: 25, + paddingRight: 25, backgroundColor: Colors.primary, - borderRadius: 8 + borderRadius: 8, + display: 'flex', + flexDirection: 'row', + gap: 5, + alignItems: 'center' }, disableGenerateButton: { - padding: 8, + padding: 7, + paddingLeft: 25, + paddingRight: 25, backgroundColor: 'gray', - borderRadius: 8 + borderRadius: 8, + display: 'flex', + flexDirection: 'row', + gap: 5, + alignItems: 'center' }, textGenerate: { color: 'white', - fontSize: 16, + fontSize: 20, fontWeight: 'bold', textAlign: 'center' }, diff --git a/screens/HomeSearchScreen.js b/screens/HomeSearchScreen.js new file mode 100644 index 00000000..a25226dd --- /dev/null +++ b/screens/HomeSearchScreen.js @@ -0,0 +1,160 @@ +import { + ScrollView, + StyleSheet, + TouchableOpacity, + View, + TextInput, + Text, + ImageBackground +} from 'react-native'; +import FontAwesome from 'react-native-vector-icons/FontAwesome'; +import Colors from '../constants/Styles'; +import React, { useState, useEffect, useRef } from 'react'; +import { material } from 'react-native-typography'; +import { logEvent } from '../libs/Helpers'; +import useApplicationStore from '../hooks/useApplicationStore'; +import GridActionButton from '../components/GridActionButton'; +import AdBanner from '../components/AdBanner'; + +const bgImage = require('../assets/images/colorful_background.jpg'); + +const ChatSessionScreen = (props) => { + const { navigation } = props; + const [userQuery, setUserQuery] = useState(''); + const scrollViewRef = useRef(); + const { pro } = useApplicationStore(); + + useEffect(() => { + logEvent('chat_session_follow_up_screen'); + }, []); + + return ( + + + + + + Welcome to HueHive AI! + + Create a color palette using AI or explore various methods to extract a color + palette. + + + + { + navigation.navigate('ChatSession', { userQuery: userQuery }); + }} + style={ + userQuery.trim() === '' ? styles.disableGenerateButton : styles.generateButton + }> + Generate + + + + + + {Platform.OS == 'android' && } + + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + display: 'flex', + flexDirection: 'column', + backgroundColor: '#d6e4ff', + flex: 1 + }, + chat_container: { + flex: 1, + padding: 5 + }, + backgroundImage: { + flex: 1, + resizeMode: 'cover', + justifyContent: 'center' + }, + bgImageOpecity: { + backgroundColor: 'rgba(255, 255, 255, 0.6)', + flex: 1 + }, + searchContainer: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + padding: 20 + }, + searchTitle: { + ...material.headline, + textAlign: 'center', + marginBottom: 10 + }, + searchSubtitle: { + ...material.body1, + textAlign: 'center', + marginBottom: 20 + }, + searchInputContainer: { + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + width: '100%', + gap: 15, + marginTop: 10 + }, + input: { + borderWidth: 1, + borderColor: 'gray', + borderRadius: 8, + width: '100%', + height: 40, + fontSize: 16 + }, + generateButton: { + padding: 10, + paddingLeft: 25, + paddingRight: 25, + backgroundColor: Colors.primary, + borderRadius: 8, + display: 'flex', + flexDirection: 'row', + gap: 5, + alignItems: 'center' + }, + disableGenerateButton: { + padding: 7, + paddingLeft: 25, + paddingRight: 25, + backgroundColor: 'gray', + borderRadius: 8, + display: 'flex', + flexDirection: 'row', + gap: 5, + alignItems: 'center' + }, + textGenerate: { + color: 'white', + fontSize: 20, + fontWeight: 'bold', + textAlign: 'center' + } +}); + +export default ChatSessionScreen; diff --git a/screens/UserProfileScreen.js b/screens/UserProfileScreen.js index c8ced28c..ce210e50 100644 --- a/screens/UserProfileScreen.js +++ b/screens/UserProfileScreen.js @@ -27,7 +27,7 @@ function UserProfile(props) { }, []); const { t } = useTranslation(); - + useEffect(() => { GoogleSignin.configure({ webClientId: '865618605576-j2tb9toevqc7tonmbp01dim1ddvod7r0.apps.googleusercontent.com', @@ -40,7 +40,7 @@ function UserProfile(props) { const consentInfo = await AdsConsent.requestInfoUpdate(); setConsentInfo(consentInfo); } - + fetchConsentStatus(); }, []); @@ -62,7 +62,7 @@ function UserProfile(props) { await removeUserSession(); await loadUserData(); reloadPalettes(); - props.navigation.navigate(ROUTE_NAMES.HOME); + props.navigation.navigate(ROUTE_NAMES.HOME_SEARCH); try { await GoogleSignin.revokeAccess(); } catch (error) { @@ -72,7 +72,7 @@ function UserProfile(props) { return ( - {userData && + {userData && ( @@ -90,9 +90,9 @@ function UserProfile(props) { {t('Logout')} - } - - {!userData && + )} + + {!userData && ( {t('Sign In / Sign Up')} - } + )} - { consentInfo.status == 'REQUIRED' && - - - {consentInfo.canRequestAds ? t('Withdraw Ad Consent') : t('Give Ad Consent')} - - - } + {consentInfo.status == 'REQUIRED' && ( + + + {consentInfo.canRequestAds ? t('Withdraw Ad Consent') : t('Give Ad Consent')} + + + )} ); }