Skip to content

Commit

Permalink
feat: Add Hook to monitor network connectivity status (#56861)
Browse files Browse the repository at this point in the history
* feat: Frame useNetInfo hook foundation

This code is non-functioning currently.

* feat: Add iOS connection status bridge utilities

This bridge will be required for the planned JavaScript Hook to monitor
connection status.

* feat: Add `useIsConnected` hook

Provides React Hook for monitoring the network connection status via the
bridge to the host app.

* Revert "feat: Frame useNetInfo hook foundation"

This reverts commit a8d3660.

* refactor: Align with project Swift syntax

Semicolon is unnecessary.

Co-authored-by: Tanner Stokes <tanner.stokes@automattic.com>

* feat: Add Android connection status bridge utilities

This bridge enables monitoring the connection status on Android.

* feat: Android network connection status request utility

Allow the Android platform to request the current network connection
status.

* fix: Add missing `requestConnectionStatus` bridge method mock

The Demo editor fails to build without a mocked bridge method.

---------

Co-authored-by: Tanner Stokes <tanner.stokes@automattic.com>
  • Loading branch information
dcalhoun and twstokes authored Dec 11, 2023
1 parent 9080cb6 commit 65486ec
Show file tree
Hide file tree
Showing 11 changed files with 120 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ interface BlockTypeImpressionsCallback {
void onRequestBlockTypeImpressions(ReadableMap impressions);
}

interface ConnectionStatusCallback {
void onRequestConnectionStatus(boolean isConnected);
}

// Ref: https://github.com/facebook/react-native/blob/HEAD/Libraries/polyfills/console.js#L376
enum LogLevel {
TRACE(0),
Expand Down Expand Up @@ -183,4 +187,6 @@ void gutenbergDidRequestUnsupportedBlockFallback(ReplaceUnsupportedBlockCallback
void toggleUndoButton(boolean isDisabled);

void toggleRedoButton(boolean isDisabled);

void requestConnectionStatus(ConnectionStatusCallback connectionStatusCallback);
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;

import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergBridgeJS2Parent.ConnectionStatusCallback;
import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergBridgeJS2Parent.MediaType;
import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergBridgeJS2Parent.OtherMediaOptionsReceivedCallback;
import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergBridgeJS2Parent.FocalPointPickerTooltipShownCallback;
Expand Down Expand Up @@ -85,6 +86,8 @@ public class RNReactNativeGutenbergBridgeModule extends ReactContextBaseJavaModu

public static final String MAP_KEY_FEATURED_IMAGE_ID = "featuredImageId";

public static final String MAP_KEY_IS_CONNECTED = "isConnected";

private boolean mIsDarkMode;

public RNReactNativeGutenbergBridgeModule(ReactApplicationContext reactContext,
Expand Down Expand Up @@ -533,4 +536,18 @@ public void generateHapticFeedback() {
}
}
}

@ReactMethod
public void requestConnectionStatus(final Callback jsCallback) {
ConnectionStatusCallback connectionStatusCallback = requestConnectionStatusCallback(jsCallback);
mGutenbergBridgeJS2Parent.requestConnectionStatus(connectionStatusCallback);
}

private ConnectionStatusCallback requestConnectionStatusCallback(final Callback jsCallback) {
return new GutenbergBridgeJS2Parent.ConnectionStatusCallback() {
@Override public void onRequestConnectionStatus(boolean isConnected) {
jsCallback.invoke(isConnected);
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import static org.wordpress.mobile.ReactNativeGutenbergBridge.RNReactNativeGutenbergBridgeModule.MAP_KEY_IS_CONNECTED;
import static org.wordpress.mobile.ReactNativeGutenbergBridge.RNReactNativeGutenbergBridgeModule.MAP_KEY_MEDIA_FILE_UPLOAD_MEDIA_ID;
import static org.wordpress.mobile.ReactNativeGutenbergBridge.RNReactNativeGutenbergBridgeModule.MAP_KEY_MEDIA_FILE_UPLOAD_MEDIA_NEW_ID;
import static org.wordpress.mobile.ReactNativeGutenbergBridge.RNReactNativeGutenbergBridgeModule.MAP_KEY_MEDIA_FILE_UPLOAD_MEDIA_URL;
Expand Down Expand Up @@ -44,6 +45,8 @@ public interface JSEventEmitter {

private static final String EVENT_FEATURED_IMAGE_ID_NATIVE_UPDATED = "featuredImageIdNativeUpdated";

private static final String EVENT_CONNECTION_STATUS_CHANGE = "connectionStatusChange";

private static final String MAP_KEY_MEDIA_FILE_STATE = "state";
private static final String MAP_KEY_MEDIA_FILE_MEDIA_ACTION_PROGRESS = "progress";
private static final String MAP_KEY_MEDIA_FILE_MEDIA_SERVER_ID = "mediaServerId";
Expand Down Expand Up @@ -222,6 +225,12 @@ public void sendToJSFeaturedImageId(int mediaId) {
queueActionToJS(EVENT_FEATURED_IMAGE_ID_NATIVE_UPDATED, writableMap);
}

public void onConnectionStatusChange(boolean isConnected) {
WritableMap writableMap = new WritableNativeMap();
writableMap.putBoolean(MAP_KEY_IS_CONNECTED, isConnected);
queueActionToJS(EVENT_CONNECTION_STATUS_CHANGE, writableMap);
}

@Override public void onReplaceMediaFilesEditedBlock(String mediaFiles, String blockId) {
WritableMap writableMap = new WritableNativeMap();
writableMap.putString(MAP_KEY_REPLACE_BLOCK_HTML, mediaFiles);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ public class WPAndroidGlueCode {
private OnToggleUndoButtonListener mOnToggleUndoButtonListener;

private OnToggleRedoButtonListener mOnToggleRedoButtonListener;
private OnConnectionStatusEventListener mOnConnectionStatusEventListener;
private boolean mIsEditorMounted;

private String mContentHtml = "";
Expand Down Expand Up @@ -259,6 +260,10 @@ public interface OnToggleRedoButtonListener {
void onToggleRedoButton(boolean isDisabled);
}

public interface OnConnectionStatusEventListener {
boolean onRequestConnectionStatus();
}

public void mediaSelectionCancelled() {
mAppendsMultipleSelectedToSiblingBlocks = false;
}
Expand Down Expand Up @@ -594,6 +599,12 @@ public void toggleUndoButton(boolean isDisabled) {
public void toggleRedoButton(boolean isDisabled) {
mOnToggleRedoButtonListener.onToggleRedoButton(isDisabled);
}

@Override
public void requestConnectionStatus(ConnectionStatusCallback connectionStatusCallback) {
boolean isConnected = mOnConnectionStatusEventListener.onRequestConnectionStatus();
connectionStatusCallback.onRequestConnectionStatus(isConnected);
}
}, mIsDarkMode);

return Arrays.asList(
Expand Down Expand Up @@ -688,6 +699,7 @@ public void attachToContainer(ViewGroup viewGroup,
OnSendEventToHostListener onSendEventToHostListener,
OnToggleUndoButtonListener onToggleUndoButtonListener,
OnToggleRedoButtonListener onToggleRedoButtonListener,
OnConnectionStatusEventListener onConnectionStatusEventListener,
boolean isDarkMode) {
MutableContextWrapper contextWrapper = (MutableContextWrapper) mReactRootView.getContext();
contextWrapper.setBaseContext(viewGroup.getContext());
Expand All @@ -713,6 +725,7 @@ public void attachToContainer(ViewGroup viewGroup,
mOnSendEventToHostListener = onSendEventToHostListener;
mOnToggleUndoButtonListener = onToggleUndoButtonListener;
mOnToggleRedoButtonListener = onToggleRedoButtonListener;
mOnConnectionStatusEventListener = onConnectionStatusEventListener;

sAddCookiesInterceptor.setOnAuthHeaderRequestedListener(onAuthHeaderRequestedListener);

Expand Down Expand Up @@ -1149,6 +1162,10 @@ public void sendToJSFeaturedImageId(int mediaId) {
mDeferredEventEmitter.sendToJSFeaturedImageId(mediaId);
}

public void connectionStatusChange(boolean isConnected) {
mDeferredEventEmitter.onConnectionStatusChange(isConnected);
}

public void replaceUnsupportedBlock(String content, String blockId) {
if (mReplaceUnsupportedBlockCallback != null) {
mReplaceUnsupportedBlockCallback.replaceUnsupportedBlock(content, blockId);
Expand Down
48 changes: 48 additions & 0 deletions packages/react-native-bridge/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
*/
import { NativeModules, NativeEventEmitter, Platform } from 'react-native';

/**
* WordPress dependencies
*/
import { useEffect, useState } from '@wordpress/element';

const { RNReactNativeGutenbergBridge } = NativeModules;
const isIOS = Platform.OS === 'ios';
const isAndroid = Platform.OS === 'android';
Expand Down Expand Up @@ -185,6 +190,49 @@ export function subscribeOnRedoPressed( callback ) {
return gutenbergBridgeEvents.addListener( 'onRedoPressed', callback );
}

export function useIsConnected() {
const [ isConnected, setIsConnected ] = useState( null );

useEffect( () => {
let isCurrent = true;

RNReactNativeGutenbergBridge.requestConnectionStatus(
( isBridgeConnected ) => {
if ( ! isCurrent ) {
return;
}

setIsConnected( isBridgeConnected );
}
);

return () => {
isCurrent = false;
};
}, [] );

useEffect( () => {
const subscription = subscribeConnectionStatus(
( { isConnected: isBridgeConnected } ) => {
setIsConnected( isBridgeConnected );
}
);

return () => {
subscription.remove();
};
}, [] );

return { isConnected };
}

function subscribeConnectionStatus( callback ) {
return gutenbergBridgeEvents.addListener(
'connectionStatusChange',
callback
);
}

/**
* Request media picker for the given media source.
*
Expand Down
5 changes: 5 additions & 0 deletions packages/react-native-bridge/ios/Gutenberg.swift
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,11 @@ public class Gutenberg: UIResponder {
bridgeModule.sendEventIfNeeded(.onRedoPressed, body: nil)
}

public func connectionStatusChange(isConnected: Bool) {
var data: [String: Any] = ["isConnected": isConnected]
bridgeModule.sendEventIfNeeded(.connectionStatusChange, body: data)
}

private func properties(from editorSettings: GutenbergEditorSettings?) -> [String : Any] {
var settingsUpdates = [String : Any]()
settingsUpdates["isFSETheme"] = editorSettings?.isFSETheme ?? false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,8 @@ public protocol GutenbergBridgeDelegate: AnyObject {
func gutenbergDidRequestToggleUndoButton(_ isDisabled: Bool)

func gutenbergDidRequestToggleRedoButton(_ isDisabled: Bool)

func gutenbergDidRequestConnectionStatus() -> Bool
}

// MARK: - Optional GutenbergBridgeDelegate methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,6 @@ @interface RCT_EXTERN_MODULE(RNReactNativeGutenbergBridge, NSObject)
RCT_EXTERN_METHOD(generateHapticFeedback)
RCT_EXTERN_METHOD(toggleUndoButton:(BOOL)isDisabled)
RCT_EXTERN_METHOD(toggleRedoButton:(BOOL)isDisabled)
RCT_EXTERN_METHOD(requestConnectionStatus:(RCTResponseSenderBlock)callback)

@end
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,11 @@ public class RNReactNativeGutenbergBridge: RCTEventEmitter {
func toggleRedoButton(_ isDisabled: Bool) {
self.delegate?.gutenbergDidRequestToggleRedoButton(isDisabled)
}

@objc
func requestConnectionStatus(_ callback: @escaping RCTResponseSenderBlock) {
callback([self.delegate?.gutenbergDidRequestConnectionStatus() ?? true])
}
}

// MARK: - RCTBridgeModule delegate
Expand Down Expand Up @@ -450,6 +455,7 @@ extension RNReactNativeGutenbergBridge {
case showEditorHelp
case onUndoPressed
case onRedoPressed
case connectionStatusChange
}

public override func supportedEvents() -> [String]! {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,11 @@ public void toggleRedoButton(boolean isDisabled) {
mainActivity.updateRedoItem(isDisabled);
}
}

@Override
public void requestConnectionStatus(ConnectionStatusCallback connectionStatusCallback) {
connectionStatusCallback.onRequestConnectionStatus(true);
}
}, isDarkMode());

return new DefaultReactNativeHost(this) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,10 @@ extension GutenbergViewController: GutenbergBridgeDelegate {
}
}
}

func gutenbergDidRequestConnectionStatus() -> Bool {
return true
}
}

extension GutenbergViewController: GutenbergWebDelegate {
Expand Down

1 comment on commit 65486ec

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flaky tests detected in 65486ec.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/7174834511
📝 Reported issues:

Please sign in to comment.