Closed holmesworcester closed 1 year ago
Hey :wave:,
At Berty, we use OrbitDB for messages and metadata and ~raw IPFS for attachments and avatars. We encrypt each attachment and avatar per conversation to reduce metadata leaking, but then the transfer and discovery process is pretty standard.
There are some optimizations we already implemented and some others we have in mind, like:
@n0izn0iz, @clegirar, and @glouvigny may have additional hints to share.
Thanks for these thoughts! We have a basic implementation using just IPFS in our desktop app with a size threshold that determines auto download or manual download.
How do you handle an IPFS upload on iOS when there's a high chance or inevitability of the process getting killed?
@holmesworcester I could not find designs for attaching files on mobile. Please attach designs while composing tasks about features. Should I assume that picked files would look similar as on Slack or our Desktop?
@EmiM the figma designs are here: https://www.figma.com/file/68ucD6gpOAXW1kRNq2gjFK/Send-and-receive-files?type=design&node-id=300-12826&mode=design&t=f1tEWfGIplZTJAhm-0
But they don't cover mobile explicitly. I'll leave this up to you: if you think it's insufficient guidance we can have Jason do designs on Friday. If you have enough to proceed then we can do it now.
On desktop I think we are using the file icon placeholder for file attachments, and not the wider placeholder with the file name and file type icon. So we should also take advantage of that simplification on mobile too, and not do it the way Slack does in your example above.
@EmiM can you share more information in this issue about the problems we're running into, as we discussed on our call this week?
Right now I got stuck on accessing files on Android. I'm using https://www.npmjs.com/package/react-native-document-picker which opens native file explorer and allows to choose one or multiple files. It then returns the information about those files, including URI. URI on Android is returned in a form of "content://".
This works fine. The problem begins with accessing the file (reading):
07-04 15:09:03.050 21232 21351 W System.err: java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaDocumentsProvider uri content://com.android.providers.media.documents/document/document:1000003887 from pid=21232, uid=10403 requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs
I added <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
(and WRITE_EXTERNAL_STORAGE, and MANAGE_EXTERNAL_STORAGE just in case) to AndroidManifest but that did not work.
I also added android:requestLegacyExternalStorage="true"
(though it should not work anymore according to docs).
I think that's because Android requires runtime permissions for some actions since SDK version 23: https://reactnative.dev/docs/permissionsandroid.html
I see that list of "Permissions that require prompting the user" includes READ_EXTERNAL_STORAGE
.
I've tried their snippet, however right now instead of prompt asking about permission I'm getting (in the logs) information that I just don't have permission to access storage.
I also don't have any permissions listed in app's settings.
This is what I've looked at (and other): https://github.com/itinance/react-native-fs/pull/395 https://github.com/RonRadtke/react-native-blob-util/issues/118 https://github.com/itinance/react-native-fs/issues/676 https://github.com/itinance/react-native-fs/issues/756#issuecomment-554937928
For a moment I thought that the problem lies in Scoped Storage but I consulted Wiktor and it's probably not the case: https://blog.notesnook.com/scoped-storage-in-react-native/
Is this answer helpful? https://chat.openai.com/share/688e6e75-6c46-4176-81f3-14bc2adda6c0
And is this the same problem? https://github.com/rnmods/react-native-document-picker/discussions/626
This solution also looks worth trying: https://github.com/rnmods/react-native-document-picker/discussions/235#discussioncomment-241563
Also @EmiM can you post screenshots of this feature as soon as you have any that are helpful?
Is this answer helpful? https://chat.openai.com/share/688e6e75-6c46-4176-81f3-14bc2adda6c0
I am not sure about this chatgpt response. It just showed a code that opens a file but does not solve permission problem. I can try it, I already saw someone doing something similar (https://stackoverflow.com/questions/60168797/react-native-get-file-name-from-android-content-uri) but I preferred to use react-native solutions first.
And is this the same problem? rnmods/react-native-document-picker#626
Yes. Other people also encountered this (in the links I posted above)
This solution also looks worth trying: rnmods/react-native-document-picker#235 (comment)
As I mentioned in the comment above - I'm already using this runtime permission.
@EmiM not sure if this is helpful, but looking through the code there's a lot of additional complexity here for handling multiple files, previews, copying the file, etc. Would it help to boil everything down to just a single function for selecting one file and logging it?
The docs say that if the user picks the file you should be able to use it without additional permissions, but I'm wondering if something is getting lost in there, or if there is a special way you have to use the URI.
What do you mean by additional complexity? There is no complexity and handling file itself is done in one function. I can select one file and log it, I'm doing it, those are basics, I don't know what you are trying to suggest.
The main problem for me right now is mobile dev environment, which is not deterministic for me and often does not work. I was talking with Wiktor, he does not have those problems or have them rarely, Other teammates also face them very rarely. Maybe properly running project on emulator is a better option form me than running it on my phone, I don't know.
I know that you are trying to help but in this case I need someone experienced with mobile development.
Well it's good if we're uncovering that problem. We definitely need a deterministic mobile dev environment for everyone.
I can explain what I mean by additional complexity the next time we talk voice.
Maybe I'll explain that in other words: I understand what you mean, I don't understand what you mean in this particular case because this code has no complexity.
https://github.com/TryQuiet/quiet/pull/1622
Btw. the solution (or workaround) to problem with 'content://' URI was to use copyTo
argument of DocumentPicker.pick
. It then copies file to cache directory and retrieves regular URI (file://
) which allows for further file manipulation (access).
Files picking right now (android): https://github.com/TryQuiet/quiet/assets/6099829/8d15a65f-9374-4772-a75a-88ee36e27a83
Looks good enough to release as a first iteration but there's something a little awkward about the design. On desktop the paperclip and the attachment thumbnails display inside the border of the editing field. But here they are outside it. The fix would be to put them inside the border of the editing field.
If it's an easy change let's try it. If not we can release it now and adjust it later when we get a design from Jason.
Jason's quick comments are similar:
"Would be nice to put paperclip and images in the field. I think we should round the corners more. If we really want to polish, the send button could be hidden completely when the field is empty. Could spend some time on it Friday. Just some quick thoughts."
On my phone there are no issues with uploading files (Pixel 5a, Android 13).
However on Bartek's phone (Samsung Galaxy s20+5g, Android 13, One UI 5.1) the application throws No such file or directory
error:
07-24 13:51:57.246 24737 24948 E NODEJS-MOBILE: 2023-07-24T11:51:57.246Z backend:StorageService Writing to public channel db general_837baee97c5dd40cdfc72fb45f6f5aa9
07-24 13:51:57.399 24737 24737 W Glide : Load failed for file:///data/user/0/com.quietmobile.debug/files/backend/files/uploads/1690199517144_8i5sl804rjx_Screenshot_20230714_123220_Debug%20Quiet.jpg with size [823x1828]
07-24 13:51:57.399 24737 24737 W Glide : class com.bumptech.glide.load.engine.GlideException: Failed to load resource
07-24 13:51:57.399 24737 24737 W Glide : There were 3 root causes:
07-24 13:51:57.399 24737 24737 W Glide : java.io.FileNotFoundException(/data/user/0/com.quietmobile.debug/files/backend/files/uploads/1690199517144_8i5sl804rjx_Screenshot_20230714_123220_Debug Quiet.jpg: open failed: ENOENT (No such file or directory))
07-24 13:51:57.399 24737 24737 W Glide : java.io.FileNotFoundException(open failed: ENOENT (No such file or directory))
07-24 13:51:57.399 24737 24737 W Glide : java.io.FileNotFoundException(open failed: ENOENT (No such file or directory))
07-24 13:51:57.399 24737 24737 W Glide : call GlideException#logRootCauses(String) for more detail
07-24 13:51:57.399 24737 24737 W Glide : Cause (1 of 3): class com.bumptech.glide.load.engine.GlideException: Fetching data failed, class java.io.InputStream, LOCAL
07-24 13:51:57.399 24737 24737 W Glide : There was 1 root cause:
07-24 13:51:57.399 24737 24737 W Glide : java.io.FileNotFoundException(/data/user/0/com.quietmobile.debug/files/backend/files/uploads/1690199517144_8i5sl804rjx_Screenshot_20230714_123220_Debug Quiet.jpg: open failed: ENOENT (No such file or directory))
07-24 13:51:57.399 24737 24737 W Glide : call GlideException#logRootCauses(String) for more detail
07-24 13:51:57.399 24737 24737 W Glide : Cause (1 of 1): class com.bumptech.glide.load.engine.GlideException: Fetch failed
07-24 13:51:57.399 24737 24737 W Glide : There was 1 root cause:
07-24 13:51:57.399 24737 24737 W Glide : java.io.FileNotFoundException(/data/user/0/com.quietmobile.debug/files/backend/files/uploads/1690199517144_8i5sl804rjx_Screenshot_20230714_123220_Debug Quiet.jpg: open failed: ENOENT (No such file or directory))
07-24 13:51:57.399 24737 24737 W Glide : call GlideException#logRootCauses(String) for more detail
07-24 13:51:57.399 24737 24737 W Glide : Cause (1 of 1): class java.io.FileNotFoundException: /data/user/0/com.quietmobile.debug/files/backend/files/uploads/1690199517144_8i5sl804rjx_Screenshot_20230714_123220_Debug Quiet.jpg: open failed: ENOENT (No such file or directory)
07-24 13:51:57.399 24737 24737 W Glide : Cause (2 of 3): class com.bumptech.glide.load.engine.GlideException: Fetching data failed, class android.os.ParcelFileDescriptor, LOCAL
07-24 13:51:57.399 24737 24737 W Glide : There was 1 root cause:
07-24 13:51:57.399 24737 24737 W Glide : java.io.FileNotFoundException(open failed: ENOENT (No such file or directory))
07-24 13:51:57.399 24737 24737 W Glide : call GlideException#logRootCauses(String) for more detail
07-24 13:51:57.399 24737 24737 W Glide : Cause (1 of 1): class com.bumptech.glide.load.engine.GlideException: Fetch failed
07-24 13:51:57.399 24737 24737 W Glide : There was 1 root cause:
07-24 13:51:57.399 24737 24737 W Glide : java.io.FileNotFoundException(open failed: ENOENT (No such file or directory))
07-24 13:51:57.399 24737 24737 W Glide : call GlideException#logRootCauses(String) for more detail
07-24 13:51:57.399 24737 24737 W Glide : Cause (1 of 1): class java.io.FileNotFoundException: open failed: ENOENT (No such file or directory)
07-24 13:51:57.399 24737 24737 W Glide : Cause (3 of 3): class com.bumptech.glide.load.engine.GlideException: Fetching data failed, class android.content.res.AssetFileDescriptor, LOCAL
07-24 13:51:57.399 24737 24737 W Glide : There was 1 root cause:
07-24 13:51:57.399 24737 24737 W Glide : java.io.FileNotFoundException(open failed: ENOENT (No such file or directory))
07-24 13:51:57.399 24737 24737 W Glide : call GlideException#logRootCauses(String) for more detail
07-24 13:51:57.399 24737 24737 W Glide : Cause (1 of 1): class java.io.FileNotFoundException: open failed: ENOENT (No such file or directory)
07-24 13:51:57.399 24737 24737 I Glide : Root cause (1 of 3)
07-24 13:51:57.399 24737 24737 I Glide : java.io.FileNotFoundException: /data/user/0/com.quietmobile.debug/files/backend/files/uploads/1690199517144_8i5sl804rjx_Screenshot_20230714_123220_Debug Quiet.jpg: open failed: ENOENT (No such file or directory)
07-24 13:51:57.399 24737 24737 I Glide : at libcore.io.IoBridge.open(IoBridge.java:574)
07-24 13:51:57.399 24737 24737 I Glide : at java.io.FileInputStream.<init>(FileInputStream.java:160)
07-24 13:51:57.399 24737 24737 I Glide : at java.io.FileInputStream.<init>(FileInputStream.java:115)
07-24 13:51:57.399 24737 24737 I Glide : at android.content.ContentResolver.openInputStream(ContentResolver.java:1523)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.data.StreamLocalUriFetcher.loadResourceFromUri(StreamLocalUriFetcher.java:74)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.data.StreamLocalUriFetcher.loadResource(StreamLocalUriFetcher.java:50)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.data.StreamLocalUriFetcher.loadResource(StreamLocalUriFetcher.java:13)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.data.LocalUriFetcher.loadData(LocalUriFetcher.java:44)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.loadData(MultiModelLoader.java:100)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.engine.SourceGenerator.startNextLoad(SourceGenerator.java:70)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.engine.SourceGenerator.startNext(SourceGenerator.java:63)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:311)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.engine.DecodeJob.runWrapped(DecodeJob.java:280)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.engine.DecodeJob.run(DecodeJob.java:235)
07-24 13:51:57.399 24737 24737 I Glide : at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137)
07-24 13:51:57.399 24737 24737 I Glide : at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637)
07-24 13:51:57.399 24737 24737 I Glide : at java.lang.Thread.run(Thread.java:1012)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.engine.executor.GlideExecutor$DefaultThreadFactory$1.run(GlideExecutor.java:393)
07-24 13:51:57.399 24737 24737 I Glide : Caused by: android.system.ErrnoException: open failed: ENOENT (No such file or directory)
07-24 13:51:57.399 24737 24737 I Glide : at libcore.io.Linux.open(Native Method)
07-24 13:51:57.399 24737 24737 I Glide : at libcore.io.ForwardingOs.open(ForwardingOs.java:563)
07-24 13:51:57.399 24737 24737 I Glide : at libcore.io.BlockGuardOs.open(BlockGuardOs.java:274)
07-24 13:51:57.399 24737 24737 I Glide : at libcore.io.ForwardingOs.open(ForwardingOs.java:563)
07-24 13:51:57.399 24737 24737 I Glide : at android.app.ActivityThread$AndroidOs.open(ActivityThread.java:8619)
07-24 13:51:57.399 24737 24737 I Glide : at libcore.io.IoBridge.open(IoBridge.java:560)
07-24 13:51:57.399 24737 24737 I Glide : ... 17 more
07-24 13:51:57.399 24737 24737 I Glide : Root cause (2 of 3)
07-24 13:51:57.399 24737 24737 I Glide : java.io.FileNotFoundException: open failed: ENOENT (No such file or directory)
07-24 13:51:57.399 24737 24737 I Glide : at android.os.ParcelFileDescriptor.openInternal(ParcelFileDescriptor.java:342)
07-24 13:51:57.399 24737 24737 I Glide : at android.os.ParcelFileDescriptor.open(ParcelFileDescriptor.java:229)
07-24 13:51:57.399 24737 24737 I Glide : at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1844)
07-24 13:51:57.399 24737 24737 I Glide : at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1765)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.data.FileDescriptorLocalUriFetcher.loadResource(FileDescriptorLocalUriFetcher.java:20)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.data.FileDescriptorLocalUriFetcher.loadResource(FileDescriptorLocalUriFetcher.java:12)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.data.LocalUriFetcher.loadData(LocalUriFetcher.java:44)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.loadData(MultiModelLoader.java:100)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.engine.SourceGenerator.startNextLoad(SourceGenerator.java:70)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.engine.SourceGenerator.startNext(SourceGenerator.java:63)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:311)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.engine.DecodeJob.onDataFetcherFailed(DecodeJob.java:412)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.engine.SourceGenerator.onLoadFailedInternal(SourceGenerator.java:160)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.engine.SourceGenerator$1.onLoadFailed(SourceGenerator.java:83)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.startNextOrFail(MultiModelLoader.java:167)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.onLoadFailed(MultiModelLoader.java:154)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.data.LocalUriFetcher.loadData(LocalUriFetcher.java:50)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.loadData(MultiModelLoader.java:100)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.engine.SourceGenerator.startNextLoad(SourceGenerator.java:70)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.engine.SourceGenerator.startNext(SourceGenerator.java:63)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:311)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.engine.DecodeJob.runWrapped(DecodeJob.java:280)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.engine.DecodeJob.run(DecodeJob.java:235)
07-24 13:51:57.399 24737 24737 I Glide : at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137)
07-24 13:51:57.399 24737 24737 I Glide : at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637)
07-24 13:51:57.399 24737 24737 I Glide : at java.lang.Thread.run(Thread.java:1012)
07-24 13:51:57.399 24737 24737 I Glide : at com.bumptech.glide.load.engine.executor.GlideExecutor$DefaultThreadFactory$1.run(GlideExecutor.java:393)
07-24 13:51:57.400 24737 24737 I Glide : Root cause (3 of 3)
07-24 13:51:57.400 24737 24737 I Glide : java.io.FileNotFoundException: open failed: ENOENT (No such file or directory)
Does it happen for really small files too? Could it be a memory issue?
No. It was a "filename with whitespace" issue. Fixed.
Excellent!
Do we have a test to make sure a large file works, e.g. 1GB? Or should we set a size limit?
Also, is there currently a limit on the number of files a user can attach?
We have test for large file in the backend, since most of files handling logic is done by the backend. I also tried sending > 1GB file on my Android. However be aware that those files are copied before sending (twice but one of the copies is deleted immediately) so maybe we will need a size limit or to display a warning on mobile devices?
There is no limit on the number of files a user can attach but I am not sure if we need such limit.
Note File explorer shows on Bartek's phone very limited number of files when opened from our application. To be investigated in separate task.
Note File explorer shows on Bartek's phone very limited number of files when opened from our application. To be investigated in separate task.
Let's create that issue and link it here.
However be aware that those files are copied before sending (twice but one of the copies is deleted immediately) so maybe we will need a size limit or to display a warning on mobile devices?
What will happen if the user runs out of space because there is no space to copy these large files? Will they get an appropriate error message?
There is no limit on the number of files a user can attach but I am not sure if we need such limit.
I think we do need to eventually as part of protections against malicious users DoS'ing others, but I created a separate issue for this, since there are many things like this to address: https://github.com/TryQuiet/quiet/issues/1665
Any idea when I can send files on mobile 🥺?
We're just about to release it! Though we just found a regression with sending large files, so while images should work bigger files may be very slow or unreliable until we fix the bug.
Drop me an email and I'll let you know when it's live and you can tell me what you think and if there's anything you're missing :) h@quiet.chat
I can also send you an invite to our internal chat if you're not in there already.
Version 1.8.1 System: iOS, android
@holmesworcester sending on mobiles is working rather well (except of general problems that we've encountered. with download). I've got some doubts about design but I can't find design on Figma so if I would be grateful if you can show me where this specific one is.
I also have some ideas to test with files big enough to clog mobile (mobile phone, not mobile app) that I want to test. I should be able to do this today.
What do you mean "mobile phone not mobile app"?
For design, we decided to release as is on this one and then get a design review from Jason.
1) What do you mean "mobile phone not mobile app"? As we are copying file that we want to upload to Quiet data it's possible that mobile device will run out of space with some big file. Especially that Emi made me aware that at one point during upload we have the file twice on Quiet. So let's say say that you've got 3 GB space on your mobile and want to upload 2 GB file via Quiet. Quiet will take 2 GB of this space so it looks like everything is fine but at some point it is actually taking twice as much space. I have no idea how Quiet and device will react. I'm also not sure what is going to happen if we try to upload file bigger than space that we have on our phone. With sizes of phones memory it looks like corner case but it would be good to check it at some point.
2) For design, we decided to release as is on this one and then get a design review from Jason.
If you want to provide Jason with how it looks now, this is mobile version: And desktop version to compare:
If you want to provide Jason with how it looks now, this is mobile version:
He left some feedback already but we decided to release first anyway. A full review with the app in his hands will be better, regardless.
As we are copying file that we want to upload to Quiet data it's possible that mobile device will run out of space
@EmiM what checks are in place right now with sending files to ensure that a user doesn't fill up their device? What behavior should kinga be checking?
@kingalg it's possible we know already that these cases are not yet considered and that we just need to create an issue. So you might as well check with emi first before spending time on this.
@holmesworcester There was some of information missing in description so checking with Emi was first thing that I did. I normally would think about it as "out of scope" as it's not mentioned anywhere but as it had a risk of potentially crash not only our app but phone itself as well I decided that it is important to check it before release. It looks like when the phone run out of space we are not uploading anymore but don't inform user about anything. I will create separate issue for this case but I can confirm that it didn't break app or phone on both iOS and Android so it is not something that we need to address before releasing this feature.
Requirements:
Non-requirements: