From f1c7d0e3901693ebc9a5cf0deff22c881b7a7d7c Mon Sep 17 00:00:00 2001 From: ankushcodes69 Date: Wed, 22 Jan 2025 23:48:15 +0530 Subject: [PATCH] Add UpdateModal component to show latest update --- app/(tabs)/_layout.tsx | 4 +- app/(tabs)/index.tsx | 1 - components/MessageModal.tsx | 6 +- components/UpdateModal.tsx | 167 ++++++++++++++++++++++++++++++++++++ package-lock.json | 9 ++ package.json | 5 +- 6 files changed, 185 insertions(+), 7 deletions(-) create mode 100644 components/UpdateModal.tsx diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx index 23cfd4e..0a0c9d4 100644 --- a/app/(tabs)/_layout.tsx +++ b/app/(tabs)/_layout.tsx @@ -5,13 +5,15 @@ import { TabBarIcon } from "@/components/navigation/TabBarIcon"; import { Colors } from "@/constants/Colors"; import { FloatingPlayer } from "@/components/FloatingPlayer"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import { MessageModal } from "@/components/MessageModal"; // Import MessageModal +import { MessageModal } from "@/components/MessageModal"; +import { UpdateModal } from "@/components/UpdateModal"; function TabLayoutContent() { const { bottom } = useSafeAreaInsets(); return ( + { + const [isModalVisible, setIsModalVisible] = useState(false); + const [latestVersion, setLatestVersion] = useState(null); + + function compareVersions(v1: string, v2: string): number { + const normalizeVersion = (version: string) => { + return version.startsWith("v") ? version.slice(1) : version; + }; + + const [major1, minor1, patch1] = normalizeVersion(v1) + .split(".") + .map(Number); + const [major2, minor2, patch2] = normalizeVersion(v2) + .split(".") + .map(Number); + + if (major1 !== major2) return major1 > major2 ? 1 : -1; + if (minor1 !== minor2) return minor1 > minor2 ? 1 : -1; + if (patch1 !== patch2) return patch1 > patch2 ? 1 : -1; + return 0; + } + + const message = `A new version of AudioScape is available!\n\nPlease update to version ${latestVersion} to get the latest features and bug fixes.\n\nDownload and install the latest version from "Assets" section from : https://github.com/ankushcodes69/AudioScape/releases/latest`; + let flag = false; + + useEffect(() => { + const fetchMessage = async () => { + try { + const response = await fetch( + "https://api.github.com/repos/ankushcodes69/AudioScape/releases/latest", + { + headers: { + Accept: "application/vnd.github.v3+json", + }, + } + ); + + if (!response.ok) { + throw new Error(`Error: ${response.status}`); + } + + const data = await response.json(); + + if ( + compareVersions( + `${Application.nativeApplicationVersion}`, + data.tag_name + ) == -1 + ) { + setLatestVersion(data.tag_name); + setIsModalVisible(true); + } else flag = true; + } catch (err: any) { + console.error(err.message); + } + }; + + fetchMessage(); + }, []); + + const handleLinkPress = (url: string) => { + Linking.openURL(url).catch((err) => + console.error("Failed to open URL:", err) + ); + }; + + if (flag) return null; + + return ( + setIsModalVisible(false)} + > + + + + {message + .replace(/\\n/g, "\n") + .split(/(https?:\/\/\S+)/) + .map((part, index) => + /^https?:\/\//.test(part) ? ( + handleLinkPress(part)} + > + {part} + + ) : ( + {part} + ) + )} + + + setIsModalVisible(false)} + > + Dismiss + + + + + ); +}; + +const styles = StyleSheet.create({ + modalOverlay: { + flex: 1, + justifyContent: "center", + alignItems: "center", + backgroundColor: "rgba(0, 0, 0, 0.3)", + }, + modalContent: { + width: "85%", + maxWidth: 300, + padding: 10, + backgroundColor: Colors.background, + borderRadius: 10, + alignItems: "center", + shadowColor: "#636363", + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.3, + shadowRadius: 4, + elevation: 5, + }, + modalText: { + fontSize: 16, + color: Colors.text, + marginBottom: 8, + textAlign: "center", + flexWrap: "wrap", + }, + linkText: { + color: "#0252c2", + textAlign: "center", + }, + modalButton: { + backgroundColor: "white", + paddingVertical: 8, + paddingHorizontal: 16, + borderRadius: 50, + flex: 1, + marginHorizontal: 5, + }, + modalButtonText: { + color: "black", + fontSize: 15, + fontWeight: "bold", + textAlign: "center", + }, +}); diff --git a/package-lock.json b/package-lock.json index af3f6c4..edc35a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "color": "^4.2.3", "event-target-polyfill": "^0.0.4", "expo": "~52.0.26", + "expo-application": "~6.0.2", "expo-av": "~15.0.2", "expo-constants": "~17.0.3", "expo-dev-client": "~5.0.10", @@ -8872,6 +8873,14 @@ } } }, + "node_modules/expo-application": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/expo-application/-/expo-application-6.0.2.tgz", + "integrity": "sha512-qcj6kGq3mc7x5yIb5KxESurFTJCoEKwNEL34RdPEvTB/xhl7SeVZlu05sZBqxB1V4Ryzq/LsCb7NHNfBbb3L7A==", + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-asset": { "version": "11.0.2", "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-11.0.2.tgz", diff --git a/package.json b/package.json index f6192eb..5d9fb71 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,8 @@ "text-encoding-polyfill": "^0.6.7", "ts-pattern": "^5.5.0", "web-streams-polyfill": "^3.3.2", - "youtubei.js": "^12.2.0" + "youtubei.js": "^12.2.0", + "expo-application": "~6.0.2" }, "devDependencies": { "@babel/core": "^7.26.0", @@ -90,4 +91,4 @@ } }, "private": true -} \ No newline at end of file +}