mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-23 20:01:01 +08:00
Initial commit
This commit is contained in:
9
ReactKit/Modules/RCTAlertManager.h
Normal file
9
ReactKit/Modules/RCTAlertManager.h
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTExport.h"
|
||||
|
||||
@interface RCTAlertManager : NSObject <RCTNativeModule>
|
||||
|
||||
@end
|
||||
107
ReactKit/Modules/RCTAlertManager.m
Normal file
107
ReactKit/Modules/RCTAlertManager.m
Normal file
@@ -0,0 +1,107 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTAlertManager.h"
|
||||
|
||||
#import "RCTLog.h"
|
||||
|
||||
@interface RCTAlertManager() <UIAlertViewDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTAlertManager
|
||||
{
|
||||
NSMutableArray *_alerts;
|
||||
NSMutableArray *_alertCallbacks;
|
||||
NSMutableArray *_alertButtonKeys;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_alerts = [[NSMutableArray alloc] init];
|
||||
_alertCallbacks = [[NSMutableArray alloc] init];
|
||||
_alertButtonKeys = [[NSMutableArray alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {NSDictionary} args Dictionary of the form
|
||||
*
|
||||
* @{
|
||||
* @"message": @"<Alert message>",
|
||||
* @"buttons": @[
|
||||
* @{@"<key1>": @"<title1>"},
|
||||
* @{@"<key2>": @"<cancelButtonTitle>"},
|
||||
* ]
|
||||
* }
|
||||
* The key from the `buttons` dictionary is passed back in the callback on click.
|
||||
* Buttons are displayed in the order they are specified. If "cancel" is used as
|
||||
* the button key, it will be differently highlighted, according to iOS UI conventions.
|
||||
*/
|
||||
- (void)alertWithArgs:(NSDictionary *)args callback:(RCTResponseSenderBlock)callback
|
||||
{
|
||||
RCT_EXPORT();
|
||||
|
||||
NSString *title = args[@"title"];
|
||||
NSString *message = args[@"message"];
|
||||
NSArray *buttons = args[@"buttons"];
|
||||
|
||||
if (!title && !message) {
|
||||
RCTLogMustFix(@"Must specify either an alert title, or message, or both");
|
||||
return;
|
||||
} else if (buttons.count == 0) {
|
||||
RCTLogMustFix(@"Must have at least one button.");
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:title
|
||||
message:message
|
||||
delegate:self
|
||||
cancelButtonTitle:nil
|
||||
otherButtonTitles:nil];
|
||||
|
||||
NSMutableArray *buttonKeys = [[NSMutableArray alloc] initWithCapacity:buttons.count];
|
||||
|
||||
NSInteger index = 0;
|
||||
for (NSDictionary *button in buttons) {
|
||||
if (button.count != 1) {
|
||||
RCTLogMustFix(@"Button definitions should have exactly one key.");
|
||||
}
|
||||
NSString *buttonKey = [button.allKeys firstObject];
|
||||
NSString *buttonTitle = [button[buttonKey] description];
|
||||
[alertView addButtonWithTitle:buttonTitle];
|
||||
if ([buttonKey isEqualToString: @"cancel"]) {
|
||||
alertView.cancelButtonIndex = index;
|
||||
}
|
||||
[buttonKeys addObject:buttonKey];
|
||||
index ++;
|
||||
}
|
||||
|
||||
[_alerts addObject:alertView];
|
||||
[_alertCallbacks addObject:callback ?: ^(id unused) {}];
|
||||
[_alertButtonKeys addObject:buttonKeys];
|
||||
|
||||
[alertView show];
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - UIAlertViewDelegate
|
||||
|
||||
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
|
||||
{
|
||||
NSUInteger index = [_alerts indexOfObject:alertView];
|
||||
RCTAssert(index != NSNotFound, @"Dismissed alert was not recognised");
|
||||
|
||||
RCTResponseSenderBlock callback = _alertCallbacks[index];
|
||||
NSArray *buttonKeys = _alertButtonKeys[index];
|
||||
callback(@[buttonKeys[buttonIndex]]);
|
||||
|
||||
[_alerts removeObjectAtIndex:index];
|
||||
[_alertCallbacks removeObjectAtIndex:index];
|
||||
[_alertButtonKeys removeObjectAtIndex:index];
|
||||
}
|
||||
|
||||
@end
|
||||
10
ReactKit/Modules/RCTDataManager.h
Normal file
10
ReactKit/Modules/RCTDataManager.h
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "RCTExport.h"
|
||||
|
||||
@interface RCTDataManager : NSObject <RCTNativeModule>
|
||||
|
||||
@end
|
||||
|
||||
73
ReactKit/Modules/RCTDataManager.m
Normal file
73
ReactKit/Modules/RCTDataManager.m
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTDataManager.h"
|
||||
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
@implementation RCTDataManager
|
||||
|
||||
/**
|
||||
* Executes a network request.
|
||||
* The responseSender block won't be called on same thread as called.
|
||||
*/
|
||||
- (void)executeQuery:(NSString *)queryType
|
||||
query:(id)query
|
||||
queryHash:(__unused NSString *)queryHash
|
||||
responseSender:(RCTResponseSenderBlock)responseSender
|
||||
{
|
||||
RCT_EXPORT(queryData);
|
||||
|
||||
if ([queryType isEqualToString:@"http"]) {
|
||||
|
||||
// Parse query
|
||||
NSDictionary *queryDict = query;
|
||||
if ([query isKindOfClass:[NSString class]]) {
|
||||
// TODO: it would be more efficient just to send a dictionary
|
||||
queryDict = RCTJSONParse(query, NULL);
|
||||
}
|
||||
|
||||
// Build request
|
||||
NSURL *url = [NSURL URLWithString:queryDict[@"url"]];
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
|
||||
request.HTTPMethod = queryDict[@"method"] ?: @"GET";
|
||||
request.allHTTPHeaderFields = queryDict[@"headers"];
|
||||
if ([queryDict[@"data"] isKindOfClass:[NSString class]]) {
|
||||
request.HTTPBody = [queryDict[@"data"] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
// Build data task
|
||||
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *connectionError) {
|
||||
|
||||
// Build response
|
||||
NSDictionary *responseJSON;
|
||||
if (connectionError == nil) {
|
||||
NSStringEncoding encoding;
|
||||
if (response.textEncodingName) {
|
||||
CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName);
|
||||
encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
|
||||
} else {
|
||||
encoding = NSUTF8StringEncoding;
|
||||
}
|
||||
NSString *returnData = [[NSString alloc] initWithData:data encoding:encoding];
|
||||
responseJSON = @{@"status": @200, @"responseText": returnData};
|
||||
} else {
|
||||
responseJSON = @{@"status": @0, @"responseText": [connectionError localizedDescription]};
|
||||
}
|
||||
|
||||
// Send response (won't be sent on same thread as caller)
|
||||
responseSender(@[RCTJSONStringify(responseJSON, NULL)]);
|
||||
|
||||
}];
|
||||
|
||||
[task resume];
|
||||
|
||||
} else {
|
||||
|
||||
RCTLogMustFix(@"unsupported query type %@", queryType);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
9
ReactKit/Modules/RCTExceptionsManager.h
Normal file
9
ReactKit/Modules/RCTExceptionsManager.h
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "RCTExport.h"
|
||||
|
||||
@interface RCTExceptionsManager : NSObject <RCTNativeModule>
|
||||
|
||||
@end
|
||||
23
ReactKit/Modules/RCTExceptionsManager.m
Normal file
23
ReactKit/Modules/RCTExceptionsManager.m
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTExceptionsManager.h"
|
||||
|
||||
#import "RCTRedBox.h"
|
||||
|
||||
@implementation RCTExceptionsManager
|
||||
|
||||
- (void)reportUnhandledExceptionWithMessage:(NSString *)message stack:(NSArray *)stack
|
||||
{
|
||||
RCT_EXPORT(reportUnhandledException);
|
||||
|
||||
[[RCTRedBox sharedInstance] showErrorMessage:message withStack:stack];
|
||||
}
|
||||
|
||||
- (void)updateExceptionMessage:(NSString *)message stack:(NSArray *)stack
|
||||
{
|
||||
RCT_EXPORT(updateExceptionMessage);
|
||||
|
||||
[[RCTRedBox sharedInstance] updateErrorMessage:message withStack:stack];
|
||||
}
|
||||
|
||||
@end
|
||||
14
ReactKit/Modules/RCTTiming.h
Normal file
14
ReactKit/Modules/RCTTiming.h
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "RCTExport.h"
|
||||
|
||||
@class RCTBridge;
|
||||
|
||||
@interface RCTTiming : NSObject <RCTNativeModule>
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||
- (void)enqueueUpdateTimers;
|
||||
|
||||
@end
|
||||
154
ReactKit/Modules/RCTTiming.m
Normal file
154
ReactKit/Modules/RCTTiming.m
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTTiming.h"
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTModuleIDs.h"
|
||||
#import "RCTSparseArray.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
@interface RCTTimer : NSObject
|
||||
|
||||
@property (nonatomic, strong, readonly) NSDate *target;
|
||||
@property (nonatomic, assign, readonly, getter=isActive) BOOL active;
|
||||
@property (nonatomic, assign, readonly) BOOL repeats;
|
||||
@property (nonatomic, strong, readonly) NSNumber *callbackID;
|
||||
@property (nonatomic, assign, readonly) NSTimeInterval interval;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTTimer
|
||||
|
||||
- (instancetype)initWithCallbackID:(NSNumber *)callbackID
|
||||
interval:(NSTimeInterval)interval
|
||||
targetTime:(NSTimeInterval)targetTime
|
||||
repeats:(BOOL)repeats
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_active = YES;
|
||||
_interval = interval;
|
||||
_repeats = repeats;
|
||||
_callbackID = callbackID;
|
||||
_target = [NSDate dateWithTimeIntervalSinceNow:targetTime];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `YES` if we should invoke the JS callback.
|
||||
*/
|
||||
- (BOOL)updateFoundNeedsJSUpdate
|
||||
{
|
||||
if (_active && _target.timeIntervalSinceNow <= 0) {
|
||||
// The JS Timers will do fine grained calculating of expired timeouts.
|
||||
if (_repeats) {
|
||||
_target = [NSDate dateWithTimeIntervalSinceNow:_interval];
|
||||
} else {
|
||||
_active = NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTTiming
|
||||
{
|
||||
RCTSparseArray *_timers;
|
||||
RCTBridge *_bridge;
|
||||
}
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_bridge = bridge;
|
||||
_timers = [[RCTSparseArray alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO (#5906496): Wait until operations on `javaScriptQueue` are complete to complete the
|
||||
* `dealloc`.
|
||||
*/
|
||||
/* - (void)dealloc
|
||||
{
|
||||
} */
|
||||
|
||||
- (void)enqueueUpdateTimers
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
NSMutableArray *timersToCall = [[NSMutableArray alloc] init];
|
||||
for (RCTTimer *timer in _timers.allObjects) {
|
||||
if ([timer updateFoundNeedsJSUpdate]) {
|
||||
[timersToCall addObject:timer.callbackID];
|
||||
}
|
||||
if (!timer.active) {
|
||||
_timers[timer.callbackID] = nil;
|
||||
}
|
||||
}
|
||||
|
||||
// call timers that need to be called
|
||||
if ([timersToCall count] > 0) {
|
||||
[_bridge enqueueJSCall:RCTModuleIDJSTimers methodID:RCTJSTimersCallTimers args:@[timersToCall]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scheduleCallbackID:(NSNumber *)callbackID interval:(NSTimeInterval)interval targetTime:(NSTimeInterval)targetTime repeats:(BOOL)repeats
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
RCTTimer *timer = [[RCTTimer alloc] initWithCallbackID:callbackID interval:interval targetTime:targetTime repeats:repeats];
|
||||
_timers[callbackID] = timer;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* There's a small difference between the time when we call
|
||||
* setTimeout/setInterval/requestAnimation frame and the time it actually makes
|
||||
* it here. This is important and needs to be taken into account when
|
||||
* calculating the timer's target time. We calculate this by passing in
|
||||
* Date.now() from JS and then subtracting that from the current time here.
|
||||
*/
|
||||
- (void)createTimer:(NSNumber *)callbackID
|
||||
duration:(NSNumber *)jsDuration
|
||||
jsSchedulingTime:(NSNumber *)jsSchedulingTime
|
||||
repeats:(NSNumber *)repeats
|
||||
{
|
||||
RCT_EXPORT();
|
||||
|
||||
NSTimeInterval interval = jsDuration.doubleValue / 1000;
|
||||
NSTimeInterval jsCreationTimeSinceUnixEpoch = jsSchedulingTime.doubleValue / 1000;
|
||||
NSTimeInterval currentTimeSinceUnixEpoch = [[NSDate date] timeIntervalSince1970];
|
||||
NSTimeInterval jsSchedulingOverhead = currentTimeSinceUnixEpoch - jsCreationTimeSinceUnixEpoch;
|
||||
if (jsSchedulingOverhead < 0) {
|
||||
RCTLogWarn(@"jsSchedulingOverhead (%ims) should be positive", (int)(jsSchedulingOverhead * 1000));
|
||||
}
|
||||
|
||||
NSTimeInterval targetTime = interval - jsSchedulingOverhead;
|
||||
if (interval < 0.018) { // Make sure short intervals run each frame
|
||||
interval = 0;
|
||||
}
|
||||
|
||||
[self scheduleCallbackID:callbackID
|
||||
interval:interval
|
||||
targetTime:targetTime
|
||||
repeats:repeats.boolValue];
|
||||
}
|
||||
|
||||
- (void)deleteTimer:(NSNumber *)timerID
|
||||
{
|
||||
RCT_EXPORT();
|
||||
|
||||
if (timerID) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
_timers[timerID] = nil;
|
||||
});
|
||||
} else {
|
||||
RCTLogWarn(@"Called deleteTimer: with a nil timerID");
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
40
ReactKit/Modules/RCTUIManager.h
Normal file
40
ReactKit/Modules/RCTUIManager.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTExport.h"
|
||||
#import "RCTInvalidating.h"
|
||||
|
||||
@protocol RCTScrollableProtocol;
|
||||
@protocol RCTViewNodeProtocol;
|
||||
|
||||
@class RCTRootView;
|
||||
@class RCTJavaScriptEventDispatcher;
|
||||
@class RCTShadowView;
|
||||
@class RCTAnimationRegistry;
|
||||
|
||||
@interface RCTUIManager : NSObject <RCTInvalidating, RCTNativeModule>
|
||||
|
||||
- (instancetype)initWithShadowQueue:(dispatch_queue_t)shadowQueue
|
||||
viewManagers:(NSDictionary *)viewManagers;
|
||||
|
||||
@property (nonatomic, strong) RCTJavaScriptEventDispatcher *eventDispatcher;
|
||||
@property (nonatomic, strong) RCTSparseArray *shadowViewRegistry;
|
||||
@property (nonatomic, strong) RCTSparseArray *viewRegistry;
|
||||
@property (nonatomic, strong) RCTAnimationRegistry *animationRegistry;
|
||||
@property (nonatomic, weak) id<RCTScrollableProtocol> mainScrollView;
|
||||
|
||||
/**
|
||||
* Allows native environment code to respond to "the main scroll view" events.
|
||||
* see `RCTUIManager`'s `setMainScrollViewTag`.
|
||||
*/
|
||||
@property (nonatomic, readwrite, weak) id<UIScrollViewDelegate> nativeMainScrollDelegate;
|
||||
|
||||
+ (UIView <RCTViewNodeProtocol> *)closestReactAncestor:(UIView *)view;
|
||||
+ (UIView <RCTViewNodeProtocol> *)closestReactAncestorThatRespondsToTouch:(UITouch *)touch;
|
||||
|
||||
- (void)registerRootView:(RCTRootView *)rootView;
|
||||
|
||||
+ (UIView *)JSResponder;
|
||||
|
||||
@end
|
||||
1190
ReactKit/Modules/RCTUIManager.m
Normal file
1190
ReactKit/Modules/RCTUIManager.m
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user