Skip to content

Commit

Permalink
Merge pull request #191 from rvasseur31/feature/callbacks
Browse files Browse the repository at this point in the history
Implement snackbar visibility events
  • Loading branch information
cooperka authored May 9, 2023
2 parents 7afebab + 4115a3b commit fd8679a
Show file tree
Hide file tree
Showing 15 changed files with 291 additions and 34 deletions.
47 changes: 47 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,53 @@ The old keys will continue to work for now but are deprecated and may be removed

Dismisses any existing Snackbars.

## Advanced usage

### Snackbar events
You can have information on snackbar visibility.

```js
componentDidMount() {
const SnackbarEventEmitter = new NativeEventEmitter(
NativeModules.RNSnackbar,
);
this.eventListener = SnackbarEventEmitter.addListener('onSnackbarVisibility', (event) => {
console.log(event.event);
});
}
componentWillUnmount() {
this.eventListener.remove();
}
```

Or, with functional components:

```js
useEffect(() => {
const subscription = new NativeEventEmitter(
NativeModules.RNSnackbar,
).addListener('onSnackbarVisibility', event => {
console.log(event.event);
});
return () => {
subscription.remove();
};
}, []);
```

Where event is one of the following options :

| Key | Data type | Value | Description |
|-----|-----------|----------------|-------------|
| `Snackbar.DISMISS_EVENT_SWIPE` | `number` | 0 | Indicates that the Snackbar was dismissed via a swipe. |
| `Snackbar.DISMISS_EVENT_ACTION` | `number` | 1 | Indicates that the Snackbar was dismissed via an action click. |
| `Snackbar.DISMISS_EVENT_TIMEOUT` | `number` | 2 | Indicates that the Snackbar was dismissed via a timeout. |
| `Snackbar.DISMISS_EVENT_MANUAL` | `number` | 3 | Indicates that the Snackbar was dismissed via Snackbar.dismiss() call. |
| `Snackbar.DISMISS_EVENT_CONSECUTIVE` | `number` | 4 | Indicates that the Snackbar was dismissed from a new Snackbar being shown. |
| `Snackbar.SHOW_EVENT` | `number` | 5 | Indicates that Snackbar appears |


## Troubleshooting

#### Snackbar not appearing [Android]
Expand Down
1 change: 1 addition & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ repositories {

dependencies {
implementation "com.facebook.react:react-native:${_reactNativeVersion}"
implementation "androidx.appcompat:appcompat:1.3.1"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
implementation "com.google.android.material:material:${_materialVersion}"
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@

import android.graphics.Color;
import android.graphics.Typeface;

import com.google.android.material.snackbar.BaseTransientBottomBar;
import com.google.android.material.snackbar.Snackbar;

import android.os.Build;
import android.content.Context;
import android.util.DisplayMetrics;
Expand All @@ -14,28 +10,42 @@
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.google.android.material.snackbar.BaseTransientBottomBar;
import com.google.android.material.snackbar.Snackbar;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Collections;

public class SnackbarModule extends ReactContextBaseJavaModule {

private static final String REACT_NAME = "RNSnackbar";

private List<Snackbar> mActiveSnackbars = new ArrayList<>();

private static final String ON_SNACKBAR_VISIBILITY_EVENT = "onSnackbarVisibility";
private static final int SHOW_EVENT = 5;

private final List<Snackbar> mActiveSnackbars = new ArrayList<>();

public SnackbarModule(ReactApplicationContext reactContext) {
super(reactContext);
}

@NonNull
@Override
public String getName() {
return REACT_NAME;
Expand All @@ -48,6 +58,12 @@ public Map<String, Object> getConstants() {
constants.put("LENGTH_LONG", Snackbar.LENGTH_LONG);
constants.put("LENGTH_SHORT", Snackbar.LENGTH_SHORT);
constants.put("LENGTH_INDEFINITE", Snackbar.LENGTH_INDEFINITE);
constants.put("DISMISS_EVENT_SWIPE", Snackbar.Callback.DISMISS_EVENT_SWIPE);
constants.put("DISMISS_EVENT_ACTION", Snackbar.Callback.DISMISS_EVENT_ACTION);
constants.put("DISMISS_EVENT_TIMEOUT", Snackbar.Callback.DISMISS_EVENT_TIMEOUT);
constants.put("DISMISS_EVENT_MANUAL", Snackbar.Callback.DISMISS_EVENT_MANUAL);
constants.put("DISMISS_EVENT_CONSECUTIVE", Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE);
constants.put("SHOW_EVENT", SHOW_EVENT);

return constants;
}
Expand Down Expand Up @@ -107,6 +123,16 @@ public void dismiss() {
mActiveSnackbars.clear();
}

@ReactMethod
public void addListener(String eventName) {
// Keep: Required for RN built in Event Emitter Calls.
}

@ReactMethod
public void removeListeners(Integer count) {
// Keep: Required for RN built in Event Emitter Calls.
}

private void displaySnackbar(View view, ReadableMap options, final Callback callback) {
String text = getOptionValue(options, "text", "");
int duration = getOptionValue(options, "duration", Snackbar.LENGTH_SHORT);
Expand Down Expand Up @@ -191,6 +217,18 @@ public void onClick(View v) {
}
}

snackbar.addCallback(new BaseTransientBottomBar.BaseCallback<Snackbar>() {
@Override
public void onDismissed(Snackbar transientBottomBar, int event) {
sendSnackbarVisibilityEvent(event);
}

@Override
public void onShown(Snackbar transientBottomBar) {
sendSnackbarVisibilityEvent(SHOW_EVENT);
}
});

snackbar.show();
}

Expand All @@ -217,6 +255,20 @@ private ArrayList<View> recursiveLoopChildren(ViewGroup view, ArrayList<View> mo
return modals;
}

private void sendSnackbarVisibilityEvent(int event) {
WritableMap params = Arguments.createMap();
params.putInt("event", event);
sendEvent(getReactApplicationContext(), ON_SNACKBAR_VISIBILITY_EVENT, params);
}

private void sendEvent(ReactContext reactContext,
String eventName,
@Nullable WritableMap params) {
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
}

private String getOptionValue(ReadableMap options, String key, String fallback) {
return options.hasKey(key) ? options.getString(key) : fallback;
}
Expand Down
21 changes: 21 additions & 0 deletions example/ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ target 'SnackbarExample' do
# Pods for testing
end
use_native_modules!

post_install do |installer|
## Fix for XCode 12.5
find_and_replace("../node_modules/react-native/React/CxxBridge/RCTCxxBridge.mm",
"_initializeModules:(NSArray<id<RCTBridgeModule>> *)modules", "_initializeModules:(NSArray<Class> *)modules")
find_and_replace("../node_modules/react-native/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm",
"RCTBridgeModuleNameForClass(module))", "RCTBridgeModuleNameForClass(Class(module)))")
end
end
target 'SnackbarExample-tvOS' do
# Pods for SnackbarExample-tvOS
Expand All @@ -43,3 +51,16 @@ target 'SnackbarExample-tvOS' do
# Pods for testing
end
end

def find_and_replace(dir, findstr, replacestr)
Dir[dir].each do |name|
text = File.read(name)
replace = text.gsub(findstr,replacestr)
if text != replace
puts "Fix: " + name
File.open(name, "w") { |file| file.puts replace }
STDOUT.flush
end
end
Dir[dir + '*/'].each(&method(:find_and_replace))
end
10 changes: 5 additions & 5 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ PODS:
- React-cxxreact (= 0.61.2)
- React-jsi (= 0.61.2)
- ReactCommon/jscallinvoker (= 0.61.2)
- RNSnackbar (2.2.4):
- RNSnackbar (2.4.0):
- React-Core
- Yoga (1.14.0)

Expand Down Expand Up @@ -253,7 +253,7 @@ DEPENDENCIES:
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)

SPEC REPOS:
https://github.com/CocoaPods/Specs.git:
trunk:
- boost-for-react-native

EXTERNAL SOURCES:
Expand Down Expand Up @@ -336,9 +336,9 @@ SPEC CHECKSUMS:
React-RCTText: e3ef6191cdb627855ff7fe8fa0c1e14094967fb8
React-RCTVibration: fb54c732fd20405a76598e431aa2f8c2bf527de9
ReactCommon: 5848032ed2f274fcb40f6b9ec24067787c42d479
RNSnackbar: 86092381dba7a0ed9fbd7acdb82ebbd5bfd26372
RNSnackbar: be3333a21a453ccc272f41a8add5a71f7d44dfcd
Yoga: 14927e37bd25376d216b150ab2a561773d57911f

PODFILE CHECKSUM: 4ec39b585cf6ab1637af9ccf9756b2ea164b2d94
PODFILE CHECKSUM: fb6c1683e9500b74f4151c37cfa77a763e247845

COCOAPODS: 1.9.3
COCOAPODS: 1.10.1
15 changes: 14 additions & 1 deletion example/src/App.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
import React, { Component } from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
import { Text, View, TouchableOpacity, NativeEventEmitter, NativeModules } from 'react-native';
import Snackbar from 'react-native-snackbar';

import styles from '../styles';

// eslint-disable-next-line react/prefer-stateless-function
class Example extends Component {
componentDidMount() {
const SnackbarEventEmitter = new NativeEventEmitter(
NativeModules.RNSnackbar,
);
this.eventListener = SnackbarEventEmitter.addListener('onSnackbarVisibility', (event) => {
console.log(event.event);
});
}

componentWillUnmount() {
this.eventListener.remove();
}

render() {
return (
<View style={styles.container}>
Expand Down
4 changes: 4 additions & 0 deletions example/src/__tests__/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import renderer from 'react-test-renderer';

import App from '../App';

jest.mock(
'../../node_modules/react-native/Libraries/EventEmitter/NativeEventEmitter',
);

describe('Snackbar example app', () => {
it('renders without crashing', () => {
renderer.create(<App />);
Expand Down
2 changes: 1 addition & 1 deletion example/sync.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
#
# Saved as its own file so that Travis uses the correct shell to run it.

mkdir node_modules/react-native-snackbar
mkdir -p node_modules/react-native-snackbar
cp -R ../{package.json,android,ios,lib} node_modules/react-native-snackbar/
3 changes: 2 additions & 1 deletion ios/RNSnackBarView.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
//

#import <UIKit/UIKit.h>
#import "RNSnackbar.h"

@interface RNSnackBarView : UIView

+ (void)showWithOptions:(NSDictionary *)options andCallback:(void (^)())callback;
+ (void)showWithOptions:(NSDictionary *)options andCallback:(void (^)())callback rnSnackbar:(RNSnackbar *)rnSnackbar;
+ (void)dismiss;

@end
Loading

0 comments on commit fd8679a

Please sign in to comment.