Implement React Native bridge method for making api requests#10779
Implement React Native bridge method for making api requests#10779
Conversation
|
You can test the changes on this Pull Request by downloading the APK here. |
54766bf to
8ef5ac9
Compare
8ef5ac9 to
c026d8d
Compare
| onSuccess: Consumer<String>, | ||
| onError: Consumer<String> | ||
| ) { | ||
| GlobalScope.launch(bgDispatcher) { |
There was a problem hiding this comment.
GlobalScope should generally not be used. We should add the CoroutineScope interface to this class and add hooks to onCreate and onDestroy where the first one initializes the scope and the second one cancels it. When I'm thinking about it, it's probably enough to just hook to onDestroy to cancel the job since the ReactNativeRequestHandler is always recreated with the activity.
The disadvantage of the GlobalScope is that it creates a memory leaks (of the two passed Consumers). The operation is also not cancelled when the activity is destroyed.
There was a problem hiding this comment.
That makes sense. Thanks for explaining. I've updated this. I did implement this in a slightly different way from what I've seen elsewhere in the app (instead of storing the job in a field, I just got it from the coroutineContext when I needed to cancel it), so let me know if you see any issues with that since I don't have a lot of experience with coroutines.
|
|
||
| private const val WPCOM_ENDPOINT = "https://public-api.wordpress.com" | ||
|
|
||
| internal fun parseUrlAndParamsForWPCom(pathWithParams: String, wpComSiteId: Long): Pair<String, Map<String, String>>? = |
There was a problem hiding this comment.
I'd change this file to an injected Utils class so we don't have to retest its functionality in all the places where it's called.
There was a problem hiding this comment.
I wanted to avoid creating one of those util classes that doesn't have any state or any dependencies because those types of classes are one of the few cases where imho testing considerations lead to worse code design. Of course, I also didn't realize that we weren't using powermock or mockk in this project 😄 , and I certainly understand there are also good reasons for avoiding libraries that let you mock static methods.
Updated to be an injectable ReactNativeUrlUtil class. 👍
| response: ReactNativeFetchResponse, | ||
| onSuccess: (String) -> Unit, | ||
| onError: (String) -> Unit | ||
| ) { |
There was a problem hiding this comment.
I think this should have withContext(mainDispatcher) so you make sure the it's called on the main thread (it's injected with @Named(UI_THREAD) private val mainDispatcher: CoroutineDispatcher)
There was a problem hiding this comment.
Because this class is specifically for use only with React Native I'm not sure I agree. Anything sent to React Native is sent asynchronously across the javascript bridge, so keeping it on a background thread on the native side makes sense to me. If we ever were to try to do some UI updates on the native side based on the result of this call (I can't imagine why we would, but never say never 😄 ), I assume the issue would immediately present itself pretty clearly (immediate crash with UI-update-from-background-thread type of exception), and we would be better equipped to determine the best way to handle that issue at that time.
With that said, there's really not any processing done between this point and the bridge, so I don't necessarily think it would do any harm to move it to the main thread here. I just don't want to make that change if this is only going to be used with React Native and there's no benefit for that (React Native) use case. But what do you think?
There was a problem hiding this comment.
That makes sense. I just assumed that the callback is somehow affecting the UI and that's why I suggested the shift (basically the first launch jumps to the background thread so I assumed we want to go back to the original thread here).
|
And now I know to run ktlint 😂 |
WordPress/src/test/java/org/wordpress/android/ui/posts/reactnative/ReactNativeUrlUtilTest.kt
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
I also toyed with the idea of making this something like:
coroutineContext[Job]?.apply {
cancel()
} ?: AppLog.e(EDITOR, "Scope could not be cancelled because it does not have a job. This should never happen.")
I decided against this approach since this class creates a CoroutineContext with a Job on initialization, and if this changes so that it starts failing a big, obvious, and immediate failure seems better than a silent memory leak. I do always feel a little weird using !! though, so let me know if anyone prefers this (or some other) approach.
There was a problem hiding this comment.
I usually just keep the instance of the job and cancel it directly but I think this approach is fine 👍
a0f2571 to
d23c1ee
Compare
Ref of fluxc develop once react native call changes were merged
…ry/gutenberg-image-size-networking-native
| ((AztecEditorFragment) mEditorFragment).disableContentLogOnCrashes(); | ||
| } | ||
|
|
||
| if (mReactNativeRequestHandler != null) mReactNativeRequestHandler.destroy(); |
There was a problem hiding this comment.
NIT: I'd just use {} for consistency.
build.gradle
Outdated
| daggerVersion = '2.22.1' | ||
| fluxCVersion = '1.5.1' | ||
| // FIXME update to FluxC release | ||
| fluxCVersion = 'd058757009410e19dfbf86cb0a6895d90b97cf43' |
There was a problem hiding this comment.
edit: you know about this
There was a problem hiding this comment.
I just wanted to wait until this PR got some testing before tagging a fluxc release in case any issues were found that needed additional fluxc changes. But hopefully you're right, and I'll just be tagging this commit. 😄
| import javax.inject.Inject | ||
| import javax.inject.Named | ||
|
|
||
| class ReactNativeRequestHandler @Inject constructor( |
There was a problem hiding this comment.
do you think you can write some tests for this class to?
|
Matt is a few hours away (different timezone) so I will re-target this PR to point to develop, and also update the gutenberg-mobile pointer to the latest of the respective PR. |
|
Tried out against a WPCOM site and works 👍 Against a .org (Pressable, Jetpack installed but not activated) site of mine the size setting didn't seem to work and noticed the following network error in logcat: While investigating I saw that Gutenberg was outdated and not activated on the site. After updating and activating I got the following in logcat when opening the post-with-the-image I tried before: Then, tapping on the image got me the same 401 error (in logcat). Not sure if some other configuration is weird on my .org site. I'll try another too. |
hypest
left a comment
There was a problem hiding this comment.
Tried on a newly created Pressable site and the size setting works fine. I do get some 403s during the while process but, the setting works nevertheless.
I think this is good to merge and we can follow up with dedicated PR(s) for any issue.
|
Thanks for the review @hypest ! Errors in LogsI investigated the errors you noticed in the logs a bit and confirmed that they are not causing problems for this PR. I think the root of the issue is that in the native code we’re essentially using the local database key as the image’s id until we get an update from the web. Since we make a getMedia request every time the id changes we make a request with the initial id, which fails. But it works out in the end because the web responds to the upload with the proper id, and we then make another getMedia request with that id, which succeeds. That’s why the error we’re seeing in the logs isn’t causing a problem. On Android, it looks like we’re using the database id (if there is not a media id from the wp api yet) here. api call missing imagesI have noticed some intermittent behavior where an image that is showing in the media library on both web and on mobile is not appearing in the api call. During testing it appeared that this was happening on your testing site @hypesty since you were uploading images, but "https://hypestpressable.mystagingwebsite.com/wp-json/wp/v2/media/" continued to show only a single media file (which is an audio file). As an example on my jurassic ninja site, if I hit the rest api I only get a single image with id 21: If I ssh in though and run If I create a post on the web using this image (id 5), I can successfully resize the image with no issues. Even loading the post on mobile will display with the updated size (although I cannot change the size on mobile). I can directly access image 21 using the rest api: If I try and directly access image 5, I get a 401 forbidden error: Interestingly, if I curl an actually non-existent image (i.e., an id other than 5 or 21) I do not get a 401 forbidden error like I get for image 5. Instead I now get a 404 invalid id error: At this point, I cannot find a way to consistently recreate this issue, but I think it may be worth adding to the issue tracking work for this feature, wdyt @etoledom ? |
…e-size-networking-native
This PR implements a minimal api call to allow gutenberg to request from javascript the api call necessary to retrieve an image's available sizes.
Related PRs:
To test:
Perform this test with both (1) a WP.com site and (2) a self-hosted site. It should work for both:
git submodule update --init --recursive)TODO before merge
PR submission checklist:
I have considered adding unit tests where possible.
I have considered if this change warrants user-facing release notes and have added them to
RELEASE-NOTES.txtif necessary.