From 2c85d93c660214dac3718f6ae7902e294197c313 Mon Sep 17 00:00:00 2001
From: Kelvin Tam <3705865+kelvinthh@users.noreply.github.com>
Date: Wed, 19 Apr 2023 22:06:57 -0400
Subject: [PATCH 1/2] Add NetInfo the check network status
---
App.tsx | 26 ++++++++++++++++++++++----
ios/Podfile.lock | 8 +++++++-
package-lock.json | 9 +++++++++
package.json | 7 ++++---
src/components/Body.tsx | 24 +++++++++++++++++++-----
src/components/Header.tsx | 6 +++++-
src/components/ImageItem.tsx | 2 +-
src/state/imageState.ts | 6 +++---
src/state/internetState.ts | 8 ++++++++
9 files changed, 78 insertions(+), 18 deletions(-)
create mode 100644 src/state/internetState.ts
diff --git a/App.tsx b/App.tsx
index cae1f9b..f35765c 100644
--- a/App.tsx
+++ b/App.tsx
@@ -1,22 +1,40 @@
import React, { useEffect } from "react";
import { SafeAreaView } from "react-native";
-import { RecoilRoot, useSetRecoilState } from "recoil";
+import { RecoilRoot, useRecoilState, useSetRecoilState } from "recoil";
import Header from "./src/components/Header";
import imageState from "./src/state/imageState";
import Body from "./src/components/Body";
import suggestionState from "./src/state/suggestionState";
import { fetchImages, fetchSuggestion } from "./src/fetchData";
-import { Toast } from 'react-native-toast-message/lib/src/Toast';
+import { Toast } from "react-native-toast-message/lib/src/Toast";
+import internetState from "./src/state/internetState";
+import NetInfo from "@react-native-community/netinfo";
const AppContent = () => {
const setImages = useSetRecoilState(imageState);
const setSuggestion = useSetRecoilState(suggestionState);
+ const [hasInternet, setHasInternet] = useRecoilState(internetState);
+
+ useEffect(() => {
+ const unsubscribe = NetInfo.addEventListener((state) => {
+ if (state.isInternetReachable !== hasInternet) {
+ console.log(
+ `Internet state: ${state.type} ${state.isInternetReachable}`
+ );
+ setHasInternet(state.isInternetReachable);
+ }
+ });
+
+ return () => {
+ unsubscribe();
+ };
+ }, []);
useEffect(() => {
(async () => {
const images = await fetchImages();
const suggestion = await fetchSuggestion();
-
+
setImages(images ?? []); // Provide an empty array as the default value
setSuggestion(suggestion ?? ""); // Provide an empty string as the default value
})();
@@ -34,7 +52,7 @@ const App = () => {
return (
-
+
);
};
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 933b5a8..e35d570 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -361,6 +361,8 @@ PODS:
- React-jsinspector (0.71.6)
- React-logger (0.71.6):
- glog
+ - react-native-netinfo (9.3.9):
+ - React-Core
- React-perflogger (0.71.6)
- React-RCTActionSheet (0.71.6):
- React-Core/RCTActionSheetHeaders (= 0.71.6)
@@ -485,6 +487,7 @@ DEPENDENCIES:
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
+ - "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
- React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
@@ -576,6 +579,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/jsinspector"
React-logger:
:path: "../node_modules/react-native/ReactCommon/logger"
+ react-native-netinfo:
+ :path: "../node_modules/@react-native-community/netinfo"
React-perflogger:
:path: "../node_modules/react-native/ReactCommon/reactperflogger"
React-RCTActionSheet:
@@ -643,6 +648,7 @@ SPEC CHECKSUMS:
React-jsiexecutor: 7894956638ff3e00819dd3f9f6f4a84da38f2409
React-jsinspector: d5ce2ef3eb8fd30c28389d0bc577918c70821bd6
React-logger: 9332c3e7b4ef007a0211c0a9868253aac3e1da82
+ react-native-netinfo: 22c082970cbd99071a4e5aa7a612ac20d66b08f0
React-perflogger: 43392072a5b867a504e2b4857606f8fc5a403d7f
React-RCTActionSheet: c7b67c125bebeda9fb19fc7b200d85cb9d6899c4
React-RCTAnimation: c2de79906f607986633a7114bee44854e4c7e2f5
@@ -660,4 +666,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: ebe432ad56513222ebfc09b56e491909e15bf652
-COCOAPODS: 1.12.0
+COCOAPODS: 1.12.1
diff --git a/package-lock.json b/package-lock.json
index b18be02..321d6a9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,7 @@
"name": "img-gen-rn",
"version": "1.0.0",
"dependencies": {
+ "@react-native-community/netinfo": "^9.3.9",
"axios": "^1.3.5",
"expo": "~48.0.10",
"expo-dev-client": "~2.1.6",
@@ -4695,6 +4696,14 @@
"node": ">=8"
}
},
+ "node_modules/@react-native-community/netinfo": {
+ "version": "9.3.9",
+ "resolved": "https://registry.npmjs.org/@react-native-community/netinfo/-/netinfo-9.3.9.tgz",
+ "integrity": "sha512-L9f8OjX5Fwh5CdP4ygDPa6iQCJJ4tAtXiFuQp6EG4/sdSXDqOXaehAhJrZAN8P8Lztnf8YN8p836GmZuBCrY+A==",
+ "peerDependencies": {
+ "react-native": ">=0.59"
+ }
+ },
"node_modules/@react-native/assets": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@react-native/assets/-/assets-1.0.0.tgz",
diff --git a/package.json b/package.json
index d5b51e8..0dfe3bc 100644
--- a/package.json
+++ b/package.json
@@ -8,8 +8,11 @@
"web": "expo start --web"
},
"dependencies": {
+ "@react-native-community/netinfo": "^9.3.9",
"axios": "^1.3.5",
"expo": "~48.0.10",
+ "expo-dev-client": "~2.1.6",
+ "expo-splash-screen": "~0.18.1",
"expo-status-bar": "~1.4.4",
"nativewind": "^2.0.11",
"react": "18.2.0",
@@ -17,9 +20,7 @@
"react-native-dotenv": "^3.4.8",
"react-native-toast-message": "^2.1.6",
"recoil": "^0.7.7",
- "tailwindcss": "^3.3.1",
- "expo-dev-client": "~2.1.6",
- "expo-splash-screen": "~0.18.1"
+ "tailwindcss": "^3.3.1"
},
"devDependencies": {
"@babel/core": "^7.20.0",
diff --git a/src/components/Body.tsx b/src/components/Body.tsx
index d52e59c..1455de0 100644
--- a/src/components/Body.tsx
+++ b/src/components/Body.tsx
@@ -10,13 +10,14 @@ import {
Keyboard,
RefreshControl,
} from "react-native";
-import { useRecoilState } from "recoil";
+import { useRecoilState, useRecoilValue } from "recoil";
import imagesState from "../state/imageState";
import suggestionState from "../state/suggestionState";
import { ImageUrl } from "../types/imageUrl";
import { fetchImages, fetchSuggestion, generateImage } from "../fetchData";
import ImageItem from "../components/ImageItem";
import { Toast } from "react-native-toast-message/lib/src/Toast";
+import internetState from "../state/internetState";
const Body = () => {
const [images, setImages] = useRecoilState(imagesState);
@@ -25,6 +26,7 @@ const Body = () => {
const [inputValue, setInputValue] = useState("");
const [refreshing, setRefreshing] = useState(false);
const [generating, setGenerating] = useState(false);
+ const hasInternet = useRecoilValue(internetState);
// Reference for the FlatList
const flatListRef = useRef>(null);
@@ -124,22 +126,23 @@ const Body = () => {
!inputValue || generating ? " bg-fuchsia-200" : "bg-fuchsia-600"
}`}
onPress={!generating ? () => handleSubmit(inputValue) : undefined}
- disabled={!inputValue || generating}
+ disabled={!inputValue || generating || !hasInternet}
>
{generating ? "Loading..." : "Submit"}
- {inputValue && (
+ {inputValue && (suggestion || hasInternet) && (
- Suggestion💡
+ Suggestion💡
{suggestion}
)}
Gimme a new suggestion!
@@ -149,8 +152,18 @@ const Body = () => {
>
Use suggestion!
+ {!hasInternet && (
+
+ ⛔️ Error: No internet...
+
+ )}
{/* */}
+ {!hasInternet && !images ? (
+
+ ⛔️ Unable to connect to the internet...
+
+ ) : (
{
refreshControl={
}
- className='mx-4'
+ className="mx-4"
/>
+ )}
);
};
diff --git a/src/components/Header.tsx b/src/components/Header.tsx
index bbd9bb9..44b1352 100644
--- a/src/components/Header.tsx
+++ b/src/components/Header.tsx
@@ -14,7 +14,11 @@ const Header = () => {
Image Generator by Kelvin Tam
Powered by OpenAI & Azure
- openURL("https://github.com/kelvinthh/Image-Generation-RN")}>
+
+ openURL("https://github.com/kelvinthh/Image-Generation-RN")
+ }
+ >
diff --git a/src/components/ImageItem.tsx b/src/components/ImageItem.tsx
index 2058e3f..8b68619 100644
--- a/src/components/ImageItem.tsx
+++ b/src/components/ImageItem.tsx
@@ -55,7 +55,7 @@ const ImageItem: React.FC = ({ item }) => {
{loading && (
- Loading...
+ Loading...
)}
({
- key: 'imagesState',
+ key: "imagesState",
default: [],
});
diff --git a/src/state/internetState.ts b/src/state/internetState.ts
new file mode 100644
index 0000000..a258816
--- /dev/null
+++ b/src/state/internetState.ts
@@ -0,0 +1,8 @@
+import { atom } from "recoil";
+
+const internetState = atom({
+ key: "internetState",
+ default: null,
+});
+
+export default internetState;
From 260c9b6392ac1c2507e5cff5deeaa75330e2c1be Mon Sep 17 00:00:00 2001
From: Kelvin Tam <3705865+kelvinthh@users.noreply.github.com>
Date: Thu, 20 Apr 2023 05:20:54 -0400
Subject: [PATCH 2/2] Major update
- Fix status bar text
- Optimize network checking, add toast pop-up
- When no internet, submit & get new suggestion buttons will be greyed out
- Suggestion no longer displays when it's in the input prompt
- Update README.md
---
App.tsx | 11 +--
README.md | 5 +-
ios/imggenrn/Info.plist | 158 ++++++++++++++++++++--------------------
src/components/Body.tsx | 75 ++++++++++++-------
4 files changed, 133 insertions(+), 116 deletions(-)
diff --git a/App.tsx b/App.tsx
index f35765c..46846bb 100644
--- a/App.tsx
+++ b/App.tsx
@@ -1,5 +1,5 @@
import React, { useEffect } from "react";
-import { SafeAreaView } from "react-native";
+import { SafeAreaView, StatusBar } from "react-native";
import { RecoilRoot, useRecoilState, useSetRecoilState } from "recoil";
import Header from "./src/components/Header";
import imageState from "./src/state/imageState";
@@ -17,12 +17,8 @@ const AppContent = () => {
useEffect(() => {
const unsubscribe = NetInfo.addEventListener((state) => {
- if (state.isInternetReachable !== hasInternet) {
- console.log(
- `Internet state: ${state.type} ${state.isInternetReachable}`
- );
- setHasInternet(state.isInternetReachable);
- }
+ console.log(`Internet state: ${state.type} ${state.isInternetReachable}`);
+ setHasInternet(state.isInternetReachable);
});
return () => {
@@ -44,6 +40,7 @@ const AppContent = () => {
+
);
};
diff --git a/README.md b/README.md
index f0e1cfc..4027bca 100644
--- a/README.md
+++ b/README.md
@@ -36,10 +36,11 @@ API_GET_IMAGES= // getImages API endpoint, e.g. /api/getImages
API_GET_SUGGESTIONS= // getChatGPTSuggestion API endpoint
API_GENERATE_IMAGE= // generateImage API endpoint
```
+5. Download **Expo Go** app from Google Play/App Store, run `npx expo start` in the terminal to start the development server, then scan the QR Code within Expo Go app or your phone's camera app.
-5. Run the app on your preferred platform (iOS or Android) using `npx expo run:ios` or `npx expo run:android`.
+6. Or to build the app, run the app on your preferred platform (iOS or Android) using `npx expo run:ios` or `npx expo run:android`.
-6. Enjoy generating and exploring images based on your text prompts! 🌈
+7. Enjoy generating and exploring images based on your text prompts! 🌈
## Additional Information ℹ️
diff --git a/ios/imggenrn/Info.plist b/ios/imggenrn/Info.plist
index 3c70324..0ce0d60 100644
--- a/ios/imggenrn/Info.plist
+++ b/ios/imggenrn/Info.plist
@@ -1,82 +1,82 @@
-
- CFBundleDevelopmentRegion
- $(DEVELOPMENT_LANGUAGE)
- CFBundleDisplayName
- AI Image Gen
- CFBundleExecutable
- $(EXECUTABLE_NAME)
- CFBundleIdentifier
- $(PRODUCT_BUNDLE_IDENTIFIER)
- CFBundleInfoDictionaryVersion
- 6.0
- CFBundleName
- $(PRODUCT_NAME)
- CFBundlePackageType
- $(PRODUCT_BUNDLE_PACKAGE_TYPE)
- CFBundleShortVersionString
- 1.0.0
- CFBundleSignature
- ????
- CFBundleURLTypes
-
-
- CFBundleURLSchemes
-
- com.hhtam.imggenrn
-
-
-
- CFBundleURLSchemes
-
- exp+img-gen-rn
-
-
-
- CFBundleVersion
- 1
- LSRequiresIPhoneOS
-
- NSAppTransportSecurity
-
- NSAllowsArbitraryLoads
-
- NSExceptionDomains
-
- localhost
-
- NSExceptionAllowsInsecureHTTPLoads
-
-
-
-
- UILaunchStoryboardName
- SplashScreen
- UIRequiredDeviceCapabilities
-
- armv7
-
- UIRequiresFullScreen
-
- UIStatusBarStyle
- UIStatusBarStyleDefault
- UISupportedInterfaceOrientations
-
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationPortraitUpsideDown
-
- UISupportedInterfaceOrientations~ipad
-
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationPortraitUpsideDown
- UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRight
-
- UIUserInterfaceStyle
- Light
- UIViewControllerBasedStatusBarAppearance
-
-
-
\ No newline at end of file
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleDisplayName
+ AI Image Gen
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ $(PRODUCT_BUNDLE_PACKAGE_TYPE)
+ CFBundleShortVersionString
+ 1.0.0
+ CFBundleSignature
+ ????
+ CFBundleURLTypes
+
+
+ CFBundleURLSchemes
+
+ com.hhtam.imggenrn
+
+
+
+ CFBundleURLSchemes
+
+ exp+img-gen-rn
+
+
+
+ CFBundleVersion
+ 1
+ LSRequiresIPhoneOS
+
+ NSAppTransportSecurity
+
+ NSAllowsArbitraryLoads
+
+ NSExceptionDomains
+
+ localhost
+
+ NSExceptionAllowsInsecureHTTPLoads
+
+
+
+
+ UILaunchStoryboardName
+ SplashScreen
+ UIRequiredDeviceCapabilities
+
+ armv7
+
+ UIRequiresFullScreen
+
+ UIStatusBarStyle
+ UIStatusBarStyleDefault
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UIUserInterfaceStyle
+ Light
+ UIViewControllerBasedStatusBarAppearance
+
+
+
diff --git a/src/components/Body.tsx b/src/components/Body.tsx
index 1455de0..b2d0f7a 100644
--- a/src/components/Body.tsx
+++ b/src/components/Body.tsx
@@ -1,4 +1,4 @@
-import React, { useState, useRef } from "react";
+import React, { useState, useRef, useEffect } from "react";
import {
View,
Text,
@@ -30,6 +30,25 @@ const Body = () => {
// Reference for the FlatList
const flatListRef = useRef>(null);
+ const isFirstRender = useRef(true);
+
+ useEffect(() => {
+
+ // Prevent the toast pop up on the first render
+ if (isFirstRender.current) {
+ isFirstRender.current = false;
+ return;
+ }
+
+ if (!hasInternet) {
+ Toast.show({
+ type: "error",
+ text1: "Error",
+ text2: "No internet connection.",
+ position: "bottom",
+ });
+ }
+ }, [hasInternet]);
const handleSubmit = async (prompt: string) => {
setGenerating(true);
@@ -99,6 +118,7 @@ const Body = () => {
};
const handleRefreshImage = async () => {
+ if (!hasInternet) return;
const newImages = await fetchImages();
setImages(newImages ?? []);
};
@@ -123,7 +143,9 @@ const Body = () => {
handleSubmit(inputValue) : undefined}
disabled={!inputValue || generating || !hasInternet}
@@ -133,14 +155,18 @@ const Body = () => {
- {inputValue && (suggestion || hasInternet) && (
-
- Suggestion💡
- {suggestion}
-
- )}
+ {inputValue &&
+ !inputValue.includes(suggestion) &&
+ (suggestion || hasInternet) && (
+
+ Suggestion💡
+ {suggestion}
+
+ )}
@@ -152,29 +178,22 @@ const Body = () => {
>
Use suggestion!
- {!hasInternet && (
+ {(!hasInternet && !isFirstRender.current) && (
- ⛔️ Error: No internet...
+ ⛔️ Error: No internet connection.
)}
- {/* */}
- {!hasInternet && !images ? (
-
- ⛔️ Unable to connect to the internet...
-
- ) : (
- item.name}
- refreshControl={
-
- }
- className="mx-4"
- />
- )}
+ item.name}
+ refreshControl={
+
+ }
+ className="mx-4"
+ />
);
};