@expensify/react-native-live-markdown 0.1.297 → 0.1.298

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.
@@ -14,6 +14,7 @@
14
14
  #import <RNLiveMarkdown/MarkdownTextInputDecoratorViewComponentDescriptor.h>
15
15
  #import <RNLiveMarkdown/MarkdownTextStorageDelegate.h>
16
16
  #import <RNLiveMarkdown/RCTMarkdownStyle.h>
17
+ #import <RNLiveMarkdown/RCTTextInput+AdaptiveImageGlyph.h>
17
18
 
18
19
  #import <objc/runtime.h>
19
20
 
@@ -85,6 +86,9 @@ using namespace facebook::react;
85
86
  // make sure `adjustsFontSizeToFitWidth` is disabled, otherwise formatting will be overwritten
86
87
  react_native_assert(_textField.adjustsFontSizeToFitWidth == NO);
87
88
 
89
+ // Enable TextField AdaptiveImageGlyph support for iOS 18.0+
90
+ [self enableAdaptiveImageGlyphSupport:_textField];
91
+
88
92
  _markdownTextFieldObserver = [[MarkdownTextFieldObserver alloc] initWithTextField:_textField markdownUtils:_markdownUtils];
89
93
 
90
94
  // register observers for future edits
@@ -101,6 +105,9 @@ using namespace facebook::react;
101
105
  } else if ([backedTextInputView isKindOfClass:[RCTUITextView class]]) {
102
106
  _textView = (RCTUITextView *)backedTextInputView;
103
107
 
108
+ // Enable TextView AdaptiveImageGlyph support for iOS 18.0+
109
+ [self enableAdaptiveImageGlyphSupport:_textView];
110
+
104
111
  // register delegate for future edits
105
112
  react_native_assert(_textView.textStorage.delegate == nil);
106
113
  _markdownTextStorageDelegate = [[MarkdownTextStorageDelegate alloc] initWithTextView:_textView markdownUtils:_markdownUtils];
@@ -140,6 +147,22 @@ using namespace facebook::react;
140
147
  }
141
148
  }
142
149
 
150
+ - (void)enableAdaptiveImageGlyphSupport:(UIView *)textInputView {
151
+ if (@available(iOS 18.0, *)) {
152
+ if ([textInputView respondsToSelector:@selector(setSupportsAdaptiveImageGlyph:)]) {
153
+ [textInputView setValue:@YES forKey:@"supportsAdaptiveImageGlyph"];
154
+ }
155
+ }
156
+ }
157
+
158
+ - (void)disableAdaptiveImageGlyphSupport:(UIView *)textInputView {
159
+ if (@available(iOS 18.0, *)) {
160
+ if ([textInputView respondsToSelector:@selector(setSupportsAdaptiveImageGlyph:)]) {
161
+ [textInputView setValue:@NO forKey:@"supportsAdaptiveImageGlyph"];
162
+ }
163
+ }
164
+ }
165
+
143
166
  - (void)removeTextInputObservers
144
167
  {
145
168
  react_native_assert(_observersAdded && "MarkdownTextInputDecoratorComponentView tried to remove TextInput observers while they were detached");
@@ -154,6 +177,7 @@ using namespace facebook::react;
154
177
  }
155
178
  _markdownBackedTextInputDelegate = nil;
156
179
  [_textView removeObserver:_markdownTextViewObserver forKeyPath:@"defaultTextAttributes" context:NULL];
180
+ [self disableAdaptiveImageGlyphSupport:_textView];
157
181
  _markdownTextViewObserver = nil;
158
182
  _markdownTextStorageDelegate = nil;
159
183
  _textView.textStorage.delegate = nil;
@@ -165,6 +189,7 @@ using namespace facebook::react;
165
189
  [_textField removeTarget:_markdownTextFieldObserver action:@selector(textFieldDidEndEditing:) forControlEvents:UIControlEventEditingDidEnd];
166
190
  [_textField removeObserver:_markdownTextFieldObserver forKeyPath:@"text" context:NULL];
167
191
  [_textField removeObserver:_markdownTextFieldObserver forKeyPath:@"attributedText" context:NULL];
192
+ [self disableAdaptiveImageGlyphSupport:_textField];
168
193
  _markdownTextFieldObserver = nil;
169
194
  _textField = nil;
170
195
  }
@@ -0,0 +1,14 @@
1
+ #import <React/RCTUITextField.h>
2
+ #import <React/RCTUITextView.h>
3
+
4
+ NS_ASSUME_NONNULL_BEGIN
5
+
6
+ @interface RCTUITextField (AdaptiveImageGlyph)
7
+ - (void)liveMarkdown_insertAdaptiveImageGlyph:(id)glyph replacementRange:(NSRange)replacementRange;
8
+ @end
9
+
10
+ @interface RCTUITextView (AdaptiveImageGlyph)
11
+ - (void)liveMarkdown_insertAdaptiveImageGlyph:(id)glyph replacementRange:(NSRange)replacementRange;
12
+ @end
13
+
14
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,128 @@
1
+ #import <RNLiveMarkdown/RCTTextInput+AdaptiveImageGlyph.h>
2
+ #import <UIKit/UIKit.h>
3
+ #import <objc/runtime.h>
4
+
5
+ // Helper method to setup method swizzling for adaptive image glyph
6
+ static void setupAdaptiveImageGlyphSwizzling(Class targetClass, SEL swizzledSelector) {
7
+ if (@available(iOS 18.0, *)) {
8
+ SEL originalSelector = @selector(insertAdaptiveImageGlyph:replacementRange:);
9
+
10
+ if ([targetClass instancesRespondToSelector:originalSelector]) {
11
+ Method originalMethod = class_getInstanceMethod(targetClass, originalSelector);
12
+ Method swizzledMethod = class_getInstanceMethod(targetClass, swizzledSelector);
13
+
14
+ if (originalMethod && swizzledMethod) {
15
+ BOOL didAddMethod = class_addMethod(targetClass,
16
+ originalSelector,
17
+ method_getImplementation(swizzledMethod),
18
+ method_getTypeEncoding(swizzledMethod));
19
+
20
+ if (didAddMethod) {
21
+ class_replaceMethod(targetClass,
22
+ swizzledSelector,
23
+ method_getImplementation(originalMethod),
24
+ method_getTypeEncoding(originalMethod));
25
+ } else {
26
+ method_exchangeImplementations(originalMethod, swizzledMethod);
27
+ }
28
+ }
29
+ }
30
+ }
31
+ }
32
+
33
+ // Helper method to process genmoji and convert to pasteable image
34
+ static UIImage *processGenmojiImage(id glyph) {
35
+ if (!glyph) {
36
+ return nil;
37
+ }
38
+
39
+ NSData *imageData = nil;
40
+ if ([glyph respondsToSelector:@selector(imageContent)]) {
41
+ imageData = [glyph performSelector:@selector(imageContent)];
42
+ }
43
+
44
+ if (!imageData) {
45
+ return nil;
46
+ }
47
+
48
+ UIImage *originalImage = [UIImage imageWithData:imageData];
49
+ if (!originalImage) {
50
+ return nil;
51
+ }
52
+
53
+ CGSize targetSize = CGSizeMake(128, 128);
54
+ UIGraphicsBeginImageContextWithOptions(targetSize, NO, 0.0);
55
+ [originalImage drawInRect:CGRectMake(0, 0, targetSize.width, targetSize.height)];
56
+ UIImage *resizedImage = UIGraphicsGetImageFromCurrentImageContext();
57
+ UIGraphicsEndImageContext();
58
+
59
+ if (!resizedImage) {
60
+ return nil;
61
+ }
62
+
63
+ NSData *pngData = UIImagePNGRepresentation(resizedImage);
64
+ if (!pngData) {
65
+ return nil;
66
+ }
67
+
68
+ return [UIImage imageWithData:pngData];
69
+ }
70
+
71
+ // Shared helper method to handle adaptive image glyph paste event
72
+ static void handleAdaptiveImageGlyphPasteEvent(UIView *textInputView, id glyph, NSRange replacementRange) {
73
+ UIImage *processedImage = processGenmojiImage(glyph);
74
+ if (!processedImage) {
75
+ return;
76
+ }
77
+
78
+ // Save current clipboard contents
79
+ UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
80
+ NSArray *savedItems = pasteboard.items;
81
+
82
+ // Set processed image in clipboard and trigger paste
83
+ pasteboard.image = processedImage;
84
+
85
+ if ([textInputView canPerformAction:@selector(paste:) withSender:nil]) {
86
+ [textInputView performSelector:@selector(paste:) withObject:nil];
87
+ }
88
+
89
+ // Restore previous clipboard contents
90
+ pasteboard.items = savedItems;
91
+
92
+ // Return without calling original method to prevent input genmoji insertion
93
+ return;
94
+ }
95
+
96
+ @implementation RCTUITextField (AdaptiveImageGlyph)
97
+
98
+ + (void)load {
99
+ static dispatch_once_t onceToken;
100
+ dispatch_once(&onceToken, ^{
101
+ setupAdaptiveImageGlyphSwizzling([self class], @selector(liveMarkdown_insertAdaptiveImageGlyph:replacementRange:));
102
+ });
103
+ }
104
+
105
+ - (void)liveMarkdown_insertAdaptiveImageGlyph:(id)glyph replacementRange:(NSRange)replacementRange {
106
+ if (@available(iOS 18.0, *)) {
107
+ handleAdaptiveImageGlyphPasteEvent(self, glyph, replacementRange);
108
+ }
109
+ }
110
+
111
+ @end
112
+
113
+ @implementation RCTUITextView (AdaptiveImageGlyph)
114
+
115
+ + (void)load {
116
+ static dispatch_once_t onceToken;
117
+ dispatch_once(&onceToken, ^{
118
+ setupAdaptiveImageGlyphSwizzling([self class], @selector(liveMarkdown_insertAdaptiveImageGlyph:replacementRange:));
119
+ });
120
+ }
121
+
122
+ - (void)liveMarkdown_insertAdaptiveImageGlyph:(id)glyph replacementRange:(NSRange)replacementRange {
123
+ if (@available(iOS 18.0, *)) {
124
+ handleAdaptiveImageGlyphPasteEvent(self, glyph, replacementRange);
125
+ }
126
+ }
127
+
128
+ @end
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expensify/react-native-live-markdown",
3
- "version": "0.1.297",
3
+ "version": "0.1.298",
4
4
  "description": "Drop-in replacement for React Native's TextInput component with Markdown formatting.",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",