react-native-bootsplash 4.0.2 → 4.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -132,6 +132,17 @@ yarn react-native generate-bootsplash assets/bootsplash_logo_original.png \
132
132
  --flavor=main
133
133
  ```
134
134
 
135
+ #### Using an SVG
136
+
137
+ You might want to use SVG as input file. For that, you can install a CLI tool to convert your logo first:
138
+
139
+ ```bash
140
+ $ brew install librsvg # available on macOS
141
+ $ rsvg-convert -w 3000 bootsplash_logo.svg bootsplash_logo.png # create a large PNG with generous leeway for 4x Android xxxhdpi devices
142
+ $ react-native generate-bootsplash bootsplash_logo.png
143
+ $ rm bootsplash_logo.png # optionally, clean up the PNG
144
+ ```
145
+
135
146
  ![](https://raw.githubusercontent.com/zoontek/react-native-bootsplash/master/docs/cli_tool.png?raw=true)
136
147
 
137
148
  This tool relies on the naming conventions that are used in the `/example` project and will therefore create the following files:
@@ -216,7 +227,7 @@ dependencies {
216
227
  //noinspection GradleDynamicVersion
217
228
  implementation "com.facebook.react:react-native:+" // From node_modules
218
229
 
219
- implementation "androidx.core:core-splashscreen:1.0.0-alpha02" // Add this line
230
+ implementation "androidx.core:core-splashscreen:1.0.0-beta01" // Add this line
220
231
 
221
232
  // …
222
233
  ```
@@ -316,8 +327,6 @@ RNBootSplash.hide(); // immediate
316
327
  RNBootSplash.hide({ fade: true }); // fade
317
328
  ```
318
329
 
319
- ---
320
-
321
330
  ### getVisibilityStatus()
322
331
 
323
332
  #### Method type
@@ -360,7 +369,25 @@ function App() {
360
369
 
361
370
  **🤙 A more complex example is available in the [`/example` folder](example).**
362
371
 
363
- ### Testing with Jest
372
+ ## Using React Navigation
373
+
374
+ If you are using React Navigation, you can hide the splash screen once the navigation container and all children have finished mounting by using the `onReady` function.
375
+
376
+ ```js
377
+ import React from "react";
378
+ import { NavigationContainer } from "@react-navigation/native";
379
+ import RNBootSplash from "react-native-bootsplash";
380
+
381
+ function App() {
382
+ return (
383
+ <NavigationContainer onReady={() => RNBootSplash.hide()}>
384
+ {/* content */}
385
+ </NavigationContainer>
386
+ );
387
+ }
388
+ ```
389
+
390
+ ## Testing with Jest
364
391
 
365
392
  Testing code which uses this library requires some setup since we need to mock the native methods.
366
393
 
@@ -383,24 +410,6 @@ After that, we need to add the setup file in the jest config. You can add it und
383
410
  }
384
411
  ```
385
412
 
386
- ### Using React Navigation
387
-
388
- If you are using React Navigation, you can hide the splash screen once the navigation container and all children have finished mounting by using the `onReady` function.
389
-
390
- ```js
391
- import React from "react";
392
- import { NavigationContainer } from "@react-navigation/native";
393
- import RNBootSplash from "react-native-bootsplash";
394
-
395
- function App() {
396
- return (
397
- <NavigationContainer onReady={() => RNBootSplash.hide()}>
398
- {/* content */}
399
- </NavigationContainer>
400
- );
401
- }
402
- ```
403
-
404
413
  ## 🕵️‍♂️ Comparison with [react-native-splash-screen](https://github.com/crazycodeboy/react-native-splash-screen)
405
414
 
406
415
  - If `react-native-splash-screen` encourages you to display an image over your application, `react-native-bootsplash` way-to-go is to design your launch screen using platforms tools.
@@ -11,7 +11,7 @@ Pod::Spec.new do |s|
11
11
  s.authors = package["author"]
12
12
  s.homepage = package["homepage"]
13
13
 
14
- s.platforms = { :ios => "11.0" }
14
+ s.platforms = { :ios => "11.0", :tvos => "11.0" }
15
15
  s.requires_arc = true
16
16
 
17
17
  s.source = { :git => package["repository"]["url"], :tag => s.version }
@@ -49,5 +49,5 @@ repositories {
49
49
  dependencies {
50
50
  //noinspection GradleDynamicVersion
51
51
  implementation "com.facebook.react:react-native:+" // From node_modules
52
- implementation "androidx.core:core-splashscreen:${safeExtGet("androidXSplashScreenVersion", "1.0.0-alpha02")}"
52
+ implementation "androidx.core:core-splashscreen:${safeExtGet("androidXSplashScreenVersion", "1.0.0-beta01")}"
53
53
  }
@@ -12,7 +12,6 @@ import androidx.core.splashscreen.SplashScreen;
12
12
  import androidx.core.splashscreen.SplashScreenViewProvider;
13
13
 
14
14
  import com.facebook.common.logging.FLog;
15
- import com.facebook.react.bridge.LifecycleEventListener;
16
15
  import com.facebook.react.bridge.Promise;
17
16
  import com.facebook.react.bridge.ReactApplicationContext;
18
17
  import com.facebook.react.bridge.ReactContextBaseJavaModule;
@@ -25,7 +24,7 @@ import java.util.Timer;
25
24
  import java.util.TimerTask;
26
25
 
27
26
  @ReactModule(name = RNBootSplashModule.NAME)
28
- public class RNBootSplashModule extends ReactContextBaseJavaModule implements LifecycleEventListener {
27
+ public class RNBootSplashModule extends ReactContextBaseJavaModule {
29
28
 
30
29
  public static final String NAME = "RNBootSplash";
31
30
  private static final int ANIMATION_DURATION = 220;
@@ -39,15 +38,13 @@ public class RNBootSplashModule extends ReactContextBaseJavaModule implements Li
39
38
  @Nullable
40
39
  private static SplashScreen mSplashScreen = null;
41
40
 
42
- private static final RNBootSplashQueue<RNBootSplashTask> mTaskQueue = new RNBootSplashQueue<>();
41
+ private static final RNBootSplashQueue<Promise> mPromiseQueue = new RNBootSplashQueue<>();
43
42
  private static Status mStatus = Status.HIDDEN;
44
- private static boolean mIsAppInForeground = true;
45
43
  private static boolean mShouldFade = false;
46
44
  private static boolean mShouldKeepOnScreen = true;
47
45
 
48
46
  public RNBootSplashModule(ReactApplicationContext reactContext) {
49
47
  super(reactContext);
50
- reactContext.addLifecycleEventListener(this);
51
48
  }
52
49
 
53
50
  @Override
@@ -66,7 +63,7 @@ public class RNBootSplashModule extends ReactContextBaseJavaModule implements Li
66
63
  mSplashScreen = SplashScreen.installSplashScreen(activity);
67
64
  mStatus = Status.VISIBLE;
68
65
 
69
- mSplashScreen.setKeepVisibleCondition(new SplashScreen.KeepOnScreenCondition() {
66
+ mSplashScreen.setKeepOnScreenCondition(new SplashScreen.KeepOnScreenCondition() {
70
67
  @Override
71
68
  public boolean shouldKeepOnScreen() {
72
69
  return mShouldKeepOnScreen;
@@ -96,95 +93,66 @@ public class RNBootSplashModule extends ReactContextBaseJavaModule implements Li
96
93
  });
97
94
  }
98
95
 
99
- @Override
100
- public void onHostDestroy() {
101
- mIsAppInForeground = false;
102
- }
103
-
104
- @Override
105
- public void onHostPause() {
106
- mIsAppInForeground = false;
107
- }
108
-
109
- @Override
110
- public void onHostResume() {
111
- mIsAppInForeground = true;
112
- shiftNextTask();
113
- }
96
+ private void clearPromiseQueue() {
97
+ while (!mPromiseQueue.isEmpty()) {
98
+ Promise promise = mPromiseQueue.shift();
114
99
 
115
- private void shiftNextTask() {
116
- if (mStatus != Status.TRANSITIONING && mIsAppInForeground) {
117
- RNBootSplashTask task = mTaskQueue.shift();
118
-
119
- if (task != null) {
120
- hideWithTask(task);
121
- }
100
+ if (promise != null)
101
+ promise.resolve(true);
122
102
  }
123
103
  }
124
104
 
125
- private void waitAndRetry() {
126
- final Timer timer = new Timer();
127
-
128
- timer.schedule(new TimerTask() {
129
- @Override
130
- public void run() {
131
- shiftNextTask();
132
- timer.cancel();
133
- }
134
- }, 250);
135
- }
136
-
137
- private void hideWithTask(final RNBootSplashTask task) {
138
- final Activity activity = getReactApplicationContext().getCurrentActivity();
139
- final boolean fade = task.getFade();
140
- final Promise promise = task.getPromise();
141
-
105
+ private void hideAndResolveAll(final boolean fade) {
142
106
  if (mSplashScreen == null || mStatus == Status.HIDDEN) {
143
- promise.resolve(true);
144
- shiftNextTask();
107
+ clearPromiseQueue();
145
108
  return;
146
109
  }
147
110
 
148
111
  UiThreadUtil.runOnUiThread(new Runnable() {
149
112
  @Override
150
113
  public void run() {
114
+ final Activity activity = getReactApplicationContext().getCurrentActivity();
115
+
151
116
  if (activity == null || activity.isFinishing()) {
152
- waitAndRetry(); // Wait for activity to be ready
153
- return;
154
- }
117
+ // Wait for activity to be ready
118
+ final Timer timer = new Timer();
155
119
 
156
- if (fade) {
157
- mStatus = Status.TRANSITIONING;
158
- }
120
+ timer.schedule(new TimerTask() {
121
+ @Override
122
+ public void run() {
123
+ hideAndResolveAll(fade);
124
+ timer.cancel();
125
+ }
126
+ }, 250);
127
+ } else {
128
+ if (fade) {
129
+ mStatus = Status.TRANSITIONING;
130
+ }
159
131
 
160
- mShouldFade = fade;
161
- mShouldKeepOnScreen = false;
132
+ mShouldFade = fade;
133
+ mShouldKeepOnScreen = false;
162
134
 
163
- final Timer timer = new Timer();
135
+ final Timer timer = new Timer();
164
136
 
165
- // We cannot rely on setOnExitAnimationListener
166
- // See https://issuetracker.google.com/issues/197906327
167
- timer.schedule(new TimerTask() {
168
- @Override
169
- public void run() {
170
- mStatus = Status.HIDDEN;
171
- timer.cancel();
172
- promise.resolve(true);
173
- shiftNextTask();
174
- }
175
- }, ANIMATION_DURATION);
137
+ // We cannot rely on setOnExitAnimationListener
138
+ // See https://issuetracker.google.com/issues/197906327
139
+ timer.schedule(new TimerTask() {
140
+ @Override
141
+ public void run() {
142
+ mStatus = Status.HIDDEN;
143
+ timer.cancel();
144
+ clearPromiseQueue();
145
+ }
146
+ }, ANIMATION_DURATION);
147
+ }
176
148
  }
177
149
  });
178
150
  }
179
151
 
180
152
  @ReactMethod
181
153
  public void hide(final boolean fade, final Promise promise) {
182
- if (mSplashScreen == null || mStatus == Status.HIDDEN) {
183
- promise.resolve(true);
184
- } else {
185
- mTaskQueue.push(new RNBootSplashTask(fade, promise));
186
- shiftNextTask();
187
- }
154
+ mPromiseQueue.push(promise);
155
+ hideAndResolveAll(fade);
188
156
  }
189
157
 
190
158
  @ReactMethod
@@ -1,25 +1,9 @@
1
1
  #import <React/RCTBridgeModule.h>
2
2
  #import <React/RCTRootView.h>
3
3
 
4
- typedef enum {
5
- RNBootSplashStatusVisible = 0,
6
- RNBootSplashStatusHidden = 1,
7
- RNBootSplashStatusTransitioning = 2
8
- } RNBootSplashStatus;
9
-
10
- @interface RNBootSplashTask : NSObject
11
-
12
- @property (nonatomic, readonly) BOOL fade;
13
- @property (nonatomic, readonly, strong) RCTPromiseResolveBlock _Nonnull resolve;
14
-
15
- - (instancetype _Nonnull)initWithFade:(BOOL)fade
16
- resolver:(RCTPromiseResolveBlock _Nonnull)resolve;
17
-
18
- @end
19
-
20
4
  @interface RNBootSplash : NSObject <RCTBridgeModule>
21
5
 
22
6
  + (void)initWithStoryboard:(NSString * _Nonnull)storyboardName
23
- rootView:(RCTRootView * _Nonnull)rootView;
7
+ rootView:(RCTRootView * _Nullable)rootView;
24
8
 
25
9
  @end
@@ -3,30 +3,16 @@
3
3
  #import <React/RCTBridge.h>
4
4
  #import <React/RCTUtils.h>
5
5
 
6
- static NSMutableArray<RNBootSplashTask *> *_taskQueue = nil;
6
+ static NSMutableArray<RCTPromiseResolveBlock> *_resolverQueue = nil;
7
7
  static RCTRootView *_rootView = nil;
8
- static RNBootSplashStatus _status = RNBootSplashStatusHidden;
9
-
10
- @implementation RNBootSplashTask
11
-
12
- - (instancetype)initWithFade:(BOOL)fade
13
- resolver:(RCTPromiseResolveBlock _Nonnull)resolve {
14
- if (self = [super init]) {
15
- _fade = fade;
16
- _resolve = resolve;
17
- }
18
-
19
- return self;
20
- }
21
-
22
- @end
8
+ static bool _isTransitioning = false;
23
9
 
24
10
  @implementation RNBootSplash
25
11
 
26
12
  RCT_EXPORT_MODULE();
27
13
 
28
14
  + (BOOL)requiresMainQueueSetup {
29
- return YES;
15
+ return NO;
30
16
  }
31
17
 
32
18
  - (dispatch_queue_t)methodQueue {
@@ -34,20 +20,26 @@ RCT_EXPORT_MODULE();
34
20
  }
35
21
 
36
22
  + (void)initWithStoryboard:(NSString * _Nonnull)storyboardName
37
- rootView:(RCTRootView * _Nonnull)rootView {
38
- _rootView = rootView;
39
- _status = RNBootSplashStatusVisible;
40
- _taskQueue = [[NSMutableArray alloc] init];
23
+ rootView:(RCTRootView * _Nullable)rootView {
24
+ if (rootView == nil || _rootView != nil || RCTRunningInAppExtension())
25
+ return;
41
26
 
42
- UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:nil];
43
- [_rootView setLoadingView:[[storyboard instantiateInitialViewController] view]];
27
+ _rootView = rootView;
44
28
 
45
29
  [[NSNotificationCenter defaultCenter] removeObserver:rootView
46
30
  name:RCTContentDidAppearNotification
47
31
  object:rootView];
48
32
 
33
+ UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:nil];
34
+ UIView *loadingView = [[storyboard instantiateInitialViewController] view];
35
+
36
+ if (_resolverQueue != nil)
37
+ return; // hide has already been called, abort init
38
+
39
+ [_rootView setLoadingView:loadingView];
40
+
49
41
  [[NSNotificationCenter defaultCenter] addObserver:self
50
- selector:@selector(onJavaScriptDidLoad:)
42
+ selector:@selector(onJavaScriptDidLoad)
51
43
  name:RCTJavaScriptDidLoadNotification
52
44
  object:nil];
53
45
 
@@ -55,107 +47,85 @@ RCT_EXPORT_MODULE();
55
47
  selector:@selector(onJavaScriptDidFailToLoad)
56
48
  name:RCTJavaScriptDidFailToLoadNotification
57
49
  object:nil];
58
-
59
- [[NSNotificationCenter defaultCenter] addObserver:self
60
- selector:@selector(shiftNextTask)
61
- name:UIApplicationDidBecomeActiveNotification
62
- object:nil];
63
50
  }
64
51
 
65
- + (void)onJavaScriptDidLoad:(NSNotification *)notification {
66
- [[NSNotificationCenter defaultCenter] removeObserver:self
67
- name:RCTJavaScriptDidLoadNotification
68
- object:nil];
69
-
70
- [[NSNotificationCenter defaultCenter] removeObserver:self
71
- name:RCTJavaScriptDidFailToLoadNotification
72
- object:nil];
52
+ + (void)onJavaScriptDidLoad {
53
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
73
54
  }
74
55
 
75
56
  + (void)onJavaScriptDidFailToLoad {
76
- _status = RNBootSplashStatusHidden;
77
-
78
- if (_rootView != nil) {
79
- _rootView.loadingView.hidden = YES;
80
- [_rootView.loadingView removeFromSuperview];
81
- _rootView.loadingView = nil;
82
- }
83
-
57
+ [self removeLoadingView];
84
58
  [[NSNotificationCenter defaultCenter] removeObserver:self];
85
59
  }
86
60
 
87
- + (void)shiftNextTask {
88
- if (_status != RNBootSplashStatusTransitioning &&
89
- [_taskQueue count] > 0 &&
90
- [[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground) {
91
- RNBootSplashTask *task = [_taskQueue objectAtIndex:0];
92
- [_taskQueue removeObjectAtIndex:0];
93
-
94
- [self hideWithTask:task];
95
- }
61
+ + (bool)isHidden {
62
+ return _rootView == nil || _rootView.loadingView == nil || [_rootView.loadingView isHidden];
96
63
  }
97
64
 
98
- + (void)hideWithTask:(RNBootSplashTask *)task {
99
- if (_status == RNBootSplashStatusHidden) {
100
- task.resolve(@(true));
101
- return [self shiftNextTask];
102
- }
103
-
104
- if (!task.fade) {
105
- _status = RNBootSplashStatusHidden;
106
-
65
+ + (void)removeLoadingView {
66
+ if (![self isHidden]) {
107
67
  _rootView.loadingView.hidden = YES;
68
+
108
69
  [_rootView.loadingView removeFromSuperview];
109
70
  _rootView.loadingView = nil;
110
-
111
- task.resolve(@(true));
112
- return [self shiftNextTask];
113
71
  }
72
+ }
114
73
 
115
- _status = RNBootSplashStatusTransitioning;
116
-
117
- [UIView transitionWithView:_rootView
118
- duration:0.220
119
- options:UIViewAnimationOptionTransitionCrossDissolve
120
- animations:^{
121
- _rootView.loadingView.hidden = YES;
122
- }
123
- completion:^(__unused BOOL finished) {
124
- _status = RNBootSplashStatusHidden;
125
-
126
- [_rootView.loadingView removeFromSuperview];
127
- _rootView.loadingView = nil;
74
+ - (void)clearResolverQueue {
75
+ while ([_resolverQueue count] > 0) {
76
+ RCTPromiseResolveBlock resolve = [_resolverQueue objectAtIndex:0];
77
+ [_resolverQueue removeObjectAtIndex:0];
128
78
 
129
- task.resolve(@(true));
130
- return [self shiftNextTask];
131
- }];
79
+ resolve(@(true));
80
+ }
132
81
  }
133
82
 
134
83
  RCT_REMAP_METHOD(hide,
135
84
  hideWithFade:(BOOL)fade
136
85
  resolver:(RCTPromiseResolveBlock)resolve
137
86
  rejecter:(RCTPromiseRejectBlock)reject) {
138
- if (_rootView == nil || _status == RNBootSplashStatusHidden)
139
- return resolve(@(true));
87
+ if (_resolverQueue == nil)
88
+ _resolverQueue = [[NSMutableArray alloc] init];
89
+
90
+ [_resolverQueue addObject:resolve];
91
+
92
+ if ([RNBootSplash isHidden] || RCTRunningInAppExtension())
93
+ return [self clearResolverQueue];
140
94
 
141
- RNBootSplashTask *task = [[RNBootSplashTask alloc] initWithFade:fade
142
- resolver:resolve];
95
+ if (!fade) {
96
+ [RNBootSplash removeLoadingView];
97
+ return [self clearResolverQueue];
98
+ }
99
+
100
+ dispatch_async(dispatch_get_main_queue(), ^{
101
+ _isTransitioning = true;
102
+
103
+ [UIView transitionWithView:_rootView
104
+ duration:0.220
105
+ options:UIViewAnimationOptionTransitionCrossDissolve
106
+ animations:^{
107
+ _rootView.loadingView.hidden = YES;
108
+ }
109
+ completion:^(__unused BOOL finished) {
110
+ [_rootView.loadingView removeFromSuperview];
111
+ _rootView.loadingView = nil;
143
112
 
144
- [_taskQueue addObject:task];
145
- [RNBootSplash shiftNextTask];
113
+ _isTransitioning = false;
114
+
115
+ return [self clearResolverQueue];
116
+ }];
117
+ });
146
118
  }
147
119
 
148
120
  RCT_REMAP_METHOD(getVisibilityStatus,
149
121
  getVisibilityStatusWithResolver:(RCTPromiseResolveBlock)resolve
150
122
  rejecter:(RCTPromiseRejectBlock)reject) {
151
- switch (_status) {
152
- case RNBootSplashStatusVisible:
153
- return resolve(@"visible");
154
- case RNBootSplashStatusHidden:
155
- return resolve(@"hidden");
156
- case RNBootSplashStatusTransitioning:
157
- return resolve(@"transitioning");
158
- }
123
+ if ([RNBootSplash isHidden])
124
+ return resolve(@"hidden");
125
+ else if (_isTransitioning)
126
+ return resolve(@"transitioning");
127
+ else
128
+ return resolve(@"visible");
159
129
  }
160
130
 
161
131
  @end
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-bootsplash",
3
- "version": "4.0.2",
3
+ "version": "4.1.3",
4
4
  "license": "MIT",
5
5
  "description": "Display a bootsplash on your app starts. Hide it when you want.",
6
6
  "author": "Mathieu Acthernoene <zoontek@gmail.com>",
@@ -65,14 +65,14 @@
65
65
  },
66
66
  "devDependencies": {
67
67
  "@types/fs-extra": "^9.0.13",
68
- "@types/react-native": "^0.66.4",
69
- "lint-staged": "^12.0.2",
70
- "prettier": "^2.4.1",
68
+ "@types/react-native": "^0.66.15",
69
+ "lint-staged": "^12.3.3",
70
+ "prettier": "^2.5.1",
71
71
  "prettier-plugin-organize-imports": "^2.3.4",
72
72
  "react": "^17.0.2",
73
- "react-native": "^0.66.3",
73
+ "react-native": "^0.67.2",
74
74
  "react-native-builder-bob": "^0.18.2",
75
75
  "rescript": "^9.1.4",
76
- "typescript": "^4.4.4"
76
+ "typescript": "^4.5.5"
77
77
  }
78
78
  }
@@ -1,25 +0,0 @@
1
- package com.zoontek.rnbootsplash;
2
-
3
- import androidx.annotation.NonNull;
4
-
5
- import com.facebook.react.bridge.Promise;
6
-
7
- public class RNBootSplashTask {
8
-
9
- private final boolean mFade;
10
- @NonNull private final Promise mPromise;
11
-
12
- public RNBootSplashTask(final boolean fade, @NonNull final Promise promise) {
13
- mFade = fade;
14
- mPromise = promise;
15
- }
16
-
17
- public boolean getFade() {
18
- return mFade;
19
- }
20
-
21
- @NonNull
22
- public Promise getPromise() {
23
- return mPromise;
24
- }
25
- }