diff --git a/android/app/build.gradle b/android/app/build.gradle
index 1f0f98bb..defe2d78 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -87,8 +87,8 @@ android {
applicationId 'com.thoulee.jointplayer'
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
- versionCode 5010004
- versionName "5.10.4"
+ versionCode 5011000
+ versionName "5.11.0"
}
signingConfigs {
debug {
diff --git a/ios/jointplayer.xcodeproj/project.pbxproj b/ios/jointplayer.xcodeproj/project.pbxproj
index 16e41861..f31004ee 100644
--- a/ios/jointplayer.xcodeproj/project.pbxproj
+++ b/ios/jointplayer.xcodeproj/project.pbxproj
@@ -480,7 +480,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
- CURRENT_PROJECT_VERSION = 5010004;
+ CURRENT_PROJECT_VERSION = 5011000;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = jointplayer/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
@@ -507,7 +507,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
- CURRENT_PROJECT_VERSION = 5010004;
+ CURRENT_PROJECT_VERSION = 5011000;
INFOPLIST_FILE = jointplayer/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
diff --git a/ios/jointplayer/Info.plist b/ios/jointplayer/Info.plist
index 951f5de4..b2bebc25 100644
--- a/ios/jointplayer/Info.plist
+++ b/ios/jointplayer/Info.plist
@@ -17,11 +17,11 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 5.10.4
+ 5.11.0
CFBundleSignature
????
CFBundleVersion
- 5010004
+ 5011000
LSRequiresIPhoneOS
NSAppTransportSecurity
diff --git a/ios/jointplayerTests/Info.plist b/ios/jointplayerTests/Info.plist
index 4def5b59..c77e8ba7 100644
--- a/ios/jointplayerTests/Info.plist
+++ b/ios/jointplayerTests/Info.plist
@@ -15,10 +15,10 @@
CFBundlePackageType
BNDL
CFBundleShortVersionString
- 5.10.4
+ 5.11.0
CFBundleSignature
????
CFBundleVersion
- 5010004
+ 5011000
diff --git a/package.json b/package.json
index 86e7ecfd..dd251ec1 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "jointplayer",
- "version": "5.10.4",
+ "version": "5.11.0",
"private": true,
"homepage": "https://github.com/thoulee21/joint-player",
"displayName": "Joint Player",
diff --git a/src/components/UpdateChecker.tsx b/src/components/UpdateChecker.tsx
index 3c6ace2f..b3ee2ebc 100644
--- a/src/components/UpdateChecker.tsx
+++ b/src/components/UpdateChecker.tsx
@@ -1,128 +1,150 @@
import * as Updates from 'expo-updates';
import React, { useCallback } from 'react';
-import { Alert, ToastAndroid } from 'react-native';
+import { Alert, Linking, ToastAndroid } from 'react-native';
import HapticFeedback, { HapticFeedbackTypes } from 'react-native-haptic-feedback';
import { ActivityIndicator, List, useTheme } from 'react-native-paper';
import RNRestart from 'react-native-restart';
+import useSWR from 'swr';
+import packageData from '../../package.json';
import { useAppSelector } from '../hook';
import { selectDevModeEnabled } from '../redux/slices';
+import type { Main } from '../types/latestRelease';
export const UpdateChecker = () => {
- const appTheme = useTheme();
- const isDev = useAppSelector(selectDevModeEnabled);
+ const appTheme = useTheme();
+ const isDev = useAppSelector(selectDevModeEnabled);
- const {
- currentlyRunning,
- isUpdatePending,
- isChecking,
- isDownloading,
- lastCheckForUpdateTimeSinceRestart: lastCheck,
- availableUpdate,
- } = Updates.useUpdates();
+ const userRepo = packageData.homepage.split('/').slice(-2).join('/');
+ const { data } = useSWR(`https://api.github.com/repos/${userRepo}/releases/latest`);
+ const latestRelease = data?.tag_name;
+ const isLatest = latestRelease === packageData.version;
- const showCurrent = useCallback(() => {
- if (isDev) {
- HapticFeedback.trigger(HapticFeedbackTypes.effectClick);
- if (availableUpdate) {
- Alert.alert(
- 'Available update info',
- JSON.stringify(availableUpdate, null, 2)
- );
- } else {
- Alert.alert(
- 'Current update info',
- JSON.stringify(currentlyRunning, null, 2)
- );
- }
- }
- }, [availableUpdate, currentlyRunning, isDev]);
-
- const fetchUpdateAndRestart = async () => {
- try {
- await Updates.fetchUpdateAsync();
- RNRestart.Restart();
- } catch (err) {
- ToastAndroid.show(
- `Error updating app: ${JSON.stringify(err)}`,
- ToastAndroid.LONG
- );
- }
- };
+ const {
+ currentlyRunning,
+ isUpdatePending,
+ isChecking,
+ isDownloading,
+ lastCheckForUpdateTimeSinceRestart: lastCheck,
+ availableUpdate,
+ } = Updates.useUpdates();
- const performUpdateAlert = useCallback(() => {
+ const showCurrent = useCallback(() => {
+ if (isDev) {
+ HapticFeedback.trigger(HapticFeedbackTypes.effectClick);
+ if (availableUpdate) {
Alert.alert(
- availableUpdate?.createdAt
- ? `New update available:\n${availableUpdate?.createdAt.toLocaleString()}`
- : 'New update available',
- 'Do you want to update now?',
- [
- { text: 'Cancel' },
- {
- text: 'OK',
- onPress: isUpdatePending
- ? () => RNRestart.Restart()
- : fetchUpdateAndRestart,
- },
- ]
+ 'Available update info',
+ JSON.stringify(availableUpdate, null, 2)
);
- }, [availableUpdate?.createdAt, isUpdatePending]);
+ } else {
+ Alert.alert(
+ 'Current update info',
+ JSON.stringify(currentlyRunning, null, 2)
+ );
+ }
+ }
+ }, [availableUpdate, currentlyRunning, isDev]);
- const checkForUpdate = async () => {
- try {
- const updateCheckRes = await Updates.checkForUpdateAsync();
+ const fetchUpdateAndRestart = async () => {
+ try {
+ await Updates.fetchUpdateAsync();
+ RNRestart.Restart();
+ } catch (err) {
+ ToastAndroid.show(
+ `Error updating app: ${JSON.stringify(err)}`,
+ ToastAndroid.LONG
+ );
+ }
+ };
- if (updateCheckRes.isAvailable) {
- performUpdateAlert();
- } else {
- ToastAndroid.show('No updates available', ToastAndroid.SHORT);
- }
- } catch (err) {
- Alert.alert(
- 'Error checking for updates',
- JSON.stringify(err, null, 2)
- );
- }
- };
+ const performUpdateAlert = useCallback(() => {
+ Alert.alert(
+ availableUpdate?.createdAt
+ ? `New update available:\n${availableUpdate?.createdAt.toLocaleString()}`
+ : 'New update available',
+ 'Do you want to update now?',
+ [
+ { text: 'Cancel' },
+ {
+ text: 'OK',
+ onPress: isUpdatePending
+ ? () => RNRestart.Restart()
+ : fetchUpdateAndRestart,
+ },
+ ]
+ );
+ }, [availableUpdate?.createdAt, isUpdatePending]);
- const handleUpdatePress = isUpdatePending ? performUpdateAlert : checkForUpdate;
- const description = isDownloading
- ? 'Downloading update...'
- : isUpdatePending
- ? 'Update is pending...'
- : isChecking
- ? 'Checking for updates...'
- : `Last checked: ${lastCheck?.toLocaleString() || 'Never'}`;
+ const checkForUpdate = async () => {
+ try {
+ const updateCheckRes = await Updates.checkForUpdateAsync();
- const renderUpdateIcon = useCallback((props: any) => {
- const updateIcon = isUpdatePending
- ? 'progress-download'
- : 'cloud-download-outline';
+ if (updateCheckRes.isAvailable) {
+ performUpdateAlert();
+ } else {
+ ToastAndroid.show('No updates available', ToastAndroid.SHORT);
+ }
+ } catch (err) {
+ Alert.alert(
+ 'Error checking for updates',
+ JSON.stringify(err, null, 2)
+ );
+ }
+ };
- return (
-
- );
- }, [appTheme.colors.primary, isUpdatePending]);
+ const handleUpdatePress = isLatest ?
+ isUpdatePending ? performUpdateAlert : checkForUpdate
+ : () => Alert.alert(
+ 'New version available',
+ `Your version: ${packageData.version}\nLatest version: ${latestRelease}`,
+ [
+ { text: 'Cancel' },
+ {
+ text: 'OK', onPress: () => {
+ Linking.openURL(data?.assets_url || 'https://github.com');
+ }
+ }
+ ]
+ );
- const isProcessing = isChecking || isDownloading;
- const renderActivityIndicator = useCallback((props: any) => (
- isProcessing ? : null
- ), [isProcessing]);
+ const description = isDownloading
+ ? 'Downloading update...'
+ : isUpdatePending
+ ? 'Update is pending...'
+ : isChecking
+ ? 'Checking for updates...'
+ : `Last checked: ${lastCheck?.toLocaleString() || 'Never'}`;
+
+ const renderUpdateIcon = useCallback((props: any) => {
+ const updateIcon = isUpdatePending
+ ? 'progress-download'
+ : 'cloud-download-outline';
return (
-
+
);
+ }, [appTheme.colors.primary, isUpdatePending]);
+
+ const isProcessing = isChecking || isDownloading;
+ const renderActivityIndicator = useCallback((props: any) => (
+ isProcessing ? : null
+ ), [isProcessing]);
+
+ return (
+
+ );
};
diff --git a/src/types/latestRelease.d.ts b/src/types/latestRelease.d.ts
new file mode 100644
index 00000000..855d1826
--- /dev/null
+++ b/src/types/latestRelease.d.ts
@@ -0,0 +1,58 @@
+export interface Main {
+ url: string;
+ assets_url: string;
+ upload_url: string;
+ html_url: string;
+ id: number;
+ author: Author;
+ node_id: string;
+ tag_name: string;
+ target_commitish: string;
+ name: string;
+ draft: boolean;
+ prerelease: boolean;
+ created_at: Date;
+ published_at: Date;
+ assets: Asset[];
+ tarball_url: string;
+ zipball_url: string;
+ body: string;
+}
+
+export interface Asset {
+ url: string;
+ id: number;
+ node_id: string;
+ name: string;
+ label: string;
+ uploader: Author;
+ content_type: string;
+ state: string;
+ size: number;
+ download_count: number;
+ created_at: Date;
+ updated_at: Date;
+ browser_download_url: string;
+}
+
+export interface Author {
+ login: string;
+ id: number;
+ node_id: string;
+ avatar_url: string;
+ gravatar_id: string;
+ url: string;
+ html_url: string;
+ followers_url: string;
+ following_url: string;
+ gists_url: string;
+ starred_url: string;
+ subscriptions_url: string;
+ organizations_url: string;
+ repos_url: string;
+ events_url: string;
+ received_events_url: string;
+ type: string;
+ user_view_type: string;
+ site_admin: boolean;
+}