From 9ecc3260088ab5737a72a3dda043ecf6e4208122 Mon Sep 17 00:00:00 2001 From: Nikolaos Karaolidis Date: Sun, 9 Jul 2023 18:13:05 +0300 Subject: [PATCH] Update Storage URI settings Signed-off-by: Nikolaos Karaolidis --- android/app/build.gradle | 2 - .../com/terminallyonline/MainActivity.java | 2 +- android/app/src/main/res/values/strings.xml | 2 +- android/settings.gradle | 5 +- app.json | 2 +- package-lock.json | 61 +++++++------- package.json | 3 +- src/constants.ts | 8 ++ src/contexts/settings.tsx | 82 +++++++++++------- src/screens/home.tsx | 4 +- src/screens/settings.tsx | 84 ++----------------- src/screens/welcome.tsx | 22 +++++ src/styles.tsx | 3 + src/types/settings.ts | 2 - 14 files changed, 125 insertions(+), 157 deletions(-) create mode 100644 src/constants.ts create mode 100644 src/screens/welcome.tsx diff --git a/android/app/build.gradle b/android/app/build.gradle index 297b0a8..8a275b8 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -118,8 +118,6 @@ dependencies { } else { implementation jscFlavor } - - implementation project(':react-native-fs') } apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) diff --git a/android/app/src/main/java/com/terminallyonline/MainActivity.java b/android/app/src/main/java/com/terminallyonline/MainActivity.java index 65fc30c..2a8aedd 100644 --- a/android/app/src/main/java/com/terminallyonline/MainActivity.java +++ b/android/app/src/main/java/com/terminallyonline/MainActivity.java @@ -13,7 +13,7 @@ public class MainActivity extends ReactActivity { */ @Override protected String getMainComponentName() { - return "TerminallyOnline"; + return "Terminally Online"; } /** diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 930bda3..812b5af 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -1,3 +1,3 @@ - TerminallyOnline + Terminally Online diff --git a/android/settings.gradle b/android/settings.gradle index 3704eb3..2f6bef2 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,7 +1,4 @@ -rootProject.name = 'TerminallyOnline' +rootProject.name = 'Terminally Online' apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) include ':app' includeBuild('../node_modules/@react-native/gradle-plugin') - -include ':react-native-fs' -project(':react-native-fs').projectDir = new File(settingsDir, '../node_modules/react-native-fs/android') diff --git a/app.json b/app.json index 2c07701..62abf52 100644 --- a/app.json +++ b/app.json @@ -1,4 +1,4 @@ { - "name": "TerminallyOnline", + "name": "Terminally Online", "displayName": "Terminally Online" } diff --git a/package-lock.json b/package-lock.json index bdcf05e..169e4e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,8 @@ "@realm/react": "^0.5.1", "react": "18.2.0", "react-native": "0.72.1", - "react-native-fs": "^2.20.0", + "react-native-document-picker": "^9.0.1", + "react-native-file-access": "^3.0.4", "react-native-gesture-handler": "^2.12.0", "react-native-paper": "^5.9.1", "react-native-reanimated": "2.2.4", @@ -5320,11 +5321,6 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "node_modules/base-64": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", - "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==" - }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -12759,15 +12755,15 @@ "react": "18.2.0" } }, - "node_modules/react-native-fs": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/react-native-fs/-/react-native-fs-2.20.0.tgz", - "integrity": "sha512-VkTBzs7fIDUiy/XajOSNk0XazFE9l+QlMAce7lGuebZcag5CnjszB+u4BdqzwaQOdcYb5wsJIsqq4kxInIRpJQ==", + "node_modules/react-native-document-picker": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/react-native-document-picker/-/react-native-document-picker-9.0.1.tgz", + "integrity": "sha512-l2c2xChwsdjzZIV9QJc85buC3vXkM5ZuY4943yMDj3TiszJp1spmHNaRMZKYIh3yVwdD2jENm0DBU5AWa+jhLg==", "dependencies": { - "base-64": "^0.1.0", - "utf8": "^3.0.0" + "invariant": "^2.2.4" }, "peerDependencies": { + "react": "*", "react-native": "*", "react-native-windows": "*" }, @@ -12777,6 +12773,15 @@ } } }, + "node_modules/react-native-file-access": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-native-file-access/-/react-native-file-access-3.0.4.tgz", + "integrity": "sha512-sDmGeIIiSlLSrdtvEtADQ1uMoF+Zoegvu/Wr0z0Ej9lU8tHX0C1q+fJnCEBJSUjQ31rSzlabjWgQDKbpOEBdWg==", + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "node_modules/react-native-gesture-handler": { "version": "2.12.0", "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.12.0.tgz", @@ -14731,11 +14736,6 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/utf8": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", - "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -18881,11 +18881,6 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "base-64": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", - "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==" - }, "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -24444,15 +24439,20 @@ } } }, - "react-native-fs": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/react-native-fs/-/react-native-fs-2.20.0.tgz", - "integrity": "sha512-VkTBzs7fIDUiy/XajOSNk0XazFE9l+QlMAce7lGuebZcag5CnjszB+u4BdqzwaQOdcYb5wsJIsqq4kxInIRpJQ==", + "react-native-document-picker": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/react-native-document-picker/-/react-native-document-picker-9.0.1.tgz", + "integrity": "sha512-l2c2xChwsdjzZIV9QJc85buC3vXkM5ZuY4943yMDj3TiszJp1spmHNaRMZKYIh3yVwdD2jENm0DBU5AWa+jhLg==", "requires": { - "base-64": "^0.1.0", - "utf8": "^3.0.0" + "invariant": "^2.2.4" } }, + "react-native-file-access": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-native-file-access/-/react-native-file-access-3.0.4.tgz", + "integrity": "sha512-sDmGeIIiSlLSrdtvEtADQ1uMoF+Zoegvu/Wr0z0Ej9lU8tHX0C1q+fJnCEBJSUjQ31rSzlabjWgQDKbpOEBdWg==", + "requires": {} + }, "react-native-gesture-handler": { "version": "2.12.0", "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.12.0.tgz", @@ -25852,11 +25852,6 @@ "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", "requires": {} }, - "utf8": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", - "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index 91b6400..42a9f42 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "@realm/react": "^0.5.1", "react": "18.2.0", "react-native": "0.72.1", - "react-native-fs": "^2.20.0", + "react-native-document-picker": "^9.0.1", + "react-native-file-access": "^3.0.4", "react-native-gesture-handler": "^2.12.0", "react-native-paper": "^5.9.1", "react-native-reanimated": "2.2.4", diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..551247d --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,8 @@ +const packageName = 'com.terminallyonline'; +const appName = 'Terminally Online'; +const escapedAppName = appName.replaceAll(' ', '%20'); + +// eslint-disable-next-line @typescript-eslint/no-empty-function +const noOp = () => {}; + +export { packageName, appName, escapedAppName, noOp }; diff --git a/src/contexts/settings.tsx b/src/contexts/settings.tsx index 13c3be1..8f740a5 100644 --- a/src/contexts/settings.tsx +++ b/src/contexts/settings.tsx @@ -1,8 +1,14 @@ import React, { createContext, useContext, useState, useEffect } from 'react'; import { Settings } from '../types'; import AsyncStorage from '@react-native-async-storage/async-storage'; -import { DocumentDirectoryPath } from 'react-native-fs'; import { LoadingView } from '../components'; +import WelcomeScreen from '../screens/welcome'; +import { FileSystem } from 'react-native-file-access'; +import { + getPersistedUriPermissions, + openDocumentTree, + releasePersistableUriPermission, +} from 'react-native-scoped-storage'; interface SettingsContextType { settings: Settings; @@ -17,28 +23,44 @@ const SettingsProvider: React.FC<{ children: React.ReactNode }> = ({ children, }) => { const [settings, setSettings] = useState({ - useInternalStorage: true, storageUri: '', - addNoMedia: false, }); const [hasLoaded, setHasLoaded] = useState(false); + const updateSettings = (newSettings: Partial) => { + const updatedSettings = { ...settings, ...newSettings }; + + if (settings.storageUri !== updatedSettings.storageUri) { + void getPersistedUriPermissions().then(permissions => { + if (permissions.includes(settings.storageUri)) { + void releasePersistableUriPermission(settings.storageUri); + } + }); + void AsyncStorage.setItem('storageUri', updatedSettings.storageUri); + } + + setSettings(updatedSettings); + }; + useEffect(() => { const loadSettings = async () => { - const useInternalStorageValue = await AsyncStorage.getItem( - 'useInternalStorage', - ); const storageUriValue = await AsyncStorage.getItem('storageUri'); - const addNoMediaValue = await AsyncStorage.getItem('addNoMedia'); + let storageUri = storageUriValue ?? ''; + + if (storageUri !== '') { + const permissions = await getPersistedUriPermissions(); + if (!permissions.includes(storageUri)) { + storageUri = ''; + } + + const exists = await FileSystem.exists(storageUri); + if (!exists) { + storageUri = ''; + } + } setSettings({ - useInternalStorage: useInternalStorageValue - ? (JSON.parse(useInternalStorageValue) as boolean) - : true, - storageUri: storageUriValue ?? DocumentDirectoryPath, - addNoMedia: addNoMediaValue - ? (JSON.parse(addNoMediaValue) as boolean) - : false, + storageUri, }); setHasLoaded(true); @@ -47,25 +69,23 @@ const SettingsProvider: React.FC<{ children: React.ReactNode }> = ({ void loadSettings(); }, []); - const updateSettings = (newSettings: Partial) => { - const updatedSettings = { ...settings, ...newSettings }; - - void AsyncStorage.setItem( - 'useInternalStorage', - JSON.stringify(updatedSettings.useInternalStorage), - ); - void AsyncStorage.setItem('storageUri', updatedSettings.storageUri); - void AsyncStorage.setItem( - 'addNoMedia', - JSON.stringify(updatedSettings.addNoMedia), - ); - - setSettings(updatedSettings); - }; - return ( - {hasLoaded ? children : } + {hasLoaded ? ( + settings.storageUri === '' ? ( + { + void openDocumentTree().then(uri => { + updateSettings({ storageUri: uri.uri }); + }); + }} + /> + ) : ( + children + ) + ) : ( + + )} ); }; diff --git a/src/screens/home.tsx b/src/screens/home.tsx index ffd26b8..7ff95cf 100644 --- a/src/screens/home.tsx +++ b/src/screens/home.tsx @@ -4,7 +4,7 @@ import { Text } from 'react-native-paper'; import { PaddedView } from '../components'; import { useSettings } from '../contexts'; -function Home(): JSX.Element { +const Home = () => { const { settings } = useSettings(); return ( @@ -13,6 +13,6 @@ function Home(): JSX.Element { Settings: {JSON.stringify(settings)} ); -} +}; export default Home; diff --git a/src/screens/settings.tsx b/src/screens/settings.tsx index 8cc1171..0ce3136 100644 --- a/src/screens/settings.tsx +++ b/src/screens/settings.tsx @@ -1,16 +1,8 @@ import React, { useState } from 'react'; import { View } from 'react-native'; -import { - Button, - Switch, - SegmentedButtons, - Text, - List, - Snackbar, -} from 'react-native-paper'; +import { Button, List, Snackbar } from 'react-native-paper'; import { useRealm } from '@realm/react'; import { openDocumentTree } from 'react-native-scoped-storage'; -import { DocumentDirectoryPath } from 'react-native-fs'; import { PaddedView } from '../components'; import styles from '../styles'; import { Meme } from '../database'; @@ -22,48 +14,7 @@ const SettingsScreen = () => { const [snackbarVisible, setSnackbarVisible] = useState(false); const [snackbarMessage, setSnackbarMessage] = useState(''); - const { settings, setSettings } = useSettings(); - - const setUseInternalStorage = (use: boolean) => { - if (settings.useInternalStorage === use) { - return; - } - - if (use) { - setSettings({ - useInternalStorage: use, - storageUri: DocumentDirectoryPath, - }); - } else { - openDocumentTree(true) - .then(uri => { - setSettings({ - useInternalStorage: use, - storageUri: uri.uri, - }); - }) - .catch(() => { - setSnackbarMessage('Failed to select storage path!'); - setSnackbarVisible(true); - }); - } - }; - - const setStorageUri = (uri: string) => { - if (settings.storageUri === uri) { - return; - } - - setSettings({ storageUri: uri }); - }; - - const setAddNoMedia = (add: boolean) => { - if (settings.addNoMedia === add) { - return; - } - - setSettings({ addNoMedia: add }); - }; + const { setSettings } = useSettings(); const realm = useRealm(); @@ -105,41 +56,16 @@ const SettingsScreen = () => { Media Storage - - setUseInternalStorage(value === 'interneal') - } - /> - - Hide media from Gallery - - diff --git a/src/screens/welcome.tsx b/src/screens/welcome.tsx new file mode 100644 index 0000000..6c8de9a --- /dev/null +++ b/src/screens/welcome.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { Button, Text } from 'react-native-paper'; +import { PaddedView } from '../components'; +import styles from '../styles'; + +const WelcomeScreen = (properties: { selectStorageLocation: () => void }) => { + return ( + + + Welcome to Terminally Online! + + + + + ); +}; + +export default WelcomeScreen; diff --git a/src/styles.tsx b/src/styles.tsx index 84c0210..e89ee45 100644 --- a/src/styles.tsx +++ b/src/styles.tsx @@ -9,6 +9,9 @@ const styles = StyleSheet.create({ justifyContent: 'space-between', alignItems: 'center', }, + centerText: { + textAlign: 'center', + }, }); export default styles; diff --git a/src/types/settings.ts b/src/types/settings.ts index 58adf23..57ec3e1 100644 --- a/src/types/settings.ts +++ b/src/types/settings.ts @@ -1,7 +1,5 @@ interface Settings { - useInternalStorage: boolean; storageUri: string; - addNoMedia: boolean; } export default Settings;