pulse-updates 1.2.5 → 1.3.1
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 +55 -4
- package/android/build.gradle +1 -0
- package/android/consumer-rules.pro +21 -0
- package/android/src/main/java/app/pulse/updates/PulseUpdatesPackage.kt +5 -1
- package/android/src/main/java/app/pulse/updates/attribution/DeferredHandoffReferrerParser.kt +47 -0
- package/android/src/main/java/app/pulse/updates/attribution/InstallReferrerReflection.kt +155 -0
- package/android/src/main/java/app/pulse/updates/attribution/PulseAttributionModule.kt +229 -0
- package/ios/PulseAttribution/PulseAttribution.mm +45 -0
- package/ios/PulseAttribution/PulseDeferredLinkPasteButtonComponentView.h +13 -0
- package/ios/PulseAttribution/PulseDeferredLinkPasteButtonComponentView.mm +81 -0
- package/ios/PulseAttribution/PulseDeferredLinkPasteButtonManager.m +28 -0
- package/ios/PulseAttribution/PulseDeferredLinkPasteView.h +21 -0
- package/ios/PulseAttribution/PulseDeferredLinkPasteView.m +231 -0
- package/lib/commonjs/DeferredLinkPasteButton.js +64 -0
- package/lib/commonjs/DeferredLinkPasteButton.js.map +1 -0
- package/lib/commonjs/NativePulseAttribution.js +13 -0
- package/lib/commonjs/NativePulseAttribution.js.map +1 -0
- package/lib/commonjs/PulseDeferredLinkPasteButtonNativeComponent.js +13 -0
- package/lib/commonjs/PulseDeferredLinkPasteButtonNativeComponent.js.map +1 -0
- package/lib/commonjs/index.js +33 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/links.js +944 -0
- package/lib/commonjs/links.js.map +1 -0
- package/lib/commonjs/nativeAttribution.js +29 -0
- package/lib/commonjs/nativeAttribution.js.map +1 -0
- package/lib/module/DeferredLinkPasteButton.js +56 -0
- package/lib/module/DeferredLinkPasteButton.js.map +1 -0
- package/lib/module/NativePulseAttribution.js +9 -0
- package/lib/module/NativePulseAttribution.js.map +1 -0
- package/lib/module/PulseDeferredLinkPasteButtonNativeComponent.js +7 -0
- package/lib/module/PulseDeferredLinkPasteButtonNativeComponent.js.map +1 -0
- package/lib/module/index.js +3 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/links.js +933 -0
- package/lib/module/links.js.map +1 -0
- package/lib/module/nativeAttribution.js +21 -0
- package/lib/module/nativeAttribution.js.map +1 -0
- package/lib/typescript/DeferredLinkPasteButton.d.ts +22 -0
- package/lib/typescript/DeferredLinkPasteButton.d.ts.map +1 -0
- package/lib/typescript/NativePulseAttribution.d.ts +11 -0
- package/lib/typescript/NativePulseAttribution.d.ts.map +1 -0
- package/lib/typescript/PulseDeferredLinkPasteButtonNativeComponent.d.ts +16 -0
- package/lib/typescript/PulseDeferredLinkPasteButtonNativeComponent.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +4 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/links.d.ts +142 -0
- package/lib/typescript/links.d.ts.map +1 -0
- package/lib/typescript/nativeAttribution.d.ts +9 -0
- package/lib/typescript/nativeAttribution.d.ts.map +1 -0
- package/package.json +17 -2
- package/pulse-updates.podspec +8 -2
- package/scripts/publish.mjs +5 -0
- package/src/DeferredLinkPasteButton.tsx +98 -0
- package/src/NativePulseAttribution.ts +12 -0
- package/src/PulseDeferredLinkPasteButtonNativeComponent.ts +18 -0
- package/src/index.ts +4 -0
- package/src/links.ts +1232 -0
- package/src/nativeAttribution.ts +25 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
2
|
+
|
|
3
|
+
#import "PulseDeferredLinkPasteButtonComponentView.h"
|
|
4
|
+
#import "PulseDeferredLinkPasteView.h"
|
|
5
|
+
|
|
6
|
+
#import <react/renderer/components/PulseAttributionSpec/ComponentDescriptors.h>
|
|
7
|
+
#import <react/renderer/components/PulseAttributionSpec/EventEmitters.h>
|
|
8
|
+
#import <react/renderer/components/PulseAttributionSpec/Props.h>
|
|
9
|
+
#import <react/renderer/components/PulseAttributionSpec/RCTComponentViewHelpers.h>
|
|
10
|
+
|
|
11
|
+
#import <React/RCTConversions.h>
|
|
12
|
+
|
|
13
|
+
using namespace facebook::react;
|
|
14
|
+
|
|
15
|
+
@interface PulseDeferredLinkPasteButtonComponentView ()
|
|
16
|
+
<RCTPulseDeferredLinkPasteButtonViewProtocol>
|
|
17
|
+
- (void)emitHandoff:(NSDictionary<NSString *, NSString *> *)body;
|
|
18
|
+
@end
|
|
19
|
+
|
|
20
|
+
@implementation PulseDeferredLinkPasteButtonComponentView {
|
|
21
|
+
PulseDeferredLinkPasteView *_pasteView;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
|
25
|
+
{
|
|
26
|
+
return concreteComponentDescriptorProvider<PulseDeferredLinkPasteButtonComponentDescriptor>();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
- (instancetype)initWithFrame:(CGRect)frame
|
|
30
|
+
{
|
|
31
|
+
if ((self = [super initWithFrame:frame])) {
|
|
32
|
+
static const auto defaultProps = std::make_shared<const PulseDeferredLinkPasteButtonProps>();
|
|
33
|
+
_props = defaultProps;
|
|
34
|
+
_pasteView = [[PulseDeferredLinkPasteView alloc] initWithFrame:self.bounds];
|
|
35
|
+
_pasteView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
36
|
+
__weak __typeof(self) weakSelf = self;
|
|
37
|
+
_pasteView.onHandoff = ^(NSDictionary<NSString *, NSString *> *body) {
|
|
38
|
+
[weakSelf emitHandoff:body];
|
|
39
|
+
};
|
|
40
|
+
self.contentView = _pasteView;
|
|
41
|
+
}
|
|
42
|
+
return self;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
- (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &)oldProps
|
|
46
|
+
{
|
|
47
|
+
const auto &oldViewProps =
|
|
48
|
+
*std::static_pointer_cast<const PulseDeferredLinkPasteButtonProps>(_props);
|
|
49
|
+
const auto &newViewProps =
|
|
50
|
+
*std::static_pointer_cast<const PulseDeferredLinkPasteButtonProps>(props);
|
|
51
|
+
if (oldViewProps.label != newViewProps.label) {
|
|
52
|
+
_pasteView.label = RCTNSStringFromStringNilIfEmpty(newViewProps.label);
|
|
53
|
+
}
|
|
54
|
+
if (oldViewProps.foregroundColor != newViewProps.foregroundColor) {
|
|
55
|
+
_pasteView.foregroundColor = RCTUIColorFromSharedColor(newViewProps.foregroundColor);
|
|
56
|
+
}
|
|
57
|
+
if (oldViewProps.handoffPrefix != newViewProps.handoffPrefix) {
|
|
58
|
+
_pasteView.handoffPrefix = RCTNSStringFromStringNilIfEmpty(newViewProps.handoffPrefix);
|
|
59
|
+
}
|
|
60
|
+
[super updateProps:props oldProps:oldProps];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
- (void)emitHandoff:(NSDictionary<NSString *, NSString *> *)body
|
|
64
|
+
{
|
|
65
|
+
if (!_eventEmitter) return;
|
|
66
|
+
NSString *token = body[@"token"];
|
|
67
|
+
if (![token isKindOfClass:NSString.class]) return;
|
|
68
|
+
PulseDeferredLinkPasteButtonEventEmitter::OnHandoff event = {};
|
|
69
|
+
event.token = std::string(token.UTF8String);
|
|
70
|
+
std::static_pointer_cast<const PulseDeferredLinkPasteButtonEventEmitter>(_eventEmitter)
|
|
71
|
+
->onHandoff(event);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@end
|
|
75
|
+
|
|
76
|
+
Class<RCTComponentViewProtocol> PulseDeferredLinkPasteButtonCls(void)
|
|
77
|
+
{
|
|
78
|
+
return PulseDeferredLinkPasteButtonComponentView.class;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
#endif
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#import <React/RCTComponent.h>
|
|
2
|
+
#import <React/RCTViewManager.h>
|
|
3
|
+
|
|
4
|
+
#import "PulseDeferredLinkPasteView.h"
|
|
5
|
+
|
|
6
|
+
@interface PulseDeferredLinkPasteButtonManager : RCTViewManager
|
|
7
|
+
@end
|
|
8
|
+
|
|
9
|
+
@implementation PulseDeferredLinkPasteButtonManager
|
|
10
|
+
|
|
11
|
+
RCT_EXPORT_MODULE(PulseDeferredLinkPasteButton)
|
|
12
|
+
|
|
13
|
+
+ (BOOL)requiresMainQueueSetup
|
|
14
|
+
{
|
|
15
|
+
return YES;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
- (UIView *)view
|
|
19
|
+
{
|
|
20
|
+
return [PulseDeferredLinkPasteView new];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
RCT_EXPORT_VIEW_PROPERTY(label, NSString)
|
|
24
|
+
RCT_EXPORT_VIEW_PROPERTY(foregroundColor, UIColor)
|
|
25
|
+
RCT_EXPORT_VIEW_PROPERTY(handoffPrefix, NSString)
|
|
26
|
+
RCT_EXPORT_VIEW_PROPERTY(onHandoff, RCTDirectEventBlock)
|
|
27
|
+
|
|
28
|
+
@end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#import <UIKit/UIKit.h>
|
|
2
|
+
|
|
3
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
4
|
+
|
|
5
|
+
typedef void (^PulseDeferredLinkHandoffHandler)(NSDictionary<NSString *, NSString *> *body);
|
|
6
|
+
|
|
7
|
+
/// Explicit-user-action clipboard boundary used by `DeferredLinkPasteButton`.
|
|
8
|
+
/// Raw clipboard contents never leave this view and are never retained or logged.
|
|
9
|
+
@interface PulseDeferredLinkPasteView : UIView <UIPasteConfigurationSupporting>
|
|
10
|
+
|
|
11
|
+
@property (nonatomic, copy, nullable) NSString *label;
|
|
12
|
+
@property (nonatomic, strong, nullable) UIColor *foregroundColor;
|
|
13
|
+
@property (nonatomic, copy, nullable) NSString *handoffPrefix;
|
|
14
|
+
@property (nonatomic, copy, nullable) PulseDeferredLinkHandoffHandler onHandoff;
|
|
15
|
+
|
|
16
|
+
+ (nullable NSString *)tokenFromHandoffString:(nullable NSString *)candidate
|
|
17
|
+
prefix:(nullable NSString *)prefix;
|
|
18
|
+
|
|
19
|
+
@end
|
|
20
|
+
|
|
21
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#import "PulseDeferredLinkPasteView.h"
|
|
2
|
+
|
|
3
|
+
static NSString *const PulseDefaultHandoffPrefix = @"encore_handoff:";
|
|
4
|
+
static NSUInteger const PulseMinimumTokenLength = 16;
|
|
5
|
+
static NSUInteger const PulseMaximumTokenLength = 512;
|
|
6
|
+
|
|
7
|
+
@interface PulseDeferredLinkPasteView () {
|
|
8
|
+
UIPasteConfiguration *_pasteConfiguration;
|
|
9
|
+
}
|
|
10
|
+
@property (nonatomic, strong) UILabel *titleLabel;
|
|
11
|
+
@property (nonatomic, strong) UIImageView *pasteIcon;
|
|
12
|
+
@property (nonatomic, strong) UIStackView *visualContent;
|
|
13
|
+
@property (nonatomic, strong) UIControl *actionControl;
|
|
14
|
+
@end
|
|
15
|
+
|
|
16
|
+
@implementation PulseDeferredLinkPasteView
|
|
17
|
+
|
|
18
|
+
@synthesize pasteConfiguration = _pasteConfiguration;
|
|
19
|
+
|
|
20
|
+
- (instancetype)initWithFrame:(CGRect)frame
|
|
21
|
+
{
|
|
22
|
+
if ((self = [super initWithFrame:frame])) {
|
|
23
|
+
[self setDefaultsAndBuild];
|
|
24
|
+
}
|
|
25
|
+
return self;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
- (instancetype)initWithCoder:(NSCoder *)coder
|
|
29
|
+
{
|
|
30
|
+
if ((self = [super initWithCoder:coder])) {
|
|
31
|
+
[self setDefaultsAndBuild];
|
|
32
|
+
}
|
|
33
|
+
return self;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
- (void)setDefaultsAndBuild
|
|
37
|
+
{
|
|
38
|
+
_label = @"Continue";
|
|
39
|
+
_foregroundColor = UIColor.whiteColor;
|
|
40
|
+
_handoffPrefix = PulseDefaultHandoffPrefix;
|
|
41
|
+
self.pasteConfiguration = [[UIPasteConfiguration alloc]
|
|
42
|
+
initWithTypeIdentifiersForAcceptingClass:NSString.class];
|
|
43
|
+
[self buildActionControl];
|
|
44
|
+
[self buildVisualContent];
|
|
45
|
+
[self applyAppearance];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
- (void)buildActionControl
|
|
49
|
+
{
|
|
50
|
+
if (@available(iOS 16.0, *)) {
|
|
51
|
+
UIPasteControlConfiguration *configuration = [UIPasteControlConfiguration new];
|
|
52
|
+
// The transparent full-size hit target remains the real system UIPasteControl. The custom
|
|
53
|
+
// label is non-interactive and cannot trigger a programmatic clipboard read.
|
|
54
|
+
configuration.displayMode = UIPasteControlDisplayModeIconOnly;
|
|
55
|
+
configuration.cornerStyle = UIButtonConfigurationCornerStyleFixed;
|
|
56
|
+
configuration.baseBackgroundColor = UIColor.clearColor;
|
|
57
|
+
configuration.baseForegroundColor = UIColor.clearColor;
|
|
58
|
+
UIPasteControl *pasteControl = [[UIPasteControl alloc] initWithConfiguration:configuration];
|
|
59
|
+
pasteControl.target = self;
|
|
60
|
+
self.actionControl = pasteControl;
|
|
61
|
+
} else {
|
|
62
|
+
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
|
|
63
|
+
button.backgroundColor = UIColor.clearColor;
|
|
64
|
+
[button addTarget:self
|
|
65
|
+
action:@selector(legacyPasteButtonTapped:)
|
|
66
|
+
forControlEvents:UIControlEventTouchUpInside];
|
|
67
|
+
self.actionControl = button;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
self.actionControl.translatesAutoresizingMaskIntoConstraints = NO;
|
|
71
|
+
self.actionControl.isAccessibilityElement = YES;
|
|
72
|
+
self.actionControl.accessibilityTraits = UIAccessibilityTraitButton;
|
|
73
|
+
[self addSubview:self.actionControl];
|
|
74
|
+
[NSLayoutConstraint activateConstraints:@[
|
|
75
|
+
[self.actionControl.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],
|
|
76
|
+
[self.actionControl.trailingAnchor constraintEqualToAnchor:self.trailingAnchor],
|
|
77
|
+
[self.actionControl.topAnchor constraintEqualToAnchor:self.topAnchor],
|
|
78
|
+
[self.actionControl.bottomAnchor constraintEqualToAnchor:self.bottomAnchor],
|
|
79
|
+
]];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
- (void)buildVisualContent
|
|
83
|
+
{
|
|
84
|
+
UIImageSymbolConfiguration *symbols =
|
|
85
|
+
[UIImageSymbolConfiguration configurationWithPointSize:15 weight:UIImageSymbolWeightSemibold];
|
|
86
|
+
UIImage *image = [UIImage systemImageNamed:@"doc.on.clipboard" withConfiguration:symbols];
|
|
87
|
+
self.pasteIcon = [[UIImageView alloc] initWithImage:image];
|
|
88
|
+
self.pasteIcon.contentMode = UIViewContentModeScaleAspectFit;
|
|
89
|
+
self.pasteIcon.translatesAutoresizingMaskIntoConstraints = NO;
|
|
90
|
+
[NSLayoutConstraint activateConstraints:@[
|
|
91
|
+
[self.pasteIcon.widthAnchor constraintEqualToConstant:18],
|
|
92
|
+
[self.pasteIcon.heightAnchor constraintEqualToConstant:18],
|
|
93
|
+
]];
|
|
94
|
+
|
|
95
|
+
self.titleLabel = [UILabel new];
|
|
96
|
+
self.titleLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
|
|
97
|
+
self.titleLabel.adjustsFontForContentSizeCategory = YES;
|
|
98
|
+
self.titleLabel.numberOfLines = 1;
|
|
99
|
+
self.titleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
|
|
100
|
+
|
|
101
|
+
self.visualContent = [[UIStackView alloc] initWithArrangedSubviews:@[
|
|
102
|
+
self.pasteIcon,
|
|
103
|
+
self.titleLabel,
|
|
104
|
+
]];
|
|
105
|
+
self.visualContent.axis = UILayoutConstraintAxisHorizontal;
|
|
106
|
+
self.visualContent.alignment = UIStackViewAlignmentCenter;
|
|
107
|
+
self.visualContent.spacing = 8;
|
|
108
|
+
self.visualContent.userInteractionEnabled = NO;
|
|
109
|
+
self.visualContent.isAccessibilityElement = NO;
|
|
110
|
+
self.visualContent.translatesAutoresizingMaskIntoConstraints = NO;
|
|
111
|
+
[self addSubview:self.visualContent];
|
|
112
|
+
[NSLayoutConstraint activateConstraints:@[
|
|
113
|
+
[self.visualContent.centerXAnchor constraintEqualToAnchor:self.centerXAnchor],
|
|
114
|
+
[self.visualContent.centerYAnchor constraintEqualToAnchor:self.centerYAnchor],
|
|
115
|
+
[self.visualContent.leadingAnchor constraintGreaterThanOrEqualToAnchor:self.leadingAnchor constant:12],
|
|
116
|
+
[self.visualContent.trailingAnchor constraintLessThanOrEqualToAnchor:self.trailingAnchor constant:-12],
|
|
117
|
+
]];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
- (void)setLabel:(NSString *)label
|
|
121
|
+
{
|
|
122
|
+
_label = [label copy];
|
|
123
|
+
[self applyAppearance];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
- (void)setForegroundColor:(UIColor *)foregroundColor
|
|
127
|
+
{
|
|
128
|
+
_foregroundColor = foregroundColor;
|
|
129
|
+
[self applyAppearance];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
- (void)setHandoffPrefix:(NSString *)handoffPrefix
|
|
133
|
+
{
|
|
134
|
+
_handoffPrefix = [[PulseDeferredLinkPasteView normalizedPrefix:handoffPrefix] copy];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
- (void)applyAppearance
|
|
138
|
+
{
|
|
139
|
+
NSString *visibleLabel = self.label.length > 0 ? self.label : @"Continue";
|
|
140
|
+
UIColor *color = self.foregroundColor ?: UIColor.labelColor;
|
|
141
|
+
self.titleLabel.text = visibleLabel;
|
|
142
|
+
self.titleLabel.textColor = color;
|
|
143
|
+
self.pasteIcon.tintColor = color;
|
|
144
|
+
self.actionControl.accessibilityLabel = visibleLabel;
|
|
145
|
+
self.actionControl.accessibilityHint = @"Pastes the secure install handoff";
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
#pragma mark - Explicit user actions only
|
|
149
|
+
|
|
150
|
+
- (void)legacyPasteButtonTapped:(UIButton *)sender
|
|
151
|
+
{
|
|
152
|
+
// iOS 13-15 fallback. This is the only direct UIPasteboard access, and the selector is reachable
|
|
153
|
+
// only from UIControlEventTouchUpInside. UIKit may show its normal paste permission UI.
|
|
154
|
+
NSString *candidate = UIPasteboard.generalPasteboard.string;
|
|
155
|
+
[self deliverCandidate:candidate];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
- (void)pasteItemProviders:(NSArray<NSItemProvider *> *)itemProviders
|
|
159
|
+
{
|
|
160
|
+
// UIKit calls this only after the user taps UIPasteControl. Accept the first plain string and
|
|
161
|
+
// discard every other clipboard type without exposing it to React Native.
|
|
162
|
+
for (NSItemProvider *provider in itemProviders) {
|
|
163
|
+
if (![provider canLoadObjectOfClass:NSString.class]) continue;
|
|
164
|
+
__weak __typeof(self) weakSelf = self;
|
|
165
|
+
[provider loadObjectOfClass:NSString.class
|
|
166
|
+
completionHandler:^(id<NSItemProviderReading> _Nullable object,
|
|
167
|
+
NSError *_Nullable error) {
|
|
168
|
+
if (error != nil || ![object isKindOfClass:NSString.class]) return;
|
|
169
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
170
|
+
[weakSelf deliverCandidate:(NSString *)object];
|
|
171
|
+
});
|
|
172
|
+
}];
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
- (BOOL)canPasteItemProviders:(NSArray<NSItemProvider *> *)itemProviders
|
|
178
|
+
{
|
|
179
|
+
for (NSItemProvider *provider in itemProviders) {
|
|
180
|
+
if ([provider canLoadObjectOfClass:NSString.class]) return YES;
|
|
181
|
+
}
|
|
182
|
+
return NO;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
- (void)deliverCandidate:(NSString *)candidate
|
|
186
|
+
{
|
|
187
|
+
NSString *token = [PulseDeferredLinkPasteView tokenFromHandoffString:candidate
|
|
188
|
+
prefix:self.handoffPrefix];
|
|
189
|
+
if (token == nil || self.onHandoff == nil) return;
|
|
190
|
+
self.onHandoff(@{ @"token": token });
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
+ (NSString *)normalizedPrefix:(NSString *)prefix
|
|
194
|
+
{
|
|
195
|
+
if (![prefix isKindOfClass:NSString.class] || prefix.length < 3 || prefix.length > 65
|
|
196
|
+
|| ![prefix hasSuffix:@":"]) {
|
|
197
|
+
return PulseDefaultHandoffPrefix;
|
|
198
|
+
}
|
|
199
|
+
NSCharacterSet *allowed = [NSCharacterSet
|
|
200
|
+
characterSetWithCharactersInString:
|
|
201
|
+
@"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.-:"];
|
|
202
|
+
if ([prefix rangeOfCharacterFromSet:allowed.invertedSet].location != NSNotFound) {
|
|
203
|
+
return PulseDefaultHandoffPrefix;
|
|
204
|
+
}
|
|
205
|
+
unichar first = [prefix characterAtIndex:0];
|
|
206
|
+
if (![[NSCharacterSet letterCharacterSet] characterIsMember:first]) {
|
|
207
|
+
return PulseDefaultHandoffPrefix;
|
|
208
|
+
}
|
|
209
|
+
return prefix;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
+ (NSString *)tokenFromHandoffString:(NSString *)candidate prefix:(NSString *)prefix
|
|
213
|
+
{
|
|
214
|
+
NSString *expectedPrefix = [self normalizedPrefix:prefix];
|
|
215
|
+
if (![candidate isKindOfClass:NSString.class] || ![candidate hasPrefix:expectedPrefix]) return nil;
|
|
216
|
+
NSString *token = [candidate substringFromIndex:expectedPrefix.length];
|
|
217
|
+
if (token.length < PulseMinimumTokenLength || token.length > PulseMaximumTokenLength) return nil;
|
|
218
|
+
|
|
219
|
+
static NSCharacterSet *invalidTokenCharacters;
|
|
220
|
+
static dispatch_once_t onceToken;
|
|
221
|
+
dispatch_once(&onceToken, ^{
|
|
222
|
+
NSCharacterSet *allowed = [NSCharacterSet
|
|
223
|
+
characterSetWithCharactersInString:
|
|
224
|
+
@"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-"];
|
|
225
|
+
invalidTokenCharacters = allowed.invertedSet;
|
|
226
|
+
});
|
|
227
|
+
if ([token rangeOfCharacterFromSet:invalidTokenCharacters].location != NSNotFound) return nil;
|
|
228
|
+
return [token copy];
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
@end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.DeferredLinkPasteButton = void 0;
|
|
7
|
+
var React = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _reactNative = require("react-native");
|
|
9
|
+
var _links = require("./links");
|
|
10
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
11
|
+
const PREFIX = /^[A-Za-z][A-Za-z0-9_.-]{1,63}:$/;
|
|
12
|
+
let NativePasteButton = null;
|
|
13
|
+
function getNativePasteButton() {
|
|
14
|
+
if (NativePasteButton == null) {
|
|
15
|
+
// Android/web/SSR never evaluate the iOS-only codegen component.
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
17
|
+
const module = require('./PulseDeferredLinkPasteButtonNativeComponent');
|
|
18
|
+
NativePasteButton = module.default ?? module;
|
|
19
|
+
}
|
|
20
|
+
return NativePasteButton;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Explicit one-tap iOS recovery. iOS 16+ is backed by UIPasteControl; iOS 13-15 reads the
|
|
24
|
+
* pasteboard only inside the fallback button's user tap handler. It never reads on mount,
|
|
25
|
+
* foreground, lifecycle callbacks or timers, and native emits only a validated opaque token.
|
|
26
|
+
*/
|
|
27
|
+
const DeferredLinkPasteButton = ({
|
|
28
|
+
onHandoff,
|
|
29
|
+
label = 'Continue',
|
|
30
|
+
foregroundColor = '#FFFFFF',
|
|
31
|
+
handoffPrefix = 'encore_handoff:',
|
|
32
|
+
style,
|
|
33
|
+
accessibilityLabel,
|
|
34
|
+
accessibilityHint = 'Pastes the secure install handoff',
|
|
35
|
+
fallback = null
|
|
36
|
+
}) => {
|
|
37
|
+
const handleNativeHandoff = React.useCallback(event => {
|
|
38
|
+
const token = event?.nativeEvent?.token;
|
|
39
|
+
if ((0, _links.isOpaqueDeferredLinkToken)(token)) onHandoff(token);
|
|
40
|
+
}, [onHandoff]);
|
|
41
|
+
if (_reactNative.Platform.OS !== 'ios') return fallback;
|
|
42
|
+
const NativeComponent = getNativePasteButton();
|
|
43
|
+
return /*#__PURE__*/React.createElement(NativeComponent, {
|
|
44
|
+
label: label,
|
|
45
|
+
foregroundColor: foregroundColor,
|
|
46
|
+
handoffPrefix: PREFIX.test(handoffPrefix) ? handoffPrefix : 'encore_handoff:',
|
|
47
|
+
accessibilityRole: "button",
|
|
48
|
+
accessibilityLabel: accessibilityLabel ?? label,
|
|
49
|
+
accessibilityHint: accessibilityHint,
|
|
50
|
+
style: [styles.control, style],
|
|
51
|
+
onHandoff: handleNativeHandoff
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
exports.DeferredLinkPasteButton = DeferredLinkPasteButton;
|
|
55
|
+
const styles = _reactNative.StyleSheet.create({
|
|
56
|
+
control: {
|
|
57
|
+
minWidth: 120,
|
|
58
|
+
height: 48,
|
|
59
|
+
borderRadius: 12,
|
|
60
|
+
overflow: 'hidden',
|
|
61
|
+
backgroundColor: '#111827'
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
//# sourceMappingURL=DeferredLinkPasteButton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["React","_interopRequireWildcard","require","_reactNative","_links","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","PREFIX","NativePasteButton","getNativePasteButton","module","DeferredLinkPasteButton","onHandoff","label","foregroundColor","handoffPrefix","style","accessibilityLabel","accessibilityHint","fallback","handleNativeHandoff","useCallback","event","token","nativeEvent","isOpaqueDeferredLinkToken","Platform","OS","NativeComponent","createElement","test","accessibilityRole","styles","control","exports","StyleSheet","create","minWidth","height","borderRadius","overflow","backgroundColor"],"sourceRoot":"../../src","sources":["DeferredLinkPasteButton.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _reactNative = require("react-native");
|
|
8
|
+
/**
|
|
9
|
+
* Separate from PulseUpdates on purpose: adding install-attribution native code must be visible to
|
|
10
|
+
* Pulse's OTA native-capability preflight and therefore requires a new store binary.
|
|
11
|
+
*/
|
|
12
|
+
var _default = exports.default = _reactNative.TurboModuleRegistry.get('PulseAttribution');
|
|
13
|
+
//# sourceMappingURL=NativePulseAttribution.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_reactNative","require","_default","exports","default","TurboModuleRegistry","get"],"sourceRoot":"../../src","sources":["NativePulseAttribution.ts"],"mappings":";;;;;;AACA,IAAAA,YAAA,GAAAC,OAAA;AAEA;AACA;AACA;AACA;AAHA,IAAAC,QAAA,GAAAC,OAAA,CAAAC,OAAA,GAQeC,gCAAmB,CAACC,GAAG,CAAO,kBAAkB,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _codegenNativeComponent = _interopRequireDefault(require("react-native/Libraries/Utilities/codegenNativeComponent"));
|
|
8
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
+
/** iOS-only Fabric spec for explicit, privacy-safe install handoff recovery. */
|
|
10
|
+
var _default = exports.default = (0, _codegenNativeComponent.default)('PulseDeferredLinkPasteButton', {
|
|
11
|
+
excludedPlatforms: ['android']
|
|
12
|
+
});
|
|
13
|
+
//# sourceMappingURL=PulseDeferredLinkPasteButtonNativeComponent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_codegenNativeComponent","_interopRequireDefault","require","e","__esModule","default","_default","exports","codegenNativeComponent","excludedPlatforms"],"sourceRoot":"../../src","sources":["PulseDeferredLinkPasteButtonNativeComponent.ts"],"mappings":";;;;;;AAGA,IAAAA,uBAAA,GAAAC,sBAAA,CAAAC,OAAA;AAA6F,SAAAD,uBAAAE,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAH7F;AAAA,IAAAG,QAAA,GAAAC,OAAA,CAAAF,OAAA,GAce,IAAAG,+BAAsB,EACnC,8BAA8B,EAC9B;EAAEC,iBAAiB,EAAE,CAAC,SAAS;AAAE,CACnC,CAAC","ignoreList":[]}
|
package/lib/commonjs/index.js
CHANGED
|
@@ -6,8 +6,15 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
var _exportNames = {
|
|
7
7
|
usePulseUpdates: true,
|
|
8
8
|
initializeAssetResolver: true,
|
|
9
|
-
updateLocalAssets: true
|
|
9
|
+
updateLocalAssets: true,
|
|
10
|
+
DeferredLinkPasteButton: true
|
|
10
11
|
};
|
|
12
|
+
Object.defineProperty(exports, "DeferredLinkPasteButton", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function () {
|
|
15
|
+
return _DeferredLinkPasteButton.DeferredLinkPasteButton;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
11
18
|
Object.defineProperty(exports, "initializeAssetResolver", {
|
|
12
19
|
enumerable: true,
|
|
13
20
|
get: function () {
|
|
@@ -100,4 +107,29 @@ Object.keys(_decisions).forEach(function (key) {
|
|
|
100
107
|
}
|
|
101
108
|
});
|
|
102
109
|
});
|
|
110
|
+
var _links = require("./links");
|
|
111
|
+
Object.keys(_links).forEach(function (key) {
|
|
112
|
+
if (key === "default" || key === "__esModule") return;
|
|
113
|
+
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
|
114
|
+
if (key in exports && exports[key] === _links[key]) return;
|
|
115
|
+
Object.defineProperty(exports, key, {
|
|
116
|
+
enumerable: true,
|
|
117
|
+
get: function () {
|
|
118
|
+
return _links[key];
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
var _nativeAttribution = require("./nativeAttribution");
|
|
123
|
+
Object.keys(_nativeAttribution).forEach(function (key) {
|
|
124
|
+
if (key === "default" || key === "__esModule") return;
|
|
125
|
+
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
|
126
|
+
if (key in exports && exports[key] === _nativeAttribution[key]) return;
|
|
127
|
+
Object.defineProperty(exports, key, {
|
|
128
|
+
enumerable: true,
|
|
129
|
+
get: function () {
|
|
130
|
+
return _nativeAttribution[key];
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
var _DeferredLinkPasteButton = require("./DeferredLinkPasteButton");
|
|
103
135
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_PulseUpdates","require","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get","_types","_usePulseUpdates","_assetResolver","_config","_track","_init","_decisions"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"names":["_PulseUpdates","require","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get","_types","_usePulseUpdates","_assetResolver","_config","_track","_init","_decisions","_links","_nativeAttribution","_DeferredLinkPasteButton"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":"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","ignoreList":[]}
|