diff --git a/android/app/build.gradle b/android/app/build.gradle
index c55ab66c..d17932ea 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -79,12 +79,12 @@ import com.android.build.OutputFile
project.ext.react = [
enableHermes: (findProperty('expo.jsEngine') ?: "jsc") == "hermes",
- cliPath: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/cli.js",
- hermesCommand: new File(["node", "--print", "require.resolve('hermes-engine/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/%OS-BIN%/hermesc",
- composeSourceMapsPath: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/scripts/compose-source-maps.js",
+ cliPath: "../../node_modules/react-native/cli.js",
+ hermesCommand: "../../node_modules/hermes-engine/%OS-BIN%/hermesc",
+ composeSourceMapsPath: "../../node_modules/react-native/scripts/compose-source-maps.js",
]
-apply from: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), "../react.gradle")
+apply from: "../../node_modules/react-native/react.gradle"
/**
* Set this to true to create two separate APKs instead of one:
@@ -135,8 +135,8 @@ android {
applicationId "io.filen.app"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
- versionCode 218
- versionName "2.0.18"
+ versionCode 220
+ versionName "2.0.20"
}
splits {
abi {
@@ -237,8 +237,8 @@ dependencies {
}
if (enableHermes) {
- debugImplementation files(new File(["node", "--print", "require.resolve('hermes-engine/package.json')"].execute(null, rootDir).text.trim(), "../android/hermes-debug.aar"))
- releaseImplementation files(new File(["node", "--print", "require.resolve('hermes-engine/package.json')"].execute(null, rootDir).text.trim(), "../android/hermes-release.aar"))
+ debugImplementation files("../../node_modules/hermes-engine/android/hermes-debug.aar")
+ releaseImplementation files("../../node_modules/hermes-engine/android/hermes-release.aar")
} else {
implementation jscFlavor
}
@@ -253,7 +253,7 @@ task copyDownloadableDepsToLibs(type: Copy) {
into 'libs'
}
-apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json')"].execute(null, rootDir).text.trim(), "../native_modules.gradle");
+apply from: "../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"
applyNativeModulesAppBuildGradle(project)
//apply plugin: 'com.google.gms.google-services'
\ No newline at end of file
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 3d6c86e6..34740615 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -10,6 +10,8 @@
+
+
com.apple.developer.icloud-container-identifiers
-
+
+ iCloud.io.filen.app
+
com.apple.developer.icloud-services
CloudDocuments
com.apple.developer.ubiquity-container-identifiers
-
+
+ iCloud.io.filen.app
+
com.apple.security.application-groups
group.io.filen.app
diff --git a/ios/Filen/Info.plist b/ios/Filen/Info.plist
index b6d77238..8813769c 100644
--- a/ios/Filen/Info.plist
+++ b/ios/Filen/Info.plist
@@ -110,5 +110,11 @@
UIViewControllerBasedStatusBarAppearance
+ BGTaskSchedulerPermittedIdentifiers
+
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ background-fetch
+ expo-background-fetch
+
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index d24537f8..a1fbc2b5 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -469,6 +469,9 @@ PODS:
- RNScreens (3.10.2):
- React-Core
- React-RCTImage
+ - RNSentry (4.7.1):
+ - React-Core
+ - Sentry (= 7.28.0)
- RNShareMenu (6.0.0):
- React
- RNSVG (12.1.1):
@@ -479,6 +482,9 @@ PODS:
- SDWebImageWebPCoder (0.8.4):
- libwebp (~> 1.0)
- SDWebImage/Core (~> 5.10)
+ - Sentry (7.28.0):
+ - Sentry/Core (= 7.28.0)
+ - Sentry/Core (7.28.0)
- UMAppLoader (3.0.1)
- UMTaskManagerInterface (7.1.1):
- ExpoModulesCore
@@ -583,6 +589,7 @@ DEPENDENCIES:
- RNPermissions (from `../node_modules/react-native-permissions`)
- RNReanimated (from `../node_modules/react-native-reanimated`)
- RNScreens (from `../node_modules/react-native-screens`)
+ - "RNSentry (from `../node_modules/@sentry/react-native`)"
- RNShareMenu (from `../node_modules/react-native-share-menu`)
- RNSVG (from `../node_modules/react-native-svg`)
- UMAppLoader (from `../node_modules/unimodules-app-loader/ios`)
@@ -609,6 +616,7 @@ SPEC REPOS:
- OpenSSL-Universal
- SDWebImage
- SDWebImageWebPCoder
+ - Sentry
- YogaKit
EXTERNAL SOURCES:
@@ -756,6 +764,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-reanimated"
RNScreens:
:path: "../node_modules/react-native-screens"
+ RNSentry:
+ :path: "../node_modules/@sentry/react-native"
RNShareMenu:
:path: "../node_modules/react-native-share-menu"
RNSVG:
@@ -856,10 +866,12 @@ SPEC CHECKSUMS:
RNPermissions: bcd846e8f5a7f39e921cc7ca7172e2de0e698b6f
RNReanimated: 46cdb89ca59ab7181334f4ed05a70e82ddb36751
RNScreens: d6da2b9e29cf523832c2542f47bf1287318b1868
+ RNSentry: 694aecc3d8240e4935374974a6636e360ae06394
RNShareMenu: cb9dac548c8bf147d06f0bf07296ad51ea9f5fc3
RNSVG: 551acb6562324b1d52a4e0758f7ca0ec234e278f
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
SDWebImageWebPCoder: f93010f3f6c031e2f8fb3081ca4ee6966c539815
+ Sentry: 2c6053e4cfe6dea6608135dea1928ffbb4ecfba5
UMAppLoader: 2ee9f1278d315672f4854c6a2310aa9988f61f72
UMTaskManagerInterface: 3184c93ecc290bd422c6e344badc89b601e9c29b
Yoga: 5cbf25add73edb290e1067017690f7ebf56c5468
diff --git a/ios/sentry.properties b/ios/sentry.properties
new file mode 100644
index 00000000..ee9840e8
--- /dev/null
+++ b/ios/sentry.properties
@@ -0,0 +1,4 @@
+defaults.url=https://sentry.io/
+defaults.org=filen-cloud-dienste-ug
+defaults.project=react-native
+auth.token=237c8075f3c441fd8d1f48d0e0dd402c3a5960b9f0f0444eb0732171529a00ac
diff --git a/package.json b/package.json
index d0f90971..66f07fe1 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "filen-mobile",
- "version": "2.0.18",
+ "version": "2.0.20",
"private": true,
"scripts": {
"android": "react-native run-android",
@@ -8,7 +8,7 @@
"start": "react-native start",
"test": "jest",
"lint": "eslint .",
- "dist-android": "./android/gradlew bundleRelease && ./android/gradlew assembleRelease",
+ "dist-android": "cd ./android && ./gradlew bundleRelease && ./gradlew assembleRelease && cd ..",
"splash": "react-native generate-bootsplash bootsplash.png --background-color=#263179 --logo-width=100 --flavor=main --assets-path=src/assets",
"icons": "react-native-svg-app-icon",
"install-release-apk": "adb install android/app/build/outputs/apk/release/app-release.apk"
@@ -23,6 +23,7 @@
"@react-navigation/bottom-tabs": "^6.2.0",
"@react-navigation/native": "^6.0.8",
"@react-navigation/native-stack": "^6.4.0",
+ "@sentry/react-native": "^4.7.1",
"axios": "^0.27.1",
"crypto-js": "^4.1.1",
"expo": "^44.0.6",
diff --git a/src/App.tsx b/src/App.tsx
index 49fda414..7d2fcdb7 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,7 +1,7 @@
import "./lib/globals"
import "./lib/node"
import React, { useState, useEffect, Fragment, memo } from "react"
-import { Dimensions, View, Platform, DeviceEventEmitter, LogBox, Appearance, AppState, Alert } from "react-native"
+import { Dimensions, View, Platform, DeviceEventEmitter, LogBox, Appearance, AppState } from "react-native"
import { setup } from "./lib/services/setup/setup"
import storage from "./lib/storage"
import { useMMKVBoolean, useMMKVString, useMMKVNumber } from "react-native-mmkv"
@@ -44,35 +44,15 @@ import { TextEditorScreen } from "./screens/TextEditorScreen/TextEditorScreen"
import { checkAppVersion } from "./lib/services/versionCheck"
import { UpdateScreen } from "./screens/UpdateScreen/UpdateScreen"
import BackgroundTimer from "react-native-background-timer"
-import { setJSExceptionHandler, setNativeExceptionHandler } from "react-native-exception-handler"
-import { reportError } from "./lib/api"
import ImageViewerScreen from "./screens/ImageViewerScreen/ImageViewerScreen"
import { CameraUploadAlbumsScreen } from "./screens/CameraUploadAlbumsScreen/CameraUploadAlbumsScreen"
import { isRouteInStack } from "./lib/helpers"
+import * as Sentry from "@sentry/react-native"
-setJSExceptionHandler((err) => {
- reportError(err.toString())
-
- Alert.alert("Unexpected error occured",
- `
- Error: ${err.name} ${err.message}
-
- The error has been automatically reported to us. Please restart the app if it does not continue to work!
- `,
- [
- {
- text: "Close",
- onPress: () => {
- return false
- }
- }
- ]
- )
-}, true)
-
-setNativeExceptionHandler((err) => {
- reportError(err)
-}, false)
+Sentry.init({
+ dsn: "https://1aa0cbb262634a27a5887e91381e4251@o4504039703314432.ingest.sentry.io/4504039705804800",
+ tracesSampleRate: 1.0
+})
NetInfo.configure({
reachabilityUrl: "https://api.filen.io",
@@ -103,7 +83,7 @@ DeviceEventEmitter.addListener("event", (data) => {
storage.set("cameraUploadUploaded", 0)
storage.set("cameraUploadTotal", 0)
-export const App = memo(() => {
+export const App = Sentry.wrap(memo(() => {
const [isLoggedIn, setIsLoggedIn] = useMMKVBoolean("isLoggedIn", storage)
const setDimensions = useStore(state => state.setDimensions)
const [darkMode, setDarkMode] = useMMKVBoolean("darkMode", storage)
@@ -641,4 +621,4 @@ export const App = memo(() => {
/>
>
)
-})
\ No newline at end of file
+}))
\ No newline at end of file
diff --git a/src/components/ActionSheets/ActionSheets.tsx b/src/components/ActionSheets/ActionSheets.tsx
index b2cff6a5..69b07c12 100644
--- a/src/components/ActionSheets/ActionSheets.tsx
+++ b/src/components/ActionSheets/ActionSheets.tsx
@@ -1489,7 +1489,7 @@ export const ItemActionSheetItemHeader = memo(() => {
}
{hideSizes ? formatBytes(0) : formatBytes(currentActionSheetItem.type == "file" ? currentActionSheetItem.size : storage.getNumber("folderSizeCache:" + currentActionSheetItem.uuid))}
{
- typeof currentActionSheetItem.sharerEmail == "string" && currentActionSheetItem.sharerEmail.length > 0 && (
+ typeof currentActionSheetItem.sharerEmail == "string" && currentActionSheetItem.sharerEmail.length > 0 && getParent().length < 32 && (
<>
•
{currentActionSheetItem.sharerEmail}
@@ -1497,10 +1497,15 @@ export const ItemActionSheetItemHeader = memo(() => {
)
}
{
- typeof currentActionSheetItem.receiverEmail == "string" && currentActionSheetItem.receiverEmail.length > 0 && (
+ typeof currentActionSheetItem.receivers !== "undefined" && Array.isArray(currentActionSheetItem.receivers) && currentActionSheetItem.receivers.length > 0 && getParent().length < 32 && (
<>
•
- {currentActionSheetItem.receiverEmail}
+
+ {currentActionSheetItem.receivers.length}
>
)
}
@@ -2079,7 +2084,7 @@ export const ItemActionSheet = memo(({ navigation }: ItemActionSheetProps) => {
)
}
{
- isDeviceOnline && routeURL.indexOf("shared-in") !== -1 && (
+ isDeviceOnline && routeURL.indexOf("shared-in") !== -1 && getParent().length < 32 && (
{
await SheetManager.hide("ItemActionSheet")
@@ -2092,7 +2097,7 @@ export const ItemActionSheet = memo(({ navigation }: ItemActionSheetProps) => {
)
}
{
- isDeviceOnline && routeURL.indexOf("shared-out") !== -1 && (
+ isDeviceOnline && routeURL.indexOf("shared-out") !== -1 && getParent().length < 32 && (
{
await SheetManager.hide("ItemActionSheet")
diff --git a/src/components/Dialogs/Dialogs.tsx b/src/components/Dialogs/Dialogs.tsx
index f77add09..4c75c9c3 100644
--- a/src/components/Dialogs/Dialogs.tsx
+++ b/src/components/Dialogs/Dialogs.tsx
@@ -11,6 +11,7 @@ import { DeviceEventEmitter, Keyboard } from "react-native"
import { logout } from "../../lib/services/auth/logout"
import { navigationAnimation } from "../../lib/state"
import { StackActions, CommonActions } from "@react-navigation/native"
+import type { Item } from "../../lib/services/items"
export const RenameDialog = memo(() => {
const [darkMode, setDarkMode] = useMMKVBoolean("darkMode", storage)
@@ -495,12 +496,30 @@ export const ConfirmStopSharingDialog = memo(() => {
label={i18n(lang, "stopSharing")}
disabled={buttonsDisabled}
onPress={() => {
+ if(typeof currentActionSheetItem.receivers == "undefined" || !Array.isArray(currentActionSheetItem.receivers)){
+ return
+ }
+
setButtonsDisabled(true)
setStopSharingDialogVisible(false)
useStore.setState({ fullscreenLoadingModalVisible: true })
- stopSharingItem({ item: currentActionSheetItem }).then(async () => {
+ const promises = []
+
+ for(let i = 0; i < currentActionSheetItem.receivers.length; i++){
+ const item: Item = {
+ ...currentActionSheetItem,
+ receiverId: currentActionSheetItem.receivers[i].id,
+ receiverEmail: currentActionSheetItem.receivers[i].email
+ }
+
+ promises.push(new Promise((resolve, reject) => {
+ stopSharingItem({ item }).then(resolve).catch(reject)
+ }))
+ }
+
+ Promise.all(promises).then(() => {
DeviceEventEmitter.emit("event", {
type: "remove-item",
data: {
diff --git a/src/components/Item/Item.tsx b/src/components/Item/Item.tsx
index be94bb36..769638cd 100644
--- a/src/components/Item/Item.tsx
+++ b/src/components/Item/Item.tsx
@@ -3,7 +3,7 @@ import { Text, View, TouchableOpacity, TouchableHighlight, DeviceEventEmitter, D
import FastImage from "react-native-fast-image"
import Ionicon from "@expo/vector-icons/Ionicons"
import { getImageForItem } from "../../assets/thumbnails"
-import { formatBytes, getFolderColor, calcPhotosGridSize, getRouteURL } from "../../lib/helpers"
+import { formatBytes, getFolderColor, calcPhotosGridSize, getRouteURL, getParent } from "../../lib/helpers"
import { i18n } from "../../i18n"
import { getColor } from "../../lib/style/colors"
import RNFS from "react-native-fs"
@@ -52,7 +52,10 @@ export const ListItem = memo(({ item, index, darkMode, hideFileNames, hideSizes,
fetchFolderSize({ folder: item, routeURL: getRouteURL() }).then((fetchedSize) => {
storage.set("folderSizeCache:" + item.uuid, fetchedSize)
- storage.set("fetchFolderSizeTimeout:" + item.uuid, (new Date().getTime() + 60000))
+
+ if(fetchedSize > 0){
+ storage.set("fetchFolderSizeTimeout:" + item.uuid, (new Date().getTime() + 60000))
+ }
if(isMounted()){
setFolderSize(fetchedSize)
@@ -172,7 +175,8 @@ export const ListItem = memo(({ item, index, darkMode, hideFileNames, hideSizes,
typeof item.offline == "boolean" && item.offline && (
<>
•
@@ -193,7 +197,7 @@ export const ListItem = memo(({ item, index, darkMode, hideFileNames, hideSizes,
}
{hideSizes ? formatBytes(0) : formatBytes(item.type == "file" ? item.size : folderSize)}
{
- typeof item.sharerEmail == "string" && item.sharerEmail.length > 0 && (
+ typeof item.sharerEmail == "string" && item.sharerEmail.length > 0 && getParent().length < 32 && (
<>
•
{item.sharerEmail}
@@ -201,10 +205,15 @@ export const ListItem = memo(({ item, index, darkMode, hideFileNames, hideSizes,
)
}
{
- typeof item.receiverEmail == "string" && item.receiverEmail.length > 0 && (
+ typeof item.receivers !== "undefined" && Array.isArray(item.receivers) && item.receivers.length > 0 && getParent().length < 32 && (
<>
•
- {item.receiverEmail}
+
+ {item.receivers.length}
>
)
}
diff --git a/src/lib/services/items/items.ts b/src/lib/services/items/items.ts
index 6792f64a..1605e3f3 100644
--- a/src/lib/services/items/items.ts
+++ b/src/lib/services/items/items.ts
@@ -16,7 +16,12 @@ import { navigationAnimation } from "../../state"
import memoryCache from "../../memoryCache"
const isGeneratingThumbnailForItemUUID: any = {}
-const isCheckingThumbnailForItemUUID : any= {}
+const isCheckingThumbnailForItemUUID : any = {}
+
+export interface ItemReceiver {
+ id: number,
+ email: string
+}
export interface Item {
id: string,
@@ -48,7 +53,8 @@ export interface Item {
chunks: number,
thumbnail: string | undefined,
version: number,
- hash: string
+ hash: string,
+ receivers?: ItemReceiver[]
}
export const ItemTemplate: Item = {
@@ -601,6 +607,39 @@ export const loadItems = async ({ parent, prevItems, setItems, masterKeys, setLo
storage.set("itemCache:file:" + file.uuid, JSON.stringify(item))
}
+
+ const groups: Item[] = []
+ const sharedTo: { [key: string]: ItemReceiver[] } = {}
+ const added: { [key: string]: boolean } = {}
+
+ for(let i = 0; i < items.length; i++){
+ if(Array.isArray(sharedTo[items[i].uuid])){
+ sharedTo[items[i].uuid].push({
+ id: items[i].receiverId,
+ email: items[i].receiverEmail
+ })
+ }
+ else{
+ sharedTo[items[i].uuid] = [{
+ id: items[i].receiverId,
+ email: items[i].receiverEmail
+ }]
+ }
+ }
+
+ for(let i = 0; i < items.length; i++){
+ if(Array.isArray(sharedTo[items[i].uuid])){
+ items[i].receivers = sharedTo[items[i].uuid]
+ }
+
+ if(!added[items[i].uuid]){
+ added[items[i].uuid] = true
+
+ groups.push(items[i])
+ }
+ }
+
+ items = groups
}
else if(parent == "photos"){
try{
diff --git a/src/lib/services/setup/setup.ts b/src/lib/services/setup/setup.ts
index 7b7c1888..2c3025db 100644
--- a/src/lib/services/setup/setup.ts
+++ b/src/lib/services/setup/setup.ts
@@ -12,6 +12,10 @@ export const clearCacheDirectories = (): Promise => {
getDownloadPath({ type: "cachedDownloads" }).then((cachedDownloadsPath) => {
RNFS.readDir(RNFS.TemporaryDirectoryPath).then(async (items) => {
for(let i = 0; i < items.length; i++){
+ if(items[i].path.indexOf("SentryCrash") !== -1){
+ continue
+ }
+
try{
await RNFS.unlink(items[i].path)
}
@@ -22,6 +26,10 @@ export const clearCacheDirectories = (): Promise => {
RNFS.readDir(RNFS.CachesDirectoryPath).then(async (items) => {
for(let i = 0; i < items.length; i++){
+ if(items[i].path.indexOf("SentryCrash") !== -1){
+ continue
+ }
+
try{
await RNFS.unlink(items[i].path)
}
@@ -32,6 +40,10 @@ export const clearCacheDirectories = (): Promise => {
RNFS.readDir(cachedDownloadsPath).then(async (items) => {
for(let i = 0; i < items.length; i++){
+ if(items[i].path.indexOf("SentryCrash") !== -1){
+ continue
+ }
+
try{
await RNFS.unlink(items[i].path)
}
diff --git a/src/screens/SettingsScreen/SettingsScreen.tsx b/src/screens/SettingsScreen/SettingsScreen.tsx
index 655d21e2..2f7c356b 100644
--- a/src/screens/SettingsScreen/SettingsScreen.tsx
+++ b/src/screens/SettingsScreen/SettingsScreen.tsx
@@ -18,6 +18,7 @@ import { hasStoragePermissions } from "../../lib/permissions"
import { SheetManager } from "react-native-actions-sheet"
import { setStatusBarStyle } from "../../lib/statusbar"
import * as MediaLibrary from "expo-media-library"
+import * as Sentry from "@sentry/react-native"
const MISC_BASE_PATH: string = RNFS.DocumentDirectoryPath + (RNFS.DocumentDirectoryPath.slice(-1) == "/" ? "" : "/") + "misc/"