Skip to content

Commit fc2c3ad

Browse files
authored
Merge pull request #6 from readwiseio/readwise/v13.16.0
Readwise/v13.16.0
2 parents 5bc526f + e561418 commit fc2c3ad

10 files changed

Lines changed: 179 additions & 1 deletion

apple/RNCAssetSchemeHandler.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#import <WebKit/WebKit.h>
2+
3+
@interface RNCAssetSchemeHandler : NSObject <WKURLSchemeHandler>
4+
@end

apple/RNCAssetSchemeHandler.m

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#import "RNCAssetSchemeHandler.h"
2+
3+
@implementation RNCAssetSchemeHandler
4+
5+
- (void)webView:(WKWebView *)webView startURLSchemeTask:(id<WKURLSchemeTask>)urlSchemeTask {
6+
NSURL *url = urlSchemeTask.request.URL;
7+
NSString *path = url.path;
8+
9+
// Strip leading slash from path to get the filename
10+
if ([path hasPrefix:@"/"]) {
11+
path = [path substringFromIndex:1];
12+
}
13+
14+
// URL-decode the path (handles brackets and other special chars)
15+
NSString *fileName = [path stringByRemovingPercentEncoding];
16+
if (!fileName || fileName.length == 0) {
17+
NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil];
18+
[urlSchemeTask didFailWithError:error];
19+
return;
20+
}
21+
22+
// Find the file in the app bundle
23+
NSString *bundleResourcePath = [[NSBundle mainBundle] resourcePath];
24+
NSString *fullPath = [bundleResourcePath stringByAppendingPathComponent:fileName];
25+
26+
if (![[NSFileManager defaultManager] fileExistsAtPath:fullPath]) {
27+
NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil];
28+
[urlSchemeTask didFailWithError:error];
29+
return;
30+
}
31+
32+
NSData *data = [NSData dataWithContentsOfFile:fullPath];
33+
if (!data) {
34+
NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCannotOpenFile userInfo:nil];
35+
[urlSchemeTask didFailWithError:error];
36+
return;
37+
}
38+
39+
// Determine MIME type from extension
40+
NSString *mimeType = @"application/octet-stream";
41+
NSString *ext = [fileName pathExtension];
42+
if ([ext isEqualToString:@"woff2"]) {
43+
mimeType = @"font/woff2";
44+
} else if ([ext isEqualToString:@"ttf"]) {
45+
mimeType = @"font/ttf";
46+
} else if ([ext isEqualToString:@"otf"]) {
47+
mimeType = @"font/otf";
48+
} else if ([ext isEqualToString:@"woff"]) {
49+
mimeType = @"font/woff";
50+
}
51+
52+
NSURLResponse *response = [[NSURLResponse alloc] initWithURL:url
53+
MIMEType:mimeType
54+
expectedContentLength:data.length
55+
textEncodingName:nil];
56+
57+
[urlSchemeTask didReceiveResponse:response];
58+
[urlSchemeTask didReceiveData:data];
59+
[urlSchemeTask didFinish];
60+
}
61+
62+
- (void)webView:(WKWebView *)webView stopURLSchemeTask:(id<WKURLSchemeTask>)urlSchemeTask {
63+
// Requests complete synchronously, nothing to cancel
64+
}
65+
66+
@end

apple/RNCWebView.mm

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,8 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &
312312
REMAP_WEBVIEW_PROP(showsHorizontalScrollIndicator)
313313
REMAP_WEBVIEW_PROP(showsVerticalScrollIndicator)
314314
REMAP_WEBVIEW_PROP(keyboardDisplayRequiresUserAction)
315+
REMAP_WEBVIEW_PROP(scrollsToTop)
316+
REMAP_WEBVIEW_PROP(dragInteractionEnabled)
315317

316318
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 /* __IPHONE_13_0 */
317319
REMAP_WEBVIEW_PROP(automaticallyAdjustContentInsets)
@@ -551,5 +553,13 @@ - (void)clearHistory {
551553
// android only
552554
}
553555

556+
- (void)setTintColor:(double)red green:(double)green blue:(double)blue alpha:(double)alpha {
557+
UIColor *color = [UIColor colorWithRed:red / 255.0
558+
green:green / 255.0
559+
blue:blue / 255.0
560+
alpha:alpha];
561+
[_view setTintColor:color];
562+
}
563+
554564
@end
555565
#endif

apple/RNCWebViewImpl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ shouldStartLoadForRequest:(NSMutableDictionary<NSString *, id> *)request
110110
@property (nonatomic, assign) BOOL pullToRefreshEnabled;
111111
@property (nonatomic, assign) BOOL refreshControlLightMode;
112112
@property (nonatomic, assign) BOOL enableApplePay;
113+
@property (nonatomic, assign) BOOL scrollsToTop;
114+
@property (nonatomic, assign) BOOL dragInteractionEnabled;
113115
@property (nonatomic, copy) NSArray<NSDictionary *> * _Nullable menuItems;
114116
@property (nonatomic, copy) NSArray<NSString *> * _Nullable suppressMenuItems;
115117
@property (nonatomic, copy) RCTDirectEventBlock onCustomMenuSelection;
@@ -149,6 +151,7 @@ shouldStartLoadForRequest:(NSMutableDictionary<NSString *, id> *)request
149151
- (void)stopLoading;
150152
- (void)requestFocus;
151153
- (void)clearCache:(BOOL)includeDiskFiles;
154+
- (void)setTintColor:(UIColor *)tintColor;
152155
#ifdef RCT_NEW_ARCH_ENABLED
153156
- (void)destroyWebView;
154157
#endif

apple/RNCWebViewImpl.m

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77

88
#import "RNCWebViewImpl.h"
9+
#import "RNCAssetSchemeHandler.h"
910
#import <React/RCTConvert.h>
1011
#import <React/RCTAutoInsetsProtocol.h>
1112
#import "RNCWKProcessPoolManager.h"
@@ -181,6 +182,8 @@ - (instancetype)initWithFrame:(CGRect)frame
181182
_injectedJavaScriptBeforeContentLoaded = nil;
182183
_injectedJavaScriptBeforeContentLoadedForMainFrameOnly = YES;
183184
_enableApplePay = NO;
185+
_scrollsToTop = YES;
186+
_dragInteractionEnabled = YES;
184187
#if TARGET_OS_IOS
185188
_savedStatusBarStyle = RCTSharedApplication().statusBarStyle;
186189
_savedStatusBarHidden = RCTSharedApplication().statusBarHidden;
@@ -509,6 +512,11 @@ - (WKWebViewConfiguration *)setUpWkWebViewConfig
509512
wkWebViewConfig.applicationNameForUserAgent = [NSString stringWithFormat:@"%@ %@", wkWebViewConfig.applicationNameForUserAgent, _applicationNameForUserAgent];
510513
}
511514

515+
// Register custom URL scheme handler to serve app bundle assets (fonts, etc.)
516+
// from WKWebView pages loaded with about:blank origin, which can't access file:// URLs.
517+
RNCAssetSchemeHandler *assetHandler = [[RNCAssetSchemeHandler alloc] init];
518+
[wkWebViewConfig setURLSchemeHandler:assetHandler forURLScheme:@"rw-asset"];
519+
512520
return wkWebViewConfig;
513521
}
514522

@@ -545,6 +553,7 @@ - (void)didMoveToWindow
545553
}
546554

547555
_webView.scrollView.directionalLockEnabled = _directionalLockEnabled;
556+
_webView.scrollView.scrollsToTop = _scrollsToTop;
548557
#endif // !TARGET_OS_OSX
549558
_webView.allowsLinkPreview = _allowsLinkPreview;
550559
[_webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
@@ -740,6 +749,11 @@ - (void)setBackgroundColor:(RCTUIColor *)backgroundColor
740749
#endif // !TARGET_OS_OSX
741750
}
742751

752+
- (void)setTintColor:(UIColor *)tintColor
753+
{
754+
_webView.tintColor = tintColor;
755+
}
756+
743757
#if !TARGET_OS_OSX
744758
- (void)setContentInsetAdjustmentBehavior:(UIScrollViewContentInsetAdjustmentBehavior)behavior
745759
{
@@ -1106,6 +1120,25 @@ - (void)setIndicatorStyle:(NSString *)indicatorStyle
11061120
_webView.scrollView.indicatorStyle = UIScrollViewIndicatorStyleDefault;
11071121
}
11081122
}
1123+
1124+
- (void)setScrollsToTop:(BOOL)scrollsToTop
1125+
{
1126+
_scrollsToTop = scrollsToTop;
1127+
_webView.scrollView.scrollsToTop = scrollsToTop;
1128+
}
1129+
1130+
- (void)setDragInteractionEnabled:(BOOL)dragInteractionEnabled
1131+
{
1132+
_dragInteractionEnabled = dragInteractionEnabled;
1133+
if (@available(iOS 11.0, *)) {
1134+
for (id interaction in _webView.scrollView.interactions) {
1135+
if ([interaction isKindOfClass:[UIDragInteraction class]]) {
1136+
((UIDragInteraction *)interaction).enabled = dragInteractionEnabled;
1137+
}
1138+
}
1139+
}
1140+
}
1141+
11091142
#endif // !TARGET_OS_OSX
11101143

11111144
- (void)postMessage:(NSString *)message

apple/RNCWebViewManager.mm

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,14 @@ - (RNCView *)view
175175
view.keyboardDisplayRequiresUserAction = json == nil ? true : [RCTConvert BOOL: json];
176176
}
177177

178+
RCT_CUSTOM_VIEW_PROPERTY(scrollsToTop, BOOL, RNCWebViewImpl) {
179+
view.scrollsToTop = json == nil ? true : [RCTConvert BOOL: json];
180+
}
181+
182+
RCT_CUSTOM_VIEW_PROPERTY(dragInteractionEnabled, BOOL, RNCWebViewImpl) {
183+
view.dragInteractionEnabled = json == nil ? true : [RCTConvert BOOL: json];
184+
}
185+
178186
#if !TARGET_OS_OSX
179187
#define BASE_VIEW_PER_OS() UIView
180188
#else
@@ -216,4 +224,20 @@ - (RNCView *)view
216224
QUICK_RCT_EXPORT_COMMAND_METHOD_PARAMS(injectJavaScript, script:(NSString *)script, script)
217225
QUICK_RCT_EXPORT_COMMAND_METHOD_PARAMS(clearCache, includeDiskFiles:(BOOL)includeDiskFiles, includeDiskFiles)
218226

227+
RCT_EXPORT_METHOD(setTintColor:(nonnull NSNumber *)reactTag red:(double)red green:(double)green blue:(double)blue alpha:(double)alpha)
228+
{
229+
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, BASE_VIEW_PER_OS() *> *viewRegistry) {
230+
RNCWebViewImpl *view = (RNCWebViewImpl *)viewRegistry[reactTag];
231+
if (![view isKindOfClass:[RNCWebViewImpl class]]) {
232+
RCTLogError(@"Invalid view returned from registry, expecting RNCWebView, got: %@", view);
233+
} else {
234+
UIColor *color = [UIColor colorWithRed:red / 255.0
235+
green:green / 255.0
236+
blue:blue / 255.0
237+
alpha:alpha];
238+
[view setTintColor:color];
239+
}
240+
}];
241+
}
242+
219243
@end

index.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ declare class WebView<P = {}> extends Component<WebViewProps & P> {
5858
* Tells this WebView to clear its internal back/forward list.
5959
*/
6060
clearHistory?: () => void;
61+
62+
/**
63+
* (iOS only)
64+
* Sets the tint color (selection color) of the WebView.
65+
*/
66+
setTintColor: (red: number, green: number, blue: number, alpha: number) => void;
6167
}
6268

6369
export {WebView};

src/RNCWebViewNativeComponent.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,9 @@ export interface NativeProps extends ViewProps {
234234
pullToRefreshEnabled?: boolean;
235235
refreshControlLightMode?: boolean;
236236
scrollEnabled?: WithDefault<boolean, true>;
237+
scrollsToTop?: WithDefault<boolean, true>;
237238
sharedCookiesEnabled?: boolean;
239+
dragInteractionEnabled?: WithDefault<boolean, true>;
238240
textInteractionEnabled?: WithDefault<boolean, true>;
239241
useSharedProcessPool?: WithDefault<boolean, true>;
240242
onContentProcessDidTerminate?: DirectEventHandler<WebViewNativeEvent>;
@@ -325,6 +327,15 @@ export interface NativeCommands {
325327
) => void;
326328
clearHistory: (viewRef: React.ElementRef<HostComponent<NativeProps>>) => void;
327329
// !Android Only
330+
331+
// iOS Only (Readwise custom)
332+
setTintColor: (
333+
viewRef: React.ElementRef<HostComponent<NativeProps>>,
334+
red: Double,
335+
green: Double,
336+
blue: Double,
337+
alpha: Double
338+
) => void;
328339
}
329340

330341
export const Commands = codegenNativeCommands<NativeCommands>({
@@ -340,6 +351,7 @@ export const Commands = codegenNativeCommands<NativeCommands>({
340351
'clearFormData',
341352
'clearCache',
342353
'clearHistory',
354+
'setTintColor',
343355
],
344356
});
345357

src/WebView.ios.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,9 @@ const WebViewComponent = forwardRef<{}, IOSWebViewProps>(
159159
clearCache: (includeDiskFiles: boolean) =>
160160
webViewRef.current &&
161161
Commands.clearCache(webViewRef.current, includeDiskFiles),
162+
setTintColor: (red: number, green: number, blue: number, alpha: number) =>
163+
webViewRef.current &&
164+
Commands.setTintColor(webViewRef.current, red, green, blue, alpha),
162165
}),
163166
[setViewState, webViewRef]
164167
);

src/WebViewTypes.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ type WebViewCommands =
2424

2525
type AndroidWebViewCommands = 'clearHistory' | 'clearFormData';
2626

27+
type IOSWebViewCommands = 'setTintColor';
28+
2729
interface RNCWebViewUIManager<Commands extends string> extends UIManagerStatic {
2830
getViewManagerConfig: (name: string) => {
2931
Commands: { [key in Commands]: number };
@@ -33,7 +35,7 @@ interface RNCWebViewUIManager<Commands extends string> extends UIManagerStatic {
3335
export type RNCWebViewUIManagerAndroid = RNCWebViewUIManager<
3436
WebViewCommands | AndroidWebViewCommands
3537
>;
36-
export type RNCWebViewUIManagerIOS = RNCWebViewUIManager<WebViewCommands>;
38+
export type RNCWebViewUIManagerIOS = RNCWebViewUIManager<WebViewCommands | IOSWebViewCommands>;
3739
export type RNCWebViewUIManagerMacOS = RNCWebViewUIManager<WebViewCommands>;
3840
export type RNCWebViewUIManagerWindows = RNCWebViewUIManager<WebViewCommands>;
3941

@@ -777,6 +779,21 @@ export interface IOSWebViewProps extends WebViewSharedProps {
777779
* @platform ios
778780
*/
779781
fraudulentWebsiteWarningEnabled?: boolean;
782+
783+
/**
784+
* A Boolean value that controls whether the web view scrolls to the top of
785+
* the content when the user taps the status bar.
786+
* The default value is `true`.
787+
* @platform ios
788+
*/
789+
scrollsToTop?: boolean;
790+
791+
/**
792+
* A Boolean value that determines whether drag interactions are enabled
793+
* on the web view. The default value is `true`.
794+
* @platform ios
795+
*/
796+
dragInteractionEnabled?: boolean;
780797
}
781798

782799
export interface MacOSWebViewProps extends WebViewSharedProps {

0 commit comments

Comments
 (0)