Skip to content

Commit

Permalink
feat: Add Hook to monitor network connectivity status (WordPress#56609)
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 <[email protected]>

* 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 <[email protected]>
  • Loading branch information
dcalhoun and twstokes authored Dec 6, 2023
1 parent 4be83f8 commit ed1b246
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

0 comments on commit ed1b246

Please sign in to comment.