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;