├── .babelrc ├── .buckconfig ├── .flowconfig ├── .gitattributes ├── .gitignore ├── .watchmanconfig ├── MakemojiRN ├── MakemojiEditTextAndroid.js ├── MakemojiReactions.js ├── MakemojiTextAndroid.js ├── MakemojiTextCelliOS.js └── MakemojiTextInput.js ├── README.md ├── __tests__ ├── index.android.js └── index.ios.js ├── android ├── .gitignore ├── app │ ├── BUCK │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ ├── com │ │ │ ├── makemoji │ │ │ │ └── mojilib │ │ │ │ │ ├── MakeMojiModule.java │ │ │ │ │ ├── MakeMojiReactPackage.java │ │ │ │ │ ├── MyMojiInputLayout.java │ │ │ │ │ ├── MyReactionsLayout.java │ │ │ │ │ ├── ReactMojiEditText.java │ │ │ │ │ ├── ReactMojiEditTextManager.java │ │ │ │ │ ├── ReactMojiInputLayoutManager.java │ │ │ │ │ ├── ReactMojiReactionsViewManager.java │ │ │ │ │ └── ReactMojiTextViewManager.java │ │ │ └── makemojireactnative │ │ │ │ ├── MainActivity.java │ │ │ │ └── MainApplication.java │ │ └── csslayout │ │ │ ├── MyReactTextShadowNode.java │ │ │ └── MyShadowNode.java │ │ └── res │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ └── values │ │ ├── strings.xml │ │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── keystores │ ├── BUCK │ └── debug.keystore.properties └── settings.gradle ├── app.json ├── index.android.js ├── index.ios.js ├── ios ├── .gitignore ├── Makemoji │ ├── MakemojiManager.h │ ├── MakemojiManager.m │ ├── RCTMESimpleTableViewCellManager.m │ ├── RCTMETextInputView.h │ ├── RCTMETextInputView.m │ ├── RCTMoji.h │ ├── RCTMoji.m │ └── RCTMojiInputLayoutManager.m ├── MakemojiReactNative-tvOS │ └── Info.plist ├── MakemojiReactNative-tvOSTests │ └── Info.plist ├── MakemojiReactNative.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ ├── MakemojiReactNative-tvOS.xcscheme │ │ └── MakemojiReactNative.xcscheme ├── MakemojiReactNative.xcworkspace │ └── contents.xcworkspacedata ├── MakemojiReactNative │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ └── LaunchScreen.xib │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── RCTMojiReactionsManager.m │ └── main.m ├── MakemojiReactNativeTests │ ├── Info.plist │ └── MakemojiReactNativeTests.m ├── Podfile └── Podfile.lock ├── package.json └── watchman.exe /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react-native"] 3 | } -------------------------------------------------------------------------------- /.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore unexpected extra "@providesModule" 9 | .*/node_modules/.*/node_modules/fbjs/.* 10 | 11 | ; Ignore duplicate module providers 12 | ; For RN Apps installed via npm, "Libraries" folder is inside 13 | ; "node_modules/react-native" but in the source repo it is in the root 14 | .*/Libraries/react-native/React.js 15 | .*/Libraries/react-native/ReactNative.js 16 | 17 | [include] 18 | 19 | [libs] 20 | node_modules/react-native/Libraries/react-native/react-native-interface.js 21 | node_modules/react-native/flow 22 | flow/ 23 | 24 | [options] 25 | emoji=true 26 | 27 | module.system=haste 28 | 29 | experimental.strict_type_args=true 30 | 31 | munge_underscores=true 32 | 33 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' 34 | 35 | suppress_type=$FlowIssue 36 | suppress_type=$FlowFixMe 37 | suppress_type=$FixMe 38 | 39 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(4[0-0]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 40 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(4[0-0]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 41 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 42 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 43 | 44 | unsafe.enable_getters_and_setters=true 45 | 46 | [version] 47 | ^0.40.0 48 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | yarn-error.log 38 | 39 | # BUCK 40 | buck-out/ 41 | \.buckd/ 42 | *.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 50 | 51 | fastlane/report.xml 52 | fastlane/Preview.html 53 | fastlane/screenshots 54 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /MakemojiRN/MakemojiEditTextAndroid.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * @providesModule MakemojiEditTextAndroid 10 | * @flow 11 | */ 12 | 'use strict'; 13 | 14 | const ColorPropType = require('ColorPropType'); 15 | const DocumentSelectionState = require('DocumentSelectionState'); 16 | const EventEmitter = require('EventEmitter'); 17 | const NativeMethodsMixin = require('NativeMethodsMixin'); 18 | const Platform = require('Platform'); 19 | const React = require('React'); 20 | const ReactNative = require('ReactNative'); 21 | const StyleSheet = require('StyleSheet'); 22 | const Text = require('Text'); 23 | const TextInputState = require('TextInputState'); 24 | const TimerMixin = require('react-timer-mixin'); 25 | const TouchableWithoutFeedback = require('TouchableWithoutFeedback'); 26 | const UIManager = require('UIManager'); 27 | const View = require('View'); 28 | 29 | const emptyFunction = require('fbjs/lib/emptyFunction'); 30 | const invariant = require('fbjs/lib/invariant'); 31 | const requireNativeComponent = require('requireNativeComponent'); 32 | const warning = require('fbjs/lib/warning'); 33 | 34 | const PropTypes = React.PropTypes; 35 | 36 | const onlyMultiline = { 37 | onTextInput: true, 38 | children: true, 39 | }; 40 | 41 | if (Platform.OS === 'android') { 42 | var AndroidTextInput = requireNativeComponent('MakeMojiAndroidTextInput', null); 43 | } else if (Platform.OS === 'ios') { 44 | var RCTTextView = requireNativeComponent('RCTTextView', null); 45 | var RCTTextField = requireNativeComponent('RCTTextField', null); 46 | } 47 | 48 | type Event = Object; 49 | type Selection = { 50 | start: number, 51 | end?: number, 52 | }; 53 | 54 | const DataDetectorTypes = [ 55 | 'phoneNumber', 56 | 'link', 57 | 'address', 58 | 'calendarEvent', 59 | 'none', 60 | 'all', 61 | ]; 62 | 63 | /** 64 | * A foundational component for inputting text into the app via a 65 | * keyboard. Props provide configurability for several features, such as 66 | * auto-correction, auto-capitalization, placeholder text, and different keyboard 67 | * types, such as a numeric keypad. 68 | * 69 | * The simplest use case is to plop down a `TextInput` and subscribe to the 70 | * `onChangeText` events to read the user input. There are also other events, 71 | * such as `onSubmitEditing` and `onFocus` that can be subscribed to. A simple 72 | * example: 73 | * 74 | * ```ReactNativeWebPlayer 75 | * import React, { Component } from 'react'; 76 | * import { AppRegistry, TextInput } from 'react-native'; 77 | * 78 | * class UselessTextInput extends Component { 79 | * constructor(props) { 80 | * super(props); 81 | * this.state = { text: 'Useless Placeholder' }; 82 | * } 83 | * 84 | * render() { 85 | * return ( 86 | * this.setState({text})} 89 | * value={this.state.text} 90 | * /> 91 | * ); 92 | * } 93 | * } 94 | * 95 | * // App registration and rendering 96 | * AppRegistry.registerComponent('AwesomeProject', () => UselessTextInput); 97 | * ``` 98 | * 99 | * Note that some props are only available with `multiline={true/false}`. 100 | * Additionally, border styles that apply to only one side of the element 101 | * (e.g., `borderBottomColor`, `borderLeftWidth`, etc.) will not be applied if 102 | * `multiline=false`. To achieve the same effect, you can wrap your `TextInput` 103 | * in a `View`: 104 | * 105 | * ```ReactNativeWebPlayer 106 | * import React, { Component } from 'react'; 107 | * import { AppRegistry, View, TextInput } from 'react-native'; 108 | * 109 | * class UselessTextInput extends Component { 110 | * render() { 111 | * return ( 112 | * 117 | * ); 118 | * } 119 | * } 120 | * 121 | * class UselessTextInputMultiline extends Component { 122 | * constructor(props) { 123 | * super(props); 124 | * this.state = { 125 | * text: 'Useless Multiline Placeholder', 126 | * }; 127 | * } 128 | * 129 | * // If you type something in the text box that is a color, the background will change to that 130 | * // color. 131 | * render() { 132 | * return ( 133 | * 138 | * this.setState({text})} 142 | * value={this.state.text} 143 | * /> 144 | * 145 | * ); 146 | * } 147 | * } 148 | * 149 | * // App registration and rendering 150 | * AppRegistry.registerComponent( 151 | * 'AwesomeProject', 152 | * () => UselessTextInputMultiline 153 | * ); 154 | * ``` 155 | * 156 | * `TextInput` has by default a border at the bottom of its view. This border 157 | * has its padding set by the background image provided by the system, and it 158 | * cannot be changed. Solutions to avoid this is to either not set height 159 | * explicitly, case in which the system will take care of displaying the border 160 | * in the correct position, or to not display the border by setting 161 | * `underlineColorAndroid` to transparent. 162 | * 163 | * Note that on Android performing text selection in input can change 164 | * app's activity `windowSoftInputMode` param to `adjustResize`. 165 | * This may cause issues with components that have position: 'absolute' 166 | * while keyboard is active. To avoid this behavior either specify `windowSoftInputMode` 167 | * in AndroidManifest.xml ( https://developer.android.com/guide/topics/manifest/activity-element.html ) 168 | * or control this param programmatically with native code. 169 | * 170 | */ 171 | const TextInput = React.createClass({ 172 | statics: { 173 | /* TODO(brentvatne) docs are needed for this */ 174 | State: TextInputState, 175 | }, 176 | 177 | propTypes: { 178 | ...View.propTypes, 179 | /** 180 | * Can tell `TextInput` to automatically capitalize certain characters. 181 | * 182 | * - `characters`: all characters. 183 | * - `words`: first letter of each word. 184 | * - `sentences`: first letter of each sentence (*default*). 185 | * - `none`: don't auto capitalize anything. 186 | */ 187 | autoCapitalize: PropTypes.oneOf([ 188 | 'none', 189 | 'sentences', 190 | 'words', 191 | 'characters', 192 | ]), 193 | /** 194 | * If `false`, disables auto-correct. The default value is `true`. 195 | */ 196 | autoCorrect: PropTypes.bool, 197 | /** 198 | * If `false`, disables spell-check style (i.e. red underlines). 199 | * The default value is inherited from `autoCorrect`. 200 | * @platform ios 201 | */ 202 | spellCheck: PropTypes.bool, 203 | /** 204 | * If `true`, focuses the input on `componentDidMount`. 205 | * The default value is `false`. 206 | */ 207 | autoFocus: PropTypes.bool, 208 | /** 209 | * If `false`, text is not editable. The default value is `true`. 210 | */ 211 | editable: PropTypes.bool, 212 | /** 213 | * Determines which keyboard to open, e.g.`numeric`. 214 | * 215 | * The following values work across platforms: 216 | * 217 | * - `default` 218 | * - `numeric` 219 | * - `email-address` 220 | * - `phone-pad` 221 | */ 222 | keyboardType: PropTypes.oneOf([ 223 | // Cross-platform 224 | 'default', 225 | 'email-address', 226 | 'numeric', 227 | 'phone-pad', 228 | // iOS-only 229 | 'ascii-capable', 230 | 'numbers-and-punctuation', 231 | 'url', 232 | 'number-pad', 233 | 'name-phone-pad', 234 | 'decimal-pad', 235 | 'twitter', 236 | 'web-search', 237 | ]), 238 | /** 239 | * Determines the color of the keyboard. 240 | * @platform ios 241 | */ 242 | keyboardAppearance: PropTypes.oneOf([ 243 | 'default', 244 | 'light', 245 | 'dark', 246 | ]), 247 | /** 248 | * Determines how the return key should look. On Android you can also use 249 | * `returnKeyLabel`. 250 | * 251 | * *Cross platform* 252 | * 253 | * The following values work across platforms: 254 | * 255 | * - `done` 256 | * - `go` 257 | * - `next` 258 | * - `search` 259 | * - `send` 260 | * 261 | * *Android Only* 262 | * 263 | * The following values work on Android only: 264 | * 265 | * - `none` 266 | * - `previous` 267 | * 268 | * *iOS Only* 269 | * 270 | * The following values work on iOS only: 271 | * 272 | * - `default` 273 | * - `emergency-call` 274 | * - `google` 275 | * - `join` 276 | * - `route` 277 | * - `yahoo` 278 | */ 279 | returnKeyType: PropTypes.oneOf([ 280 | // Cross-platform 281 | 'done', 282 | 'go', 283 | 'next', 284 | 'search', 285 | 'send', 286 | // Android-only 287 | 'none', 288 | 'previous', 289 | // iOS-only 290 | 'default', 291 | 'emergency-call', 292 | 'google', 293 | 'join', 294 | 'route', 295 | 'yahoo', 296 | ]), 297 | /** 298 | * Sets the return key to the label. Use it instead of `returnKeyType`. 299 | * @platform android 300 | */ 301 | returnKeyLabel: PropTypes.string, 302 | /** 303 | * Limits the maximum number of characters that can be entered. Use this 304 | * instead of implementing the logic in JS to avoid flicker. 305 | */ 306 | maxLength: PropTypes.number, 307 | /** 308 | * Sets the number of lines for a `TextInput`. Use it with multiline set to 309 | * `true` to be able to fill the lines. 310 | * @platform android 311 | */ 312 | numberOfLines: PropTypes.number, 313 | /** 314 | * When `false`, if there is a small amount of space available around a text input 315 | * (e.g. landscape orientation on a phone), the OS may choose to have the user edit 316 | * the text inside of a full screen text input mode. When `true`, this feature is 317 | * disabled and users will always edit the text directly inside of the text input. 318 | * Defaults to `false`. 319 | * @platform android 320 | */ 321 | disableFullscreenUI: PropTypes.bool, 322 | /** 323 | * If `true`, the keyboard disables the return key when there is no text and 324 | * automatically enables it when there is text. The default value is `false`. 325 | * @platform ios 326 | */ 327 | enablesReturnKeyAutomatically: PropTypes.bool, 328 | /** 329 | * If `true`, the text input can be multiple lines. 330 | * The default value is `false`. 331 | */ 332 | multiline: PropTypes.bool, 333 | /** 334 | * Set text break strategy on Android API Level 23+, possible values are `simple`, `highQuality`, `balanced` 335 | * The default value is `simple`. 336 | * @platform android 337 | */ 338 | textBreakStrategy: React.PropTypes.oneOf(['simple', 'highQuality', 'balanced']), 339 | /** 340 | * Callback that is called when the text input is blurred. 341 | */ 342 | onBlur: PropTypes.func, 343 | /** 344 | * Callback that is called when the text input is focused. 345 | */ 346 | onFocus: PropTypes.func, 347 | /** 348 | * Callback that is called when the text input's text changes. 349 | */ 350 | onChange: PropTypes.func, 351 | /** 352 | * Callback that is called when the text input's text changes. 353 | * Changed text is passed as an argument to the callback handler. 354 | */ 355 | onChangeText: PropTypes.func, 356 | /** 357 | * Callback that is called when the text input's content size changes. 358 | * This will be called with 359 | * `{ nativeEvent: { contentSize: { width, height } } }`. 360 | * 361 | * Only called for multiline text inputs. 362 | */ 363 | onContentSizeChange: PropTypes.func, 364 | /** 365 | * Callback that is called when text input ends. 366 | */ 367 | onEndEditing: PropTypes.func, 368 | /** 369 | * Callback that is called when the text input selection is changed. 370 | * This will be called with 371 | * `{ nativeEvent: { selection: { start, end } } }`. 372 | */ 373 | onSelectionChange: PropTypes.func, 374 | /** 375 | * Callback that is called when the text input's submit button is pressed. 376 | * Invalid if `multiline={true}` is specified. 377 | */ 378 | onSubmitEditing: PropTypes.func, 379 | /** 380 | * Callback that is called when a key is pressed. 381 | * This will be called with `{ nativeEvent: { key: keyValue } }` 382 | * where `keyValue` is `'Enter'` or `'Backspace'` for respective keys and 383 | * the typed-in character otherwise including `' '` for space. 384 | * Fires before `onChange` callbacks. 385 | * @platform ios 386 | */ 387 | onKeyPress: PropTypes.func, 388 | /** 389 | * Invoked on mount and layout changes with `{x, y, width, height}`. 390 | */ 391 | onLayout: PropTypes.func, 392 | /** 393 | * Invoked on content scroll with `{ nativeEvent: { contentOffset: { x, y } } }`. 394 | * May also contain other properties from ScrollEvent but on Android contentSize 395 | * is not provided for performance reasons. 396 | */ 397 | onScroll: PropTypes.func, 398 | /** 399 | * The string that will be rendered before text input has been entered. 400 | */ 401 | placeholder: PropTypes.node, 402 | /** 403 | * The text color of the placeholder string. 404 | */ 405 | placeholderTextColor: ColorPropType, 406 | /** 407 | * If `true`, the text input obscures the text entered so that sensitive text 408 | * like passwords stay secure. The default value is `false`. 409 | */ 410 | secureTextEntry: PropTypes.bool, 411 | /** 412 | * The highlight and cursor color of the text input. 413 | */ 414 | selectionColor: ColorPropType, 415 | /** 416 | * An instance of `DocumentSelectionState`, this is some state that is responsible for 417 | * maintaining selection information for a document. 418 | * 419 | * Some functionality that can be performed with this instance is: 420 | * 421 | * - `blur()` 422 | * - `focus()` 423 | * - `update()` 424 | * 425 | * > You can reference `DocumentSelectionState` in 426 | * > [`vendor/document/selection/DocumentSelectionState.js`](https://github.com/facebook/react-native/blob/master/Libraries/vendor/document/selection/DocumentSelectionState.js) 427 | * 428 | * @platform ios 429 | */ 430 | selectionState: PropTypes.instanceOf(DocumentSelectionState), 431 | /** 432 | * The start and end of the text input's selection. Set start and end to 433 | * the same value to position the cursor. 434 | */ 435 | selection: PropTypes.shape({ 436 | start: PropTypes.number.isRequired, 437 | end: PropTypes.number, 438 | }), 439 | /** 440 | * The value to show for the text input. `TextInput` is a controlled 441 | * component, which means the native value will be forced to match this 442 | * value prop if provided. For most uses, this works great, but in some 443 | * cases this may cause flickering - one common cause is preventing edits 444 | * by keeping value the same. In addition to simply setting the same value, 445 | * either set `editable={false}`, or set/update `maxLength` to prevent 446 | * unwanted edits without flicker. 447 | */ 448 | value: PropTypes.string, 449 | /** 450 | * Provides an initial value that will change when the user starts typing. 451 | * Useful for simple use-cases where you do not want to deal with listening 452 | * to events and updating the value prop to keep the controlled state in sync. 453 | */ 454 | defaultValue: PropTypes.node, 455 | /** 456 | * When the clear button should appear on the right side of the text view. 457 | * @platform ios 458 | */ 459 | clearButtonMode: PropTypes.oneOf([ 460 | 'never', 461 | 'while-editing', 462 | 'unless-editing', 463 | 'always', 464 | ]), 465 | /** 466 | * If `true`, clears the text field automatically when editing begins. 467 | * @platform ios 468 | */ 469 | clearTextOnFocus: PropTypes.bool, 470 | /** 471 | * If `true`, all text will automatically be selected on focus. 472 | */ 473 | selectTextOnFocus: PropTypes.bool, 474 | /** 475 | * If `true`, the text field will blur when submitted. 476 | * The default value is true for single-line fields and false for 477 | * multiline fields. Note that for multiline fields, setting `blurOnSubmit` 478 | * to `true` means that pressing return will blur the field and trigger the 479 | * `onSubmitEditing` event instead of inserting a newline into the field. 480 | */ 481 | blurOnSubmit: PropTypes.bool, 482 | /** 483 | * Note that not all Text styles are supported, 484 | * see [Issue#7070](https://github.com/facebook/react-native/issues/7070) 485 | * for more detail. 486 | * 487 | * [Styles](docs/style.html) 488 | */ 489 | style: Text.propTypes.style, 490 | /** 491 | * The color of the `TextInput` underline. 492 | * @platform android 493 | */ 494 | underlineColorAndroid: ColorPropType, 495 | 496 | /** 497 | * If defined, the provided image resource will be rendered on the left. 498 | * @platform android 499 | */ 500 | inlineImageLeft: PropTypes.string, 501 | 502 | /** 503 | * Padding between the inline image, if any, and the text input itself. 504 | * @platform android 505 | */ 506 | inlineImagePadding: PropTypes.number, 507 | 508 | /** 509 | * Determines the types of data converted to clickable URLs in the text input. 510 | * Only valid if `multiline={true}` and `editable={false}`. 511 | * By default no data types are detected. 512 | * 513 | * You can provide one type or an array of many types. 514 | * 515 | * Possible values for `dataDetectorTypes` are: 516 | * 517 | * - `'phoneNumber'` 518 | * - `'link'` 519 | * - `'address'` 520 | * - `'calendarEvent'` 521 | * - `'none'` 522 | * - `'all'` 523 | * 524 | * @platform ios 525 | */ 526 | dataDetectorTypes: PropTypes.oneOfType([ 527 | PropTypes.oneOf(DataDetectorTypes), 528 | PropTypes.arrayOf(PropTypes.oneOf(DataDetectorTypes)), 529 | ]), 530 | /** 531 | * If `true`, caret is hidden. The default value is `false`. 532 | */ 533 | caretHidden: PropTypes.bool, 534 | 535 | onHtmlGenerated:PropTypes.func, 536 | finderTag:PropTypes.string 537 | }, 538 | 539 | /** 540 | * `NativeMethodsMixin` will look for this when invoking `setNativeProps`. We 541 | * make `this` look like an actual native component class. 542 | */ 543 | mixins: [NativeMethodsMixin, TimerMixin], 544 | 545 | viewConfig: 546 | ((Platform.OS === 'ios' && RCTTextField ? 547 | RCTTextField.viewConfig : 548 | (Platform.OS === 'android' && AndroidTextInput ? 549 | AndroidTextInput.viewConfig : 550 | {})) : Object), 551 | 552 | /** 553 | * Returns `true` if the input is currently focused; `false` otherwise. 554 | */ 555 | isFocused: function(): boolean { 556 | return TextInputState.currentlyFocusedField() === 557 | ReactNative.findNodeHandle(this._inputRef); 558 | }, 559 | 560 | contextTypes: { 561 | onFocusRequested: React.PropTypes.func, 562 | focusEmitter: React.PropTypes.instanceOf(EventEmitter), 563 | }, 564 | 565 | _inputRef: (undefined: any), 566 | _focusSubscription: (undefined: ?Function), 567 | _lastNativeText: (undefined: ?string), 568 | _lastNativeSelection: (undefined: ?Selection), 569 | 570 | componentDidMount: function() { 571 | this._lastNativeText = this.props.value; 572 | if (!this.context.focusEmitter) { 573 | if (this.props.autoFocus) { 574 | this.requestAnimationFrame(this.focus); 575 | } 576 | return; 577 | } 578 | this._focusSubscription = this.context.focusEmitter.addListener( 579 | 'focus', 580 | (el) => { 581 | if (this === el) { 582 | this.requestAnimationFrame(this.focus); 583 | } else if (this.isFocused()) { 584 | this.blur(); 585 | } 586 | } 587 | ); 588 | if (this.props.autoFocus) { 589 | this.context.onFocusRequested(this); 590 | } 591 | }, 592 | 593 | componentWillUnmount: function() { 594 | this._focusSubscription && this._focusSubscription.remove(); 595 | if (this.isFocused()) { 596 | this.blur(); 597 | } 598 | }, 599 | 600 | getChildContext: function(): Object { 601 | return {isInAParentText: true}; 602 | }, 603 | 604 | childContextTypes: { 605 | isInAParentText: React.PropTypes.bool 606 | }, 607 | 608 | /** 609 | * Removes all text from the `TextInput`. 610 | */ 611 | clear: function() { 612 | this.setNativeProps({text: ''}); 613 | }, 614 | 615 | render: function() { 616 | if (Platform.OS === 'ios') { 617 | return this._renderIOS(); 618 | } else if (Platform.OS === 'android') { 619 | return this._renderAndroid(); 620 | } 621 | }, 622 | 623 | _getText: function(): ?string { 624 | return typeof this.props.value === 'string' ? 625 | this.props.value : 626 | ( 627 | typeof this.props.defaultValue === 'string' ? 628 | this.props.defaultValue : 629 | '' 630 | ); 631 | }, 632 | 633 | _setNativeRef: function(ref: any) { 634 | this._inputRef = ref; 635 | }, 636 | 637 | _renderIOS: function() { 638 | var textContainer; 639 | 640 | var props = Object.assign({}, this.props); 641 | props.style = [styles.input, this.props.style]; 642 | 643 | if (props.selection && props.selection.end == null) { 644 | props.selection = {start: props.selection.start, end: props.selection.start}; 645 | } 646 | 647 | if (!props.multiline) { 648 | if (__DEV__) { 649 | for (var propKey in onlyMultiline) { 650 | if (props[propKey]) { 651 | const error = new Error( 652 | 'TextInput prop `' + propKey + '` is only supported with multiline.' 653 | ); 654 | warning(false, '%s', error.stack); 655 | } 656 | } 657 | } 658 | textContainer = 659 | this.props.onHtmlGenerated(e.nativeEvent)} 669 | finderTag={this.props.finderTag} 670 | />; 671 | } else { 672 | var children = props.children; 673 | var childCount = 0; 674 | React.Children.forEach(children, () => ++childCount); 675 | invariant( 676 | !(props.value && childCount), 677 | 'Cannot specify both value and children.' 678 | ); 679 | if (childCount >= 1) { 680 | children = {children}; 681 | } 682 | if (props.inputView) { 683 | children = [children, props.inputView]; 684 | } 685 | textContainer = 686 | this.props.onHtmlGenerated(e.nativeEvent)} 700 | finderTag={this.props.finderTag} 701 | onScroll={this._onScroll} 702 | />; 703 | } 704 | 705 | return ( 706 | 714 | {textContainer} 715 | 716 | ); 717 | }, 718 | 719 | _renderAndroid: function() { 720 | const props = Object.assign({}, this.props); 721 | props.style = [this.props.style]; 722 | props.autoCapitalize = 723 | UIManager.AndroidTextInput.Constants.AutoCapitalizationType[this.props.autoCapitalize]; 724 | var children = this.props.children; 725 | var childCount = 0; 726 | React.Children.forEach(children, () => ++childCount); 727 | invariant( 728 | !(this.props.value && childCount), 729 | 'Cannot specify both value and children.' 730 | ); 731 | if (childCount > 1) { 732 | children = {children}; 733 | } 734 | 735 | if (props.selection && props.selection.end == null) { 736 | props.selection = {start: props.selection.start, end: props.selection.start}; 737 | } 738 | 739 | const textContainer = 740 | this.props.onHtmlGenerated(e.nativeEvent)} 755 | />; 756 | 757 | return ( 758 | 765 | {textContainer} 766 | 767 | ); 768 | }, 769 | 770 | _onFocus: function(event: Event) { 771 | if (this.props.onFocus) { 772 | this.props.onFocus(event); 773 | } 774 | 775 | if (this.props.selectionState) { 776 | this.props.selectionState.focus(); 777 | } 778 | }, 779 | 780 | _onPress: function(event: Event) { 781 | if (this.props.editable || this.props.editable === undefined) { 782 | this.focus(); 783 | } 784 | }, 785 | 786 | _onChange: function(event: Event) { 787 | // Make sure to fire the mostRecentEventCount first so it is already set on 788 | // native when the text value is set. 789 | if (this._inputRef) { 790 | this._inputRef.setNativeProps({ 791 | mostRecentEventCount: event.nativeEvent.eventCount, 792 | }); 793 | } 794 | 795 | var text = event.nativeEvent.text; 796 | this.props.onChange && this.props.onChange(event); 797 | this.props.onChangeText && this.props.onChangeText(text); 798 | 799 | if (!this._inputRef) { 800 | // calling `this.props.onChange` or `this.props.onChangeText` 801 | // may clean up the input itself. Exits here. 802 | return; 803 | } 804 | 805 | this._lastNativeText = text; 806 | this.forceUpdate(); 807 | }, 808 | 809 | _onSelectionChange: function(event: Event) { 810 | this.props.onSelectionChange && this.props.onSelectionChange(event); 811 | 812 | if (!this._inputRef) { 813 | // calling `this.props.onSelectionChange` 814 | // may clean up the input itself. Exits here. 815 | return; 816 | } 817 | 818 | this._lastNativeSelection = event.nativeEvent.selection; 819 | 820 | if (this.props.selection || this.props.selectionState) { 821 | this.forceUpdate(); 822 | } 823 | }, 824 | 825 | componentDidUpdate: function () { 826 | // This is necessary in case native updates the text and JS decides 827 | // that the update should be ignored and we should stick with the value 828 | // that we have in JS. 829 | const nativeProps = {}; 830 | 831 | if (this._lastNativeText !== this.props.value && typeof this.props.value === 'string') { 832 | nativeProps.text = this.props.value; 833 | } 834 | 835 | // Selection is also a controlled prop, if the native value doesn't match 836 | // JS, update to the JS value. 837 | const {selection} = this.props; 838 | if (this._lastNativeSelection && selection && 839 | (this._lastNativeSelection.start !== selection.start || 840 | this._lastNativeSelection.end !== selection.end)) { 841 | nativeProps.selection = this.props.selection; 842 | } 843 | 844 | if (Object.keys(nativeProps).length > 0 && this._inputRef) { 845 | this._inputRef.setNativeProps(nativeProps); 846 | } 847 | 848 | if (this.props.selectionState && selection) { 849 | this.props.selectionState.update(selection.start, selection.end); 850 | } 851 | }, 852 | 853 | _onBlur: function(event: Event) { 854 | this.blur(); 855 | if (this.props.onBlur) { 856 | this.props.onBlur(event); 857 | } 858 | 859 | if (this.props.selectionState) { 860 | this.props.selectionState.blur(); 861 | } 862 | }, 863 | 864 | _onTextInput: function(event: Event) { 865 | this.props.onTextInput && this.props.onTextInput(event); 866 | }, 867 | 868 | _onScroll: function(event: Event) { 869 | this.props.onScroll && this.props.onScroll(event); 870 | }, 871 | 872 | requestHtml: function (clear,sendAnalytics){ 873 | UIManager.dispatchViewManagerCommand( 874 | ReactNative.findNodeHandle(this), 875 | 404,[clear,sendAnalytics] 876 | ); 877 | }, 878 | setText: function (text){ 879 | UIManager.dispatchViewManagerCommand( 880 | ReactNative.findNodeHandle(this), 881 | 405,[text] 882 | );} 883 | }); 884 | 885 | var styles = StyleSheet.create({ 886 | input: { 887 | alignSelf: 'stretch', 888 | }, 889 | }); 890 | 891 | module.exports = TextInput; 892 | -------------------------------------------------------------------------------- /MakemojiRN/MakemojiReactions.js: -------------------------------------------------------------------------------- 1 | import { PropTypes } from 'react'; 2 | import React, { Component } from 'react'; 3 | import { requireNativeComponent, View, NativeModules } from 'react-native'; 4 | 5 | var ReactNative = require('ReactNative'); 6 | var NativeComponent = requireNativeComponent('RCTMojiReactions', null); 7 | export default class MakemojiReactions extends React.Component { 8 | constructor(props) { 9 | super(props); 10 | 11 | } 12 | render() { 13 | return ; 14 | } 15 | 16 | 17 | 18 | } 19 | MakemojiReactions.propTypes = { 20 | ...View.propTypes, 21 | 22 | contentId:React.PropTypes.string, 23 | }; 24 | 25 | module.exports = MakemojiReactions; -------------------------------------------------------------------------------- /MakemojiRN/MakemojiTextAndroid.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * @providesModule MakemojiText 10 | * @flow 11 | */ 12 | 13 | const ColorPropType = require('ColorPropType'); 14 | const EdgeInsetsPropType = require('EdgeInsetsPropType'); 15 | const NativeMethodsMixin = require('NativeMethodsMixin'); 16 | const Platform = require('Platform'); 17 | const React = require('React'); 18 | const ReactNativeViewAttributes = require('ReactNativeViewAttributes'); 19 | const StyleSheetPropType = require('StyleSheetPropType'); 20 | const TextStylePropTypes = require('TextStylePropTypes'); 21 | const Touchable = require('Touchable'); 22 | 23 | const processColor = require('processColor'); 24 | const createReactNativeComponentClass = require('createReactNativeComponentClass'); 25 | const mergeFast = require('mergeFast'); 26 | 27 | const { PropTypes } = React; 28 | 29 | const stylePropType = StyleSheetPropType(TextStylePropTypes); 30 | 31 | const viewConfig = { 32 | validAttributes: mergeFast(ReactNativeViewAttributes.UIView, { 33 | isHighlighted: true, 34 | numberOfLines: true, 35 | ellipsizeMode: true, 36 | allowFontScaling: true, 37 | selectable: true, 38 | selectionColor: true, 39 | adjustsFontSizeToFit: true, 40 | minimumFontScale: true, 41 | textBreakStrategy: true, 42 | html:'__VOID__', 43 | plaintext:'__VOID__' 44 | }), 45 | uiViewClassName: 'ReactMojiText', 46 | }; 47 | 48 | /** 49 | * A React component for displaying text. 50 | * 51 | * `Text` supports nesting, styling, and touch handling. 52 | * 53 | * In the following example, the nested title and body text will inherit the `fontFamily` from 54 | *`styles.baseText`, but the title provides its own additional styles. The title and body will 55 | * stack on top of each other on account of the literal newlines: 56 | * 57 | * ```ReactNativeWebPlayer 58 | * import React, { Component } from 'react'; 59 | * import { AppRegistry, Text, StyleSheet } from 'react-native'; 60 | * 61 | * class TextInANest extends Component { 62 | * constructor(props) { 63 | * super(props); 64 | * this.state = { 65 | * titleText: "Bird's Nest", 66 | * bodyText: 'This is not really a bird nest.' 67 | * }; 68 | * } 69 | * 70 | * render() { 71 | * return ( 72 | * 73 | * 74 | * {this.state.titleText}{'\n'}{'\n'} 75 | * 76 | * 77 | * {this.state.bodyText} 78 | * 79 | * 80 | * ); 81 | * } 82 | * } 83 | * 84 | * const styles = StyleSheet.create({ 85 | * baseText: { 86 | * fontFamily: 'Cochin', 87 | * }, 88 | * titleText: { 89 | * fontSize: 20, 90 | * fontWeight: 'bold', 91 | * }, 92 | * }); 93 | * 94 | * // App registration and rendering 95 | * AppRegistry.registerComponent('TextInANest', () => TextInANest); 96 | * ``` 97 | */ 98 | 99 | const MakemojiText = React.createClass({ 100 | propTypes: { 101 | /** 102 | * When `numberOfLines` is set, this prop defines how text will be truncated. 103 | * `numberOfLines` must be set in conjunction with this prop. 104 | * 105 | * This can be one of the following values: 106 | * 107 | * - `head` - The line is displayed so that the end fits in the container and the missing text 108 | * at the beginning of the line is indicated by an ellipsis glyph. e.g., "...wxyz" 109 | * - `middle` - The line is displayed so that the beginning and end fit in the container and the 110 | * missing text in the middle is indicated by an ellipsis glyph. "ab...yz" 111 | * - `tail` - The line is displayed so that the beginning fits in the container and the 112 | * missing text at the end of the line is indicated by an ellipsis glyph. e.g., "abcd..." 113 | * - `clip` - Lines are not drawn past the edge of the text container. 114 | * 115 | * The default is `tail`. 116 | * 117 | * > `clip` is working only for iOS 118 | */ 119 | ellipsizeMode: PropTypes.oneOf(['head', 'middle', 'tail', 'clip']), 120 | /** 121 | * Used to truncate the text with an ellipsis after computing the text 122 | * layout, including line wrapping, such that the total number of lines 123 | * does not exceed this number. 124 | * 125 | * This prop is commonly used with `ellipsizeMode`. 126 | */ 127 | numberOfLines: PropTypes.number, 128 | /** 129 | * Set text break strategy on Android API Level 23+, possible values are `simple`, `highQuality`, `balanced` 130 | * The default value is `highQuality`. 131 | * @platform android 132 | */ 133 | textBreakStrategy: PropTypes.oneOf(['simple', 'highQuality', 'balanced']), 134 | /** 135 | * Invoked on mount and layout changes with 136 | * 137 | * `{nativeEvent: {layout: {x, y, width, height}}}` 138 | */ 139 | onLayout: PropTypes.func, 140 | /** 141 | * This function is called on press. 142 | * 143 | * e.g., `onPress={() => console.log('1st')}` 144 | */ 145 | onPress: PropTypes.func, 146 | /** 147 | * This function is called on long press. 148 | * 149 | * e.g., `onLongPress={this.increaseSize}>` 150 | */ 151 | onLongPress: PropTypes.func, 152 | /** 153 | * When the scroll view is disabled, this defines how far your touch may 154 | * move off of the button, before deactivating the button. Once deactivated, 155 | * try moving it back and you'll see that the button is once again 156 | * reactivated! Move it back and forth several times while the scroll view 157 | * is disabled. Ensure you pass in a constant to reduce memory allocations. 158 | */ 159 | pressRetentionOffset: EdgeInsetsPropType, 160 | /** 161 | * Lets the user select text, to use the native copy and paste functionality. 162 | */ 163 | selectable: PropTypes.bool, 164 | /** 165 | * The highlight color of the text. 166 | * @platform android 167 | */ 168 | selectionColor: ColorPropType, 169 | /** 170 | * When `true`, no visual change is made when text is pressed down. By 171 | * default, a gray oval highlights the text on press down. 172 | * @platform ios 173 | */ 174 | suppressHighlighting: PropTypes.bool, 175 | style: stylePropType, 176 | /** 177 | * Used to locate this view in end-to-end tests. 178 | */ 179 | testID: PropTypes.string, 180 | /** 181 | * Specifies whether fonts should scale to respect Text Size accessibility settings. The 182 | * default is `true`. 183 | */ 184 | allowFontScaling: PropTypes.bool, 185 | /** 186 | * When set to `true`, indicates that the view is an accessibility element. The default value 187 | * for a `Text` element is `true`. 188 | * 189 | * See the 190 | * [Accessibility guide](docs/accessibility.html#accessible-ios-android) 191 | * for more information. 192 | */ 193 | accessible: PropTypes.bool, 194 | /** 195 | * Specifies whether font should be scaled down automatically to fit given style constraints. 196 | * @platform ios 197 | */ 198 | adjustsFontSizeToFit: PropTypes.bool, 199 | 200 | /** 201 | * Specifies smallest possible scale a font can reach when adjustsFontSizeToFit is enabled. (values 0.01-1.0). 202 | * @platform ios 203 | */ 204 | minimumFontScale: PropTypes.number, 205 | 206 | html: React.PropTypes.string, 207 | plaintext: React.PropTypes.string, 208 | }, 209 | getDefaultProps(): Object { 210 | return { 211 | accessible: true, 212 | allowFontScaling: true, 213 | ellipsizeMode: 'tail', 214 | }; 215 | }, 216 | getInitialState: function(): Object { 217 | return mergeFast(Touchable.Mixin.touchableGetInitialState(), { 218 | isHighlighted: false, 219 | }); 220 | }, 221 | mixins: [NativeMethodsMixin], 222 | viewConfig: viewConfig, 223 | getChildContext(): Object { 224 | return {isInAParentText: true}; 225 | }, 226 | childContextTypes: { 227 | isInAParentText: PropTypes.bool 228 | }, 229 | contextTypes: { 230 | isInAParentText: PropTypes.bool 231 | }, 232 | /** 233 | * Only assigned if touch is needed. 234 | */ 235 | _handlers: (null: ?Object), 236 | _hasPressHandler(): boolean { 237 | return !!this.props.onPress || !!this.props.onLongPress; 238 | }, 239 | /** 240 | * These are assigned lazily the first time the responder is set to make plain 241 | * text nodes as cheap as possible. 242 | */ 243 | touchableHandleActivePressIn: (null: ?Function), 244 | touchableHandleActivePressOut: (null: ?Function), 245 | touchableHandlePress: (null: ?Function), 246 | touchableHandleLongPress: (null: ?Function), 247 | touchableGetPressRectOffset: (null: ?Function), 248 | render(): React.Element { 249 | let newProps = this.props; 250 | if (this.props.onStartShouldSetResponder || this._hasPressHandler()) { 251 | if (!this._handlers) { 252 | this._handlers = { 253 | onStartShouldSetResponder: (): bool => { 254 | const shouldSetFromProps = this.props.onStartShouldSetResponder && 255 | this.props.onStartShouldSetResponder(); 256 | const setResponder = shouldSetFromProps || this._hasPressHandler(); 257 | if (setResponder && !this.touchableHandleActivePressIn) { 258 | // Attach and bind all the other handlers only the first time a touch 259 | // actually happens. 260 | for (const key in Touchable.Mixin) { 261 | if (typeof Touchable.Mixin[key] === 'function') { 262 | (this: any)[key] = Touchable.Mixin[key].bind(this); 263 | } 264 | } 265 | this.touchableHandleActivePressIn = () => { 266 | if (this.props.suppressHighlighting || !this._hasPressHandler()) { 267 | return; 268 | } 269 | this.setState({ 270 | isHighlighted: true, 271 | }); 272 | }; 273 | 274 | this.touchableHandleActivePressOut = () => { 275 | if (this.props.suppressHighlighting || !this._hasPressHandler()) { 276 | return; 277 | } 278 | this.setState({ 279 | isHighlighted: false, 280 | }); 281 | }; 282 | 283 | this.touchableHandlePress = (e: SyntheticEvent) => { 284 | this.props.onPress && this.props.onPress(e); 285 | }; 286 | 287 | this.touchableHandleLongPress = (e: SyntheticEvent) => { 288 | this.props.onLongPress && this.props.onLongPress(e); 289 | }; 290 | 291 | this.touchableGetPressRectOffset = function(): RectOffset { 292 | return this.props.pressRetentionOffset || PRESS_RECT_OFFSET; 293 | }; 294 | } 295 | return setResponder; 296 | }, 297 | onResponderGrant: function(e: SyntheticEvent, dispatchID: string) { 298 | this.touchableHandleResponderGrant(e, dispatchID); 299 | this.props.onResponderGrant && 300 | this.props.onResponderGrant.apply(this, arguments); 301 | }.bind(this), 302 | onResponderMove: function(e: SyntheticEvent) { 303 | this.touchableHandleResponderMove(e); 304 | this.props.onResponderMove && 305 | this.props.onResponderMove.apply(this, arguments); 306 | }.bind(this), 307 | onResponderRelease: function(e: SyntheticEvent) { 308 | this.touchableHandleResponderRelease(e); 309 | this.props.onResponderRelease && 310 | this.props.onResponderRelease.apply(this, arguments); 311 | }.bind(this), 312 | onResponderTerminate: function(e: SyntheticEvent) { 313 | this.touchableHandleResponderTerminate(e); 314 | this.props.onResponderTerminate && 315 | this.props.onResponderTerminate.apply(this, arguments); 316 | }.bind(this), 317 | onResponderTerminationRequest: function(): bool { 318 | // Allow touchable or props.onResponderTerminationRequest to deny 319 | // the request 320 | var allowTermination = this.touchableHandleResponderTerminationRequest(); 321 | if (allowTermination && this.props.onResponderTerminationRequest) { 322 | allowTermination = this.props.onResponderTerminationRequest.apply(this, arguments); 323 | } 324 | return allowTermination; 325 | }.bind(this), 326 | }; 327 | } 328 | newProps = { 329 | ...this.props, 330 | ...this._handlers, 331 | isHighlighted: this.state.isHighlighted, 332 | }; 333 | } 334 | if (newProps.selectionColor != null) { 335 | newProps = { 336 | ...newProps, 337 | selectionColor: processColor(newProps.selectionColor) 338 | }; 339 | } 340 | if (Touchable.TOUCH_TARGET_DEBUG && newProps.onPress) { 341 | newProps = { 342 | ...newProps, 343 | style: [this.props.style, {color: 'magenta'}], 344 | }; 345 | } 346 | if (this.context.isInAParentText) { 347 | return ; 348 | } else { 349 | return ; 350 | } 351 | }, 352 | }); 353 | 354 | type RectOffset = { 355 | top: number, 356 | left: number, 357 | right: number, 358 | bottom: number, 359 | } 360 | 361 | var PRESS_RECT_OFFSET = {top: 20, left: 20, right: 20, bottom: 30}; 362 | 363 | var ReactMojiText = createReactNativeComponentClass(viewConfig); 364 | var RCTVirtualText = ReactMojiText; 365 | 366 | if (Platform.OS === 'android') { 367 | RCTVirtualText = createReactNativeComponentClass({ 368 | validAttributes: mergeFast(ReactNativeViewAttributes.UIView, { 369 | isHighlighted: true, 370 | }), 371 | uiViewClassName: 'ReactMojiText', 372 | }); 373 | } 374 | 375 | module.exports = MakemojiText; 376 | -------------------------------------------------------------------------------- /MakemojiRN/MakemojiTextCelliOS.js: -------------------------------------------------------------------------------- 1 | // MapView.js 2 | import React from 'react'; 3 | import { requireNativeComponent,View } from 'react-native'; 4 | 5 | class MakemojiTextCelliOS extends React.Component { 6 | render() { 7 | return ; 8 | } 9 | } 10 | 11 | MakemojiTextCelliOS.propTypes = { 12 | ...View.propTypes, 13 | html: React.PropTypes.string, 14 | plaintext:React.PropTypes.string, 15 | }; 16 | 17 | var RCTTextCell = requireNativeComponent('MESimpleTableViewCell', MakemojiTextCelliOS); 18 | 19 | module.exports = MakemojiTextCelliOS; -------------------------------------------------------------------------------- /MakemojiRN/MakemojiTextInput.js: -------------------------------------------------------------------------------- 1 | import { PropTypes } from 'react'; 2 | const ColorPropType = require('ColorPropType'); 3 | import React, { Component } from 'react'; 4 | import { requireNativeComponent, findNodeHandle, View, BackAndroid, NativeModules } from 'react-native'; 5 | 6 | var ReactNative = require('ReactNative'); 7 | const UIManager = require('UIManager'); 8 | var NativeComponent = requireNativeComponent('RCTMojiInputLayout', null); 9 | export default class MakemojiTextInput extends React.Component { 10 | constructor(props) { 11 | super(props); 12 | this.state={canGoBack:false}; 13 | this.canGoBack = this.canGoBack.bind(this); 14 | this.onCanGoBackChanged = this.onCanGoBackChanged.bind(this); 15 | this.onBackPressed = this.onBackPressed.bind(this); 16 | this.onSendPress = this.onSendPress.bind(this); 17 | this.onCameraPress = this.onCameraPress.bind(this); 18 | this.onHypermojiPress = this.onHypermojiPress.bind(this); 19 | this.props.tag = this.props.tag || 'MakemojiTextInput'; 20 | this.detatch= this.detatch.bind(this); 21 | 22 | 23 | } 24 | onSendPress(e) { 25 | if (this.props.onSendPress) { 26 | this.props.onSendPress(e.nativeEvent); 27 | } 28 | } 29 | onCameraPress(e){ 30 | if (this.props.onCameraPress) { 31 | this.props.onCameraPress(e.nativeEvent); 32 | } 33 | }; 34 | onHypermojiPress(e) { 35 | if (this.props.onHypermojiPress) { 36 | this.props.onHypermojiPress(e.nativeEvent); 37 | } 38 | }; 39 | 40 | onCanGoBackChanged(e){ 41 | this.setState({canGoBack:e.nativeEvent.canGoBack}); 42 | } 43 | canGoBack(){ 44 | return this.state.canGoBack; 45 | } 46 | onBackPressed(){ 47 | UIManager.dispatchViewManagerCommand( 48 | ReactNative.findNodeHandle(this), 49 | 85,[] 50 | ); 51 | } 52 | 53 | render() { 54 | return ; 58 | } 59 | 60 | detatch(tag){ 61 | 62 | NativeModules.MakemojiManager.detatch(findNodeHandle(this.refs[this.props.tag]),tag); 63 | } 64 | getHandle (){ 65 | return ReactNative.findNodeHandle(this.refs[this.props.ref]); 66 | }; 67 | 68 | } 69 | MakemojiTextInput.propTypes = { 70 | ...View.propTypes, 71 | //ios+Android 72 | 73 | onSendPress: React.PropTypes.func, 74 | onCameraPress: React.PropTypes.func, 75 | //onHypermojiPress: React.PropTypes.func,//Use event emitter listener instead 76 | cameraVisible:React.PropTypes.bool,//show the camera button 77 | 78 | //ios only 79 | sendButtonVisible:React.PropTypes.bool, 80 | 81 | textInputTextColor:ColorPropType,//color of input text 82 | placeholderTextColor:ColorPropType, //color of text hint 83 | textSolidBackgroundColor:ColorPropType, //color of bg for text input view 84 | textInputContainerColor:ColorPropType, //color of bg behind input, camera, send 85 | //barBackgroundColor:ColorPropType, 86 | navigationBackgroundColor:ColorPropType,//left button bg color 87 | navigationHighlightColor:ColorPropType,//bg color of selected button 88 | accessoryBackgroundColor:ColorPropType, 89 | flashtagCollectionViewBackgroundColor:ColorPropType,//bg during !flashtag search 90 | emojiViewBackgroundColor:ColorPropType,//bg of emoji bar 91 | emojiPageBackgroundColor:ColorPropType,//bg of emoji pages 92 | emojiCollectionBackgroundColor:ColorPropType,//bg of grid of emojis 93 | categoriesBackgroundColor:ColorPropType,//bg of categories page 94 | detatchedInputId:React.PropTypes.number, 95 | 96 | //Android only 97 | cameraDrawable:React.PropTypes.string,//name of asset in drawables folder 98 | backspaceDrawable:React.PropTypes.string,//name of asset in drawables folder 99 | buttonContainerDrawable:React.PropTypes.string,//the background drawable name behind the left buttons 100 | topBarDrawable:React.PropTypes.string, //Background resource of top [camera,text,sendButton] view 101 | bottomPageDrawable:React.PropTypes.string, //background res of emoji bar and category pages 102 | phraseBgColor:React.PropTypes.string,//color of phrase category backing, in hex 103 | headerTextColor:React.PropTypes.string,//color of page header text, hex 104 | alwaysShowEmojiBar:React.PropTypes.bool,//always shows bar even when kb isn't up 105 | minSendLength:React.PropTypes.number,//number of characters needed to enable send button 106 | outsideEditText:React.PropTypes.string,//the tagFinder prop of a MakeMojiEditTextAndroid to use as input target, null if none 107 | }; 108 | 109 | module.exports = MakemojiTextInput -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Makemoji-React-Native 2 | 3 | ![](http://makemoji.com/docs/img/Intro.png) 4 | 5 | 6 | The Makemoji in-app keyboard is the core of our SDK. It is a dynamically controlled emoji keyboard with an tightly integrated text input that is backed by our CMS and Dashboard. You can create your own categories, upload emoji/gifs and track their usage along side unicode emoji. 7 | 8 | ## Installation 9 | Copy the folder [MakeMojiRN](https://github.com/makemoji/Makemoji-React-Native/tree/master/MakemojiRN) for the js files. 10 | Call NativeModules.MakemojiManager.init("YourSdkKey"); when your application will mount. 11 | 12 | ### Android 13 | 14 | Copy the folder [com/makemoji/mojilib](https://github.com/makemoji/Makemoji-React-Native/tree/master/android/app/src/main/java/com/makemoji/mojilib) into your android/app/src/main/java folder. 15 | 16 | In your MainApplication.java add the MakeMojiReactPackage to the list of packages. 17 | ```java 18 | @Override 19 | protected List getPackages() { 20 | return Arrays.asList( 21 | new MainReactPackage(), new MakeMojiReactPackage(MainApplication.this) 22 | ); 23 | } 24 | }; 25 | ``` 26 | Add the latest version of the sdk to your app's build.gradle 27 | 28 | [ ![MakeMoji SDK Version](https://api.bintray.com/packages/mm/maven/com.makemoji%3Amakemoji-sdk-android/images/download.svg) ](https://bintray.com/mm/maven/com.makemoji%3Amakemoji-sdk-android/_latestVersion) 29 | ``` compile "com.makemoji:makemoji-sdk-android:x.x.xxx" ``` 30 | 31 | ### iOS 32 | 33 | Copy the folder [Makemoji](https://github.com/makemoji/Makemoji-React-Native/tree/master/ios/Makemoji) to your xcode project. Right click on your project 34 | and select 'Add Files' and select all the files in the folder. 35 | Add 'pod "Makemoji-SDK"' to your podfile, and run 'pod install'. 36 | 37 | 38 | ## Usage 39 | Add the MakemojiTextInput component to your layout, with props pointing to functions to handle the camera button press and the send button being pressed. 40 | The result object contains an html field and a plaintext field, either of which can be rendered. 41 | Add one to a datasource to render in a list. 42 | ```javascript 43 | 44 | 47 | componentWillMount(){ 48 | this.subscription = emitter.addListener( 49 | 'onHypermojiPress', 50 | (event) => console.log(event.url) 51 | ); 52 | this.wallSubscription = emitter.addListener( 53 | 'onEmojiWallSelect', 54 | (event) => console.log(event) 55 | ); 56 | this.backListener = BackAndroid.addEventListener('hardwareBackPress', () => { 57 | if (this.refs.mojiInput && this.refs.mojiInput.canGoBack()){ 58 | this.refs.mojiInput.onBackPressed(); 59 | return true;//back handled 60 | } 61 | return false; 62 | }); 63 | } 64 | componentWillUnmount(){ 65 | this.subscription.remove(); 66 | this.wallSubscription.remove(); 67 | BackAndroid.removeEventListener(this.backListener); 68 | } 69 | 70 | sendPressed(sendObject){ 71 | console.log('send pressed', sendObject); 72 | var htmlMessages = [...this.state.htmlMessages,sendObject.html]; 73 | var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}); 74 | this.setState({htmlMessages:htmlMessages,dataSource:ds.cloneWithRows(htmlMessages)}); 75 | } 76 | /* sample send object 77 | { 78 | html:"

watching

" 79 | plaintext:"[Street Fighter.Bq][Chill.2a7] watching" 80 | } 81 | */ 82 | ``` 83 | 84 | ### Displaying Messages 85 | Use either the html prop OR the plaintext prop to render the message. On ios, use the MakemojiTextCelliOS component, and on android, use MakemojiTextAndroid. 86 | ```javascript 87 | 88 | 89 | 90 | 91 | ``` 92 | 93 | ### Hypermoji 94 | Hypermoji are flashing emojis associated with a url. 95 | Presses are sent as an event that can be listened for with the event emitter 96 | ```javascript 97 | componentDidMount(){ 98 | var emitter = new NativeEventEmitter(NativeModules.MakemojiManager); 99 | this.subscription = emitter.addListener( 100 | 'onHypermojiPress', 101 | (event) => console.log(event.url) 102 | ); 103 | } 104 | componentWillUnmount(){ 105 | this.subscription.remove(); 106 | } 107 | ``` 108 | 109 | ### Emoji Wall 110 | ![](http://makemoji.com/docs/img/emojiwall.png) 111 | To show the user a full screen modal or activity to choose one emoji, call 112 | ```javascript 113 | NativeModules.MakemojiManager.openWall() 114 | ``` 115 | and listen for the result 116 | 117 | ```javascript 118 | componentDidMount(){ 119 | var emitter = new NativeEventEmitter(NativeModules.MakemojiManager); 120 | this.wallSubscription = emitter.addListener( 121 | 'onEmojiWallSelect', 122 | (event) => console.log(event) 123 | ); 124 | } 125 | componentWillUnmount(){ 126 | this.wallSubscription.remove(); 127 | } 128 | /* Sample output 129 | { 130 | "emoji_id" = 935; 131 | "emoji_type" = "makemoji"; 132 | "image_url" = "http://d1tvcfe0bfyi6u.cloudfront.net/emoji/935-large@2x.png"; 133 | "name" = "Amused"; 134 | } 135 | */ 136 | ``` 137 | On android, add the wall activity to your manifest first. 138 | ```xml 139 | 142 | 143 | ``` 144 | 145 | ### Reactions 146 | 147 | Makemoji reactions allow you to add inline emoji reactions to any view. This is a great component for getting quick user feedback on any type of content. It uses our emoji wall to let the user react with any emoji you have in your library. 148 | ![](http://i.imgur.com/MCQttIW.png) 149 | 150 | Set up the wall as described above to allow users to add an arbitrary reaction from the '+' button. 151 | The contentId is the unique id from you server identifying the content that is being reacted to. 152 | The height in the style should be 30, give or take 5. 153 | ```javascript 154 | 155 | ``` 156 | 157 | 158 | ### Detatched Input 159 | If you want to use an input target other than MakemojiTextInput on android, things are a bit trickier. A MakemojiEditTextAndroid is required 160 | to ensure keyboard, backspace, and copy paste compatibility. Assign the view a static unique identifier in the finderTag prop. 161 | 162 | ```javascript 163 | 165 | ``` 166 | Then, after mount, assign MakemojiTextInput the prop outsideEditText with the same finderTag value. 167 | This will cause the input field, camera button, and send button to be hidden from MakemojiTextInput. 168 | Set outsideEditText={null} to reverse 169 | this. 170 | ```javascript 171 | this.setState({outsideEditText:'topEditText'}); 172 | ... 173 | 174 | 175 | ``` 176 | 177 | To get the {html,plaintext} value of the MakemojiEditTextAndroid, call requestHtml(shouldClear,shouldSendAnalytics) on the view. 178 | The result will be returned asynchronously through the function given to the onHtmlGenerated prop. 179 | If you need to prepopulate a MakemojiEditTextAndroid, use its setText function. 180 | Detatched input on ios coming soon™. 181 | 182 | ## Customization 183 | MakemojiTextAndroid and MakemojiEditTextAndroid are copy paste extensions of the Text and TextInput components and respond to standard styling 184 | and custmization. Descriptions of the styling available for MakemojiTextInput can be found next to the proptypes of 185 | [MakemojiTextInput.js](https://github.com/makemoji/Makemoji-React-Native/blob/master/MakemojiRN/MakemojiTextInput.js). Any other styling is available on request. 186 | -------------------------------------------------------------------------------- /__tests__/index.android.js: -------------------------------------------------------------------------------- 1 | import 'react-native'; 2 | import React from 'react'; 3 | import Index from '../index.android.js'; 4 | 5 | // Note: test renderer must be required after react-native. 6 | import renderer from 'react-test-renderer'; 7 | 8 | it('renders correctly', () => { 9 | const tree = renderer.create( 10 | 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /__tests__/index.ios.js: -------------------------------------------------------------------------------- 1 | import 'react-native'; 2 | import React from 'react'; 3 | import Index from '../index.ios.js'; 4 | 5 | // Note: test renderer must be required after react-native. 6 | import renderer from 'react-test-renderer'; 7 | 8 | it('renders correctly', () => { 9 | const tree = renderer.create( 10 | 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | /.idea -------------------------------------------------------------------------------- /android/app/BUCK: -------------------------------------------------------------------------------- 1 | # To learn about Buck see [Docs](https://buckbuild.com/). 2 | # To run your application with Buck: 3 | # - install Buck 4 | # - `npm start` - to start the packager 5 | # - `cd android` 6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 8 | # - `buck install -r android/app` - compile, install and run application 9 | # 10 | 11 | lib_deps = [] 12 | 13 | for jarfile in glob(['libs/*.jar']): 14 | name = 'jars__' + jarfile[jarfile.rindex('/') + 1: jarfile.rindex('.jar')] 15 | lib_deps.append(':' + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | 21 | for aarfile in glob(['libs/*.aar']): 22 | name = 'aars__' + aarfile[aarfile.rindex('/') + 1: aarfile.rindex('.aar')] 23 | lib_deps.append(':' + name) 24 | android_prebuilt_aar( 25 | name = name, 26 | aar = aarfile, 27 | ) 28 | 29 | android_library( 30 | name = "all-libs", 31 | exported_deps = lib_deps, 32 | ) 33 | 34 | android_library( 35 | name = "app-code", 36 | srcs = glob([ 37 | "src/main/java/**/*.java", 38 | ]), 39 | deps = [ 40 | ":all-libs", 41 | ":build_config", 42 | ":res", 43 | ], 44 | ) 45 | 46 | android_build_config( 47 | name = "build_config", 48 | package = "com.makemojireactnative", 49 | ) 50 | 51 | android_resource( 52 | name = "res", 53 | package = "com.makemojireactnative", 54 | res = "src/main/res", 55 | ) 56 | 57 | android_binary( 58 | name = "app", 59 | keystore = "//android/keystores:debug", 60 | manifest = "src/main/AndroidManifest.xml", 61 | package_type = "debug", 62 | deps = [ 63 | ":app-code", 64 | ], 65 | ) 66 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | 3 | import com.android.build.OutputFile 4 | 5 | /** 6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets 7 | * and bundleReleaseJsAndAssets). 8 | * These basically call `react-native bundle` with the correct arguments during the Android build 9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the 10 | * bundle directly from the development server. Below you can see all the possible configurations 11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the 12 | * `apply from: "../../node_modules/react-native/react.gradle"` line. 13 | * 14 | * project.ext.react = [ 15 | * // the name of the generated asset file containing your JS bundle 16 | * bundleAssetName: "index.android.bundle", 17 | * 18 | * // the entry file for bundle generation 19 | * entryFile: "index.android.js", 20 | * 21 | * // whether to bundle JS and assets in debug mode 22 | * bundleInDebug: false, 23 | * 24 | * // whether to bundle JS and assets in release mode 25 | * bundleInRelease: true, 26 | * 27 | * // whether to bundle JS and assets in another build variant (if configured). 28 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants 29 | * // The configuration property can be in the following formats 30 | * // 'bundleIn${productFlavor}${buildType}' 31 | * // 'bundleIn${buildType}' 32 | * // bundleInFreeDebug: true, 33 | * // bundleInPaidRelease: true, 34 | * // bundleInBeta: true, 35 | * 36 | * // the root of your project, i.e. where "package.json" lives 37 | * root: "../../", 38 | * 39 | * // where to put the JS bundle asset in debug mode 40 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", 41 | * 42 | * // where to put the JS bundle asset in release mode 43 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release", 44 | * 45 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 46 | * // require('./image.png')), in debug mode 47 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", 48 | * 49 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 50 | * // require('./image.png')), in release mode 51 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", 52 | * 53 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means 54 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to 55 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle 56 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ 57 | * // for example, you might want to remove it from here. 58 | * inputExcludes: ["android/**", "ios/**"], 59 | * 60 | * // override which node gets called and with what additional arguments 61 | * nodeExecutableAndArgs: ["node"] 62 | * 63 | * // supply additional arguments to the packager 64 | * extraPackagerArgs: [] 65 | * ] 66 | */ 67 | 68 | apply from: "../../node_modules/react-native/react.gradle" 69 | 70 | /** 71 | * Set this to true to create two separate APKs instead of one: 72 | * - An APK that only works on ARM devices 73 | * - An APK that only works on x86 devices 74 | * The advantage is the size of the APK is reduced by about 4MB. 75 | * Upload all the APKs to the Play Store and people will download 76 | * the correct one based on the CPU architecture of their device. 77 | */ 78 | def enableSeparateBuildPerCPUArchitecture = false 79 | 80 | /** 81 | * Run Proguard to shrink the Java bytecode in release builds. 82 | */ 83 | def enableProguardInReleaseBuilds = false 84 | 85 | android { 86 | compileSdkVersion 25 87 | buildToolsVersion '25.0.0' 88 | 89 | defaultConfig { 90 | applicationId "com.makemojireactnative" 91 | minSdkVersion 16 92 | targetSdkVersion 22 93 | versionCode 1 94 | versionName "1.0" 95 | ndk { 96 | abiFilters "armeabi-v7a", "x86" 97 | } 98 | } 99 | splits { 100 | abi { 101 | reset() 102 | enable enableSeparateBuildPerCPUArchitecture 103 | universalApk false // If true, also generate a universal APK 104 | include "armeabi-v7a", "x86" 105 | } 106 | } 107 | buildTypes { 108 | release { 109 | minifyEnabled enableProguardInReleaseBuilds 110 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 111 | } 112 | } 113 | // applicationVariants are e.g. debug, release 114 | applicationVariants.all { variant -> 115 | variant.outputs.each { output -> 116 | // For each separate APK per architecture, set a unique version code as described here: 117 | // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits 118 | def versionCodes = ["armeabi-v7a":1, "x86":2] 119 | def abi = output.getFilter(OutputFile.ABI) 120 | if (abi != null) { // null for the universal-debug, universal-release variants 121 | output.versionCodeOverride = 122 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode 123 | } 124 | } 125 | } 126 | } 127 | 128 | dependencies { 129 | compile fileTree(dir: "libs", include: ["*.jar"]) 130 | compile "com.android.support:appcompat-v7:25.3.1" 131 | compile 'com.makemoji:makemoji-sdk-android:0.9.800' 132 | compile "com.facebook.react:react-native:+" // From node_modules 133 | } 134 | 135 | // Run this once to be able to run the application with BUCK 136 | // puts all compile dependencies into folder libs for BUCK to use 137 | task copyDownloadableDepsToLibs(type: Copy) { 138 | from configurations.compile 139 | into 'libs' 140 | } 141 | -------------------------------------------------------------------------------- /android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Disabling obfuscation is useful if you collect stack traces from production crashes 20 | # (unless you are using a system that supports de-obfuscate the stack traces). 21 | -dontobfuscate 22 | 23 | # React Native 24 | 25 | # Keep our interfaces so they can be used by other ProGuard rules. 26 | # See http://sourceforge.net/p/proguard/bugs/466/ 27 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip 28 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters 29 | -keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip 30 | 31 | # Do not strip any method/class that is annotated with @DoNotStrip 32 | -keep @com.facebook.proguard.annotations.DoNotStrip class * 33 | -keep @com.facebook.common.internal.DoNotStrip class * 34 | -keepclassmembers class * { 35 | @com.facebook.proguard.annotations.DoNotStrip *; 36 | @com.facebook.common.internal.DoNotStrip *; 37 | } 38 | 39 | -keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * { 40 | void set*(***); 41 | *** get*(); 42 | } 43 | 44 | -keep class * extends com.facebook.react.bridge.JavaScriptModule { *; } 45 | -keep class * extends com.facebook.react.bridge.NativeModule { *; } 46 | -keepclassmembers,includedescriptorclasses class * { native ; } 47 | -keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; } 48 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; } 49 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; } 50 | 51 | -dontwarn com.facebook.react.** 52 | 53 | # okhttp 54 | 55 | -keepattributes Signature 56 | -keepattributes *Annotation* 57 | -keep class okhttp3.** { *; } 58 | -keep interface okhttp3.** { *; } 59 | -dontwarn okhttp3.** 60 | 61 | # okio 62 | 63 | -keep class sun.misc.Unsafe { *; } 64 | -dontwarn java.nio.file.* 65 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement 66 | -dontwarn okio.** 67 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 19 | 20 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/makemoji/mojilib/MakeMojiModule.java: -------------------------------------------------------------------------------- 1 | package com.makemoji.mojilib; 2 | 3 | import android.app.Activity; 4 | import android.app.Application; 5 | import android.content.Intent; 6 | 7 | import com.facebook.react.bridge.ActivityEventListener; 8 | import com.facebook.react.bridge.Arguments; 9 | import com.facebook.react.bridge.ReactApplicationContext; 10 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 11 | import com.facebook.react.bridge.ReactMethod; 12 | import com.facebook.react.bridge.WritableMap; 13 | import com.facebook.react.modules.core.DeviceEventManagerModule; 14 | import com.makemoji.mojilib.model.MojiModel; 15 | import com.makemoji.mojilib.model.ReactionsData; 16 | import com.makemoji.mojilib.wall.MojiWallActivity; 17 | 18 | import org.json.JSONObject; 19 | 20 | /** 21 | * Created by Makemoji on 9/11/16. 22 | */ 23 | public class MakeMojiModule extends ReactContextBaseJavaModule implements ActivityEventListener { 24 | 25 | Application app; 26 | ReactApplicationContext context; 27 | public MakeMojiModule(ReactApplicationContext reactContext, Application app) { 28 | super(reactContext); 29 | this.app = app; 30 | context = reactContext; 31 | reactContext.addActivityEventListener(this); 32 | } 33 | @Override 34 | public String getName() { 35 | return "MakemojiManager"; 36 | } 37 | @ReactMethod 38 | public void setChannel(String channel) { 39 | Moji.setChannel(channel); 40 | } 41 | @ReactMethod 42 | public void init(String key){ 43 | Moji.initialize(app,key); 44 | } 45 | 46 | @ReactMethod 47 | public void openWall(){ 48 | Intent intent = new Intent(context, MojiWallActivity.class); 49 | //intent.putExtra(MojiWallActivity.EXTRA_THEME,R.style.MojiWallDefaultStyle_Light); //to theme it 50 | intent.putExtra(MojiWallActivity.EXTRA_SHOWRECENT,true);//show recently used emojis as a tab 51 | intent.putExtra(MojiWallActivity.EXTRA_SHOWUNICODE,true);//show unicode emojis as a tab 52 | context.startActivityForResult(intent,IMojiSelected.REQUEST_MOJI_MODEL,null); 53 | } 54 | 55 | @Override 56 | public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { 57 | if (ReactionsData.onActivityResult(requestCode,resultCode,data)){ 58 | return; 59 | } 60 | if (requestCode == IMojiSelected.REQUEST_MOJI_MODEL && resultCode== Activity.RESULT_OK){ 61 | try{ 62 | String json = data.getStringExtra(Moji.EXTRA_JSON); 63 | MojiModel model = MojiModel.fromJson(new JSONObject(json)); 64 | WritableMap eventData = Arguments.createMap(); 65 | eventData.putInt("emoji_id",model.id); 66 | if (model.character==null || model.character.isEmpty())eventData.putString("emoji_type","makemoji"); 67 | else { 68 | eventData.putString("emoji_type","native"); 69 | eventData.putString("unicode_character",model.character); 70 | } 71 | eventData.putString("image_url",model.image_url); 72 | eventData.putString("name",model.name); 73 | 74 | context 75 | .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) 76 | .emit("onEmojiWallSelect", eventData); 77 | } 78 | catch (Exception e){ 79 | e.printStackTrace(); 80 | } 81 | } 82 | } 83 | 84 | @Override 85 | public void onNewIntent(Intent intent) { 86 | 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/makemoji/mojilib/MakeMojiReactPackage.java: -------------------------------------------------------------------------------- 1 | package com.makemoji.mojilib; 2 | 3 | import android.app.Application; 4 | 5 | import com.facebook.react.ReactPackage; 6 | import com.facebook.react.bridge.Arguments; 7 | import com.facebook.react.bridge.JavaScriptModule; 8 | import com.facebook.react.bridge.NativeModule; 9 | import com.facebook.react.bridge.ReactApplicationContext; 10 | import com.facebook.react.bridge.WritableMap; 11 | import com.facebook.react.bridge.WritableNativeMap; 12 | import com.facebook.react.modules.core.DeviceEventManagerModule; 13 | import com.facebook.react.uimanager.ViewManager; 14 | import com.facebook.react.uimanager.events.RCTEventEmitter; 15 | import com.makemoji.mojilib.Moji; 16 | import com.makemoji.mojilib.ReactMojiEditTextManager; 17 | import com.makemoji.mojilib.ReactMojiInputLayoutManager; 18 | import com.makemoji.mojilib.ReactMojiTextViewManager; 19 | 20 | import java.util.ArrayList; 21 | import java.util.Arrays; 22 | import java.util.Collections; 23 | import java.util.List; 24 | 25 | /** 26 | * Created by s_baa on 8/6/2016. 27 | */ 28 | public class MakeMojiReactPackage implements ReactPackage { 29 | Application app; 30 | public MakeMojiReactPackage(Application application){ 31 | super(); 32 | app = application; 33 | } 34 | @Override 35 | public List> createJSModules() { 36 | return Collections.emptyList(); 37 | } 38 | 39 | @Override 40 | public List createViewManagers(ReactApplicationContext reactContext) { 41 | 42 | 43 | return Arrays.asList( 44 | new ReactMojiInputLayoutManager(), new ReactMojiTextViewManager(), new ReactMojiEditTextManager(), 45 | new ReactMojiReactionsViewManager() 46 | ); 47 | } 48 | 49 | @Override 50 | public List createNativeModules( 51 | final ReactApplicationContext reactContext) { 52 | Moji.setDefaultHyperMojiListener(new HyperMojiListener() { 53 | @Override 54 | public void onClick(String s) { 55 | final DeviceEventManagerModule.RCTDeviceEventEmitter emitter = reactContext 56 | .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class); 57 | WritableMap map = Arguments.createMap(); 58 | map.putString("url",s); 59 | emitter.emit("onHypermojiPress",map); 60 | } 61 | }); 62 | List modules = new ArrayList<>(); 63 | modules.add(new MakeMojiModule(reactContext,app)); 64 | 65 | 66 | return modules; 67 | } 68 | } -------------------------------------------------------------------------------- /android/app/src/main/java/com/makemoji/mojilib/MyMojiInputLayout.java: -------------------------------------------------------------------------------- 1 | package com.makemoji.mojilib; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.NonNull; 5 | import android.util.AttributeSet; 6 | 7 | import com.makemoji.mojilib.MojiInputLayout; 8 | 9 | /** 10 | * Created by s_baa on 8/23/2016. 11 | */ 12 | public class MyMojiInputLayout extends MojiInputLayout{ 13 | 14 | public MyMojiInputLayout(Context context) { 15 | super(context); 16 | } 17 | 18 | public MyMojiInputLayout(Context context, AttributeSet attrs) { 19 | super(context, attrs); 20 | } 21 | 22 | public MyMojiInputLayout(Context context, AttributeSet attrs, int defStyle) { 23 | super(context, attrs, defStyle); 24 | } 25 | @Override 26 | public void setRnUpdateListener(RNUpdateListener listener){ 27 | super.setRnUpdateListener(listener); 28 | } 29 | 30 | @Override 31 | public void requestLayout() { 32 | super.requestLayout(); 33 | 34 | // The toolbar relies on a measure + layout pass happening after it calls requestLayout(). 35 | // Without this, certain calls (e.g. setLogo) only take effect after a second invalidation. 36 | post(mLayoutRunnable); 37 | } 38 | private final Runnable mLayoutRunnable = new Runnable() { 39 | @Override 40 | public void run() { 41 | measure( 42 | MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY), 43 | MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY)); 44 | layout(getLeft(), getTop(), getRight(), getBottom()); 45 | } 46 | }; 47 | 48 | } 49 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/makemoji/mojilib/MyReactionsLayout.java: -------------------------------------------------------------------------------- 1 | package com.makemoji.mojilib; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | 6 | /** 7 | * Created by Makemoji on 11/28/16. 8 | */ 9 | public class MyReactionsLayout extends ReactionsLayout { 10 | 11 | public MyReactionsLayout(Context context) { 12 | super(context); 13 | } 14 | 15 | public MyReactionsLayout(Context context, AttributeSet attrs) { 16 | super(context, attrs); 17 | } 18 | 19 | public MyReactionsLayout(Context context, AttributeSet attrs, int defStyleAttr) { 20 | super(context, attrs, defStyleAttr); 21 | } 22 | 23 | public MyReactionsLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 24 | super(context, attrs, defStyleAttr, defStyleRes); 25 | } 26 | @Override 27 | public void requestLayout() { 28 | super.requestLayout(); 29 | 30 | // The toolbar relies on a measure + layout pass happening after it calls requestLayout(). 31 | // Without this, certain calls (e.g. setLogo) only take effect after a second invalidation. 32 | post(mLayoutRunnable); 33 | } 34 | private final Runnable mLayoutRunnable = new Runnable() { 35 | @Override 36 | public void run() { 37 | measure( 38 | MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY), 39 | MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY)); 40 | layout(getLeft(), getTop(), getRight(), getBottom()); 41 | } 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/makemoji/mojilib/ReactMojiEditText.java: -------------------------------------------------------------------------------- 1 | package com.makemoji.mojilib; 2 | 3 | import android.content.ClipData; 4 | import android.content.ClipboardManager; 5 | import android.content.Context; 6 | import android.os.Bundle; 7 | import android.os.Parcelable; 8 | import android.text.Editable; 9 | import android.text.SpannableStringBuilder; 10 | import android.text.Spanned; 11 | import android.text.TextUtils; 12 | import android.text.TextWatcher; 13 | import android.util.AttributeSet; 14 | import android.util.Log; 15 | import android.view.inputmethod.EditorInfo; 16 | 17 | import com.facebook.react.views.textinput.ReactEditText; 18 | 19 | import java.lang.reflect.Field; 20 | import java.lang.reflect.Method; 21 | 22 | /** 23 | * Created by s_baa on 8/27/2016. 24 | */ 25 | public class ReactMojiEditText extends ReactEditText implements ISpecialInvalidate { 26 | public ReactMojiEditText(Context context) { 27 | super(context); 28 | init(); 29 | } 30 | public ReactMojiEditText(Context context, AttributeSet attributeSet){ 31 | super(context); 32 | init(); 33 | } 34 | 35 | Field mEditor; 36 | Object editor; 37 | Method invalidateTextDisplayList; 38 | static Object[] emptyObject = new Object[]{}; 39 | public void invalidateReflect(){ 40 | if (invalidateTextDisplayList!=null && mEditor!=null && editor!=null) 41 | try{ 42 | invalidateTextDisplayList.invoke(editor,emptyObject); 43 | } 44 | catch (Exception e){ 45 | // e.printStackTrace(); 46 | 47 | } 48 | } 49 | private void init(){ 50 | try { 51 | mEditor = getClass().getSuperclass().getSuperclass().getSuperclass().getDeclaredField("mEditor"); 52 | mEditor.setAccessible(true); 53 | Class c = mEditor.getType(); 54 | invalidateTextDisplayList = c.getDeclaredMethod("invalidateTextDisplayList"); 55 | invalidateTextDisplayList.setAccessible(true); 56 | editor = mEditor.get(this); 57 | } 58 | catch (Exception e){ 59 | // e.printStackTrace(); 60 | } 61 | //If any mojispans span less than three characters, remove them because a backspace has happened. 62 | setImeOptions(getImeOptions()| EditorInfo.IME_FLAG_NO_EXTRACT_UI|EditorInfo.IME_FLAG_NO_FULLSCREEN); 63 | addTextChangedListener(new TextWatcher() { 64 | @Override 65 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {} 66 | @Override 67 | public void onTextChanged(CharSequence s, int start, int before, int count) {} 68 | @Override 69 | public void afterTextChanged(Editable s) { 70 | String string = s.toString(); 71 | SpannableStringBuilder ssb = new SpannableStringBuilder(s); 72 | SpannableStringBuilder builder = new SpannableStringBuilder(); 73 | 74 | for (int i = 0; i < string.length();) { 75 | MojiSpan spanAtPoint[] = ssb.getSpans(i, i + 1, MojiSpan.class); 76 | if (spanAtPoint.length == 0) {//not a moji 77 | builder.append(s.charAt(i)); 78 | i++; 79 | } 80 | else{ 81 | MojiSpan span = spanAtPoint[0]; 82 | int start = ssb.getSpanStart(span); 83 | int end = ssb.getSpanEnd(span); 84 | int spanLength = end-start; 85 | if (spanLength==3) {//valid emoji, add it 86 | builder.append(ssb.subSequence(start, end)); 87 | } 88 | i+=spanLength;//invalid emoji, skip 89 | } 90 | } 91 | if (ssb.length()>builder.length()){//mojis have been deleted 92 | int selection = getSelectionStart()-(ssb.length()-builder.length()); 93 | setText(builder); 94 | setSelection(Math.max(0,Math.min(selection,getText().length()))); 95 | } 96 | } 97 | }); 98 | } 99 | 100 | 101 | public static Character replacementChar = "\uFFFC".charAt(0); 102 | 103 | @Override 104 | public boolean onTextContextMenuItem(int id) { 105 | int min = 0; 106 | int max = length(); 107 | 108 | if (isFocused()) { 109 | final int selStart = getSelectionStart(); 110 | final int selEnd = getSelectionEnd(); 111 | 112 | min = Math.max(0, Math.min(selStart, selEnd)); 113 | max = Math.max(0, Math.max(selStart, selEnd)); 114 | } 115 | 116 | //convert from html, paste 117 | if (id == android.R.id.paste) { 118 | ClipboardManager clipboard = 119 | (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE); 120 | ClipData clip = clipboard.getPrimaryClip(); 121 | if (clip==null || clip.getItemCount()==0)return true; 122 | String paste = clip.getItemAt(0).coerceToText(getContext()).toString(); 123 | 124 | ParsedAttributes pa = Moji.parseHtml(paste,this,true,true); 125 | 126 | SpannableStringBuilder original = new SpannableStringBuilder(getText()); 127 | Spanned newText = new SpannableStringBuilder 128 | ( TextUtils.concat(original.subSequence(0,min), 129 | pa.spanned, 130 | original.subSequence(max,original.length()))); 131 | setText(newText); 132 | setSelection(Math.min(min+pa.spanned.length(),getText().length())); 133 | Moji.subSpanimatable(newText,this); 134 | stopActionMode(); 135 | return true; 136 | } 137 | //convert to html, copy 138 | if (id == android.R.id.copy || id == android.R.id.cut) { 139 | SpannableStringBuilder text = new SpannableStringBuilder(getText().subSequence(min, max)); 140 | 141 | Log.d("met copy","met copy " +text.toString()); 142 | 143 | String html = Moji.toHtml(text); 144 | ClipData clip = ClipData.newPlainText(null, html); 145 | ClipboardManager clipboard = (ClipboardManager) getContext(). 146 | getSystemService(Context.CLIPBOARD_SERVICE); 147 | clipboard.setPrimaryClip(clip); 148 | 149 | if (id == android.R.id.cut){ 150 | setText(getText().delete(min,max)); 151 | setSelection(Math.min(min,getText().length())); 152 | } 153 | stopActionMode(); 154 | return true; 155 | } 156 | return super.onTextContextMenuItem(id); 157 | } 158 | private void stopActionMode(){ 159 | try{ 160 | Method m = getClass().getSuperclass().getSuperclass().getDeclaredMethod("stopTextActionMode",null); 161 | m.setAccessible(true); 162 | m.invoke(this,null); 163 | } 164 | catch (Exception e){ 165 | //e.printStackTrace(); 166 | } 167 | 168 | } 169 | 170 | @Override 171 | public Parcelable onSaveInstanceState() 172 | { 173 | Bundle bundle = new Bundle(); 174 | bundle.putParcelable("superState", super.onSaveInstanceState()); 175 | bundle.putString("html",Moji.toHtml(getText())); 176 | return bundle; 177 | } 178 | 179 | @Override 180 | public void onRestoreInstanceState(Parcelable state) 181 | { 182 | String html = null; 183 | if (state instanceof Bundle) 184 | { 185 | Bundle bundle = (Bundle) state; 186 | html = bundle.getString("html",null); 187 | state = bundle.getParcelable("superState"); 188 | } 189 | super.onRestoreInstanceState(state); 190 | if (html ==null) return; 191 | final String storedHtml = html; 192 | post(new Runnable() {//let the ui thread handle it when it's free. 193 | @Override 194 | public void run() { 195 | Moji.setText(storedHtml,ReactMojiEditText.this,true,true); 196 | setSelection(getText().length()); 197 | } 198 | }); 199 | 200 | } 201 | protected void onSelectionChanged(int selStart, int selEnd) { 202 | // Log.d("met","selection " + selStart + " " +(selStart %3)); 203 | } 204 | 205 | @Override 206 | public void specialInvalidate() { 207 | invalidateReflect(); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/makemoji/mojilib/ReactMojiEditTextManager.java: -------------------------------------------------------------------------------- 1 | package com.makemoji.mojilib; 2 | 3 | import android.support.annotation.Nullable; 4 | import android.text.InputType; 5 | import android.util.TypedValue; 6 | import android.view.View; 7 | import android.view.inputmethod.EditorInfo; 8 | 9 | import com.facebook.react.bridge.Arguments; 10 | import com.facebook.react.bridge.ReadableArray; 11 | import com.facebook.react.bridge.WritableMap; 12 | import com.facebook.react.common.MapBuilder; 13 | import com.facebook.react.uimanager.PixelUtil; 14 | import com.facebook.react.uimanager.ThemedReactContext; 15 | import com.facebook.react.uimanager.UIManagerModule; 16 | import com.facebook.react.uimanager.ViewDefaults; 17 | import com.facebook.react.uimanager.ViewProps; 18 | import com.facebook.react.uimanager.annotations.ReactProp; 19 | import com.facebook.react.uimanager.events.Event; 20 | import com.facebook.react.uimanager.events.EventDispatcher; 21 | import com.facebook.react.uimanager.events.RCTEventEmitter; 22 | import com.facebook.react.views.textinput.ReactEditText; 23 | import com.facebook.react.views.textinput.ReactTextInputManager; 24 | 25 | import java.util.Map; 26 | 27 | /** 28 | * Created by s_baa on 8/26/2016. 29 | */ 30 | public class ReactMojiEditTextManager extends ReactTextInputManager { 31 | EventDispatcher eventDispatcher; 32 | @Override 33 | public String getName() { 34 | return "MakeMojiAndroidTextInput"; 35 | } 36 | 37 | @Override 38 | public ReactEditText createViewInstance(ThemedReactContext context) { 39 | final ReactEditText editText = new ReactMojiEditText(context); 40 | int inputType = editText.getInputType(); 41 | editText.setInputType(inputType & (~InputType.TYPE_TEXT_FLAG_MULTI_LINE)); 42 | editText.setImeOptions(EditorInfo.IME_ACTION_DONE); 43 | editText.setTextSize( 44 | TypedValue.COMPLEX_UNIT_PX, 45 | (int) Math.ceil(PixelUtil.toPixelFromSP(ViewDefaults.FONT_SIZE_SP))); 46 | editText.setTag("defaultTag"); 47 | editText.setOnClickListener(new View.OnClickListener() { 48 | @Override 49 | public void onClick(View v) { 50 | editText.requestFocusFromJS(); 51 | } 52 | }); 53 | return editText; 54 | } 55 | 56 | @ReactProp(name = "finderTag") 57 | public void setFinderTag(ReactEditText view, @Nullable String tag) { 58 | view.setTag(tag); 59 | 60 | } 61 | @ReactProp(name = ViewProps.FONT_SIZE, defaultFloat = ViewDefaults.FONT_SIZE_SP) 62 | public void setFontSize(ReactEditText view, float fontSize) { 63 | String html = Moji.toHtml(view.getText()); 64 | view.setTextSize( 65 | TypedValue.COMPLEX_UNIT_PX, 66 | (int) Math.ceil(PixelUtil.toPixelFromSP(fontSize))); 67 | Moji.setText(html,view,true); 68 | } 69 | @Override 70 | public void updateExtraData(ReactEditText view, Object extraData) { 71 | if (extraData instanceof float[]){ 72 | super.updateExtraData(view,extraData); 73 | return; 74 | } 75 | } 76 | 77 | @Override 78 | public Map getCommandsMap() { 79 | return MapBuilder.of( 80 | "requestHtml", 81 | 404); 82 | } 83 | @Override 84 | public void receiveCommand( 85 | ReactEditText reactEditText, 86 | int commandId, 87 | @javax.annotation.Nullable ReadableArray args) { 88 | switch (commandId) { 89 | case 404: 90 | if (args.getBoolean(1)) MojiInputLayout.onSaveInputToRecentAndsBackend(reactEditText.getText()); 91 | eventDispatcher.dispatchEvent(new HtmlEvent(reactEditText.getId(), 92 | Moji.toHtml(reactEditText.getText()))); 93 | if (args.getBoolean(0))Moji.setText("",reactEditText,true); 94 | break; 95 | case 405: 96 | Moji.setText(args.getString(0),reactEditText,true); 97 | default: 98 | super.receiveCommand(reactEditText,commandId,args); 99 | } 100 | } 101 | @Override 102 | public Map getExportedCustomDirectEventTypeConstants() { 103 | return MapBuilder.of( 104 | HtmlEvent.EVENT_NAME, (Object) MapBuilder.of("registrationName", "onHtmlGenerated") 105 | 106 | ); 107 | } 108 | @Override 109 | protected void addEventEmitters(ThemedReactContext reactContext, ReactEditText view) { 110 | eventDispatcher = reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher(); 111 | } 112 | public static class HtmlEvent extends Event { 113 | String html; 114 | final static String EVENT_NAME = "onHtmlGenerated"; 115 | public HtmlEvent(int viewTag,String html){ 116 | super(viewTag); 117 | this.html = html; 118 | } 119 | @Override 120 | public String getEventName() { 121 | return EVENT_NAME; 122 | } 123 | 124 | @Override 125 | public void dispatch(RCTEventEmitter rctEventEmitter) { 126 | WritableMap eventData = Arguments.createMap(); 127 | eventData.putString("html", html); 128 | eventData.putString("plainText", Moji.htmlToPlainText(html)); 129 | rctEventEmitter.receiveEvent(getViewTag(),getEventName(),eventData); 130 | } 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/makemoji/mojilib/ReactMojiInputLayoutManager.java: -------------------------------------------------------------------------------- 1 | package com.makemoji.mojilib; 2 | 3 | import android.graphics.Color; 4 | import android.os.SystemClock; 5 | import android.support.annotation.Nullable; 6 | import android.text.Editable; 7 | import android.text.InputType; 8 | import android.text.Spanned; 9 | import android.text.TextWatcher; 10 | import android.util.Log; 11 | import android.view.View; 12 | import android.view.ViewParent; 13 | import android.view.ViewTreeObserver; 14 | import android.widget.EditText; 15 | import android.widget.Toast; 16 | 17 | import com.facebook.react.bridge.Arguments; 18 | import com.facebook.react.bridge.ReadableArray; 19 | import com.facebook.react.bridge.WritableMap; 20 | import com.facebook.react.common.MapBuilder; 21 | import com.facebook.react.uimanager.LayoutShadowNode; 22 | import com.facebook.react.uimanager.NativeViewHierarchyManager; 23 | import com.facebook.react.uimanager.PixelUtil; 24 | import com.facebook.react.uimanager.ReactShadowNode; 25 | import com.facebook.react.uimanager.SimpleViewManager; 26 | import com.facebook.react.uimanager.ThemedReactContext; 27 | import com.facebook.react.uimanager.UIBlock; 28 | import com.facebook.react.uimanager.UIImplementation; 29 | import com.facebook.react.uimanager.UIManagerModule; 30 | import com.facebook.react.uimanager.ViewGroupManager; 31 | import com.facebook.react.uimanager.annotations.ReactProp; 32 | import com.facebook.react.uimanager.events.Event; 33 | import com.facebook.react.uimanager.events.EventDispatcher; 34 | import com.facebook.react.uimanager.events.RCTEventEmitter; 35 | 36 | import java.lang.reflect.Method; 37 | import java.util.Map; 38 | 39 | import csslayout.MyShadowNode; 40 | 41 | /** 42 | * Created by s_baa on 8/6/2016. 43 | */ 44 | public class ReactMojiInputLayoutManager extends ViewGroupManager { 45 | EventDispatcher eventDispatcher; 46 | Method markNewLayout, getShadowNode; 47 | 48 | public ReactMojiInputLayoutManager(){ 49 | super(); 50 | if (markNewLayout == null) { 51 | try { 52 | markNewLayout = LayoutShadowNode.class.getSuperclass().getDeclaredMethod("markUpdateSeen"); 53 | markNewLayout.setAccessible(true); 54 | } catch (Exception e) { 55 | e.printStackTrace(); 56 | } 57 | } 58 | try{ 59 | if (getShadowNode==null){ 60 | getShadowNode = UIImplementation.class.getDeclaredMethod("resolveShadowNode",int.class); 61 | getShadowNode.setAccessible(true); 62 | } 63 | } catch (Exception e) { 64 | e.printStackTrace(); 65 | } 66 | 67 | } 68 | 69 | @Override 70 | public boolean needsCustomLayoutForChildren() { 71 | return true; 72 | } 73 | @Override 74 | public String getName() { 75 | return "RCTMojiInputLayout"; 76 | } 77 | 78 | @ReactProp(name = "cameraDrawable") 79 | public void setCameraDrawable(MyMojiInputLayout view, @Nullable String drawableName) { 80 | if (drawableName!=null) 81 | view.cameraImageButton.setImageResource( 82 | view.getResources().getIdentifier(drawableName,"drawable",view.getContext().getPackageName())); 83 | } 84 | @ReactProp(name = "backspaceDrawable") 85 | public void setBackspaceDrawable(MyMojiInputLayout view, @Nullable String drawableName) { 86 | if (drawableName!=null) 87 | view.backSpaceDrawableRes = view.getResources().getIdentifier(drawableName,"drawable",view.getContext().getPackageName()); 88 | } 89 | 90 | @ReactProp(name = "cameraVisible", defaultBoolean = true) 91 | public void setCameraVisibility(MyMojiInputLayout view, boolean visible) { 92 | view.setCameraVisibility(visible); 93 | } 94 | 95 | @ReactProp(name = "buttonContainerDrawable") 96 | public void setButtonContainerDrawable(MyMojiInputLayout view, @Nullable String drawableName) { 97 | if (drawableName!=null) 98 | view.findViewById(R.id._mm_left_buttons).setBackgroundResource( 99 | view.getResources().getIdentifier(drawableName,"drawable",view.getContext().getPackageName())); 100 | } 101 | @ReactProp(name = "topBarDrawable") 102 | public void setTopBarDrawable(MyMojiInputLayout view, @Nullable String drawableName) { 103 | if (drawableName!=null) 104 | view.findViewById(R.id._mm_horizontal_ll).setBackgroundResource( 105 | view.getResources().getIdentifier(drawableName,"drawable",view.getContext().getPackageName())); 106 | } 107 | @ReactProp(name = "bottomPageDrawable") 108 | public void setBottomPageDrawable(MyMojiInputLayout view, @Nullable String drawableName) { 109 | if (drawableName!=null) { 110 | view.rv.setBackgroundResource( 111 | view.getResources().getIdentifier(drawableName, "drawable", view.getContext().getPackageName())); 112 | view.getPageFrame().setBackgroundResource( 113 | view.getResources().getIdentifier(drawableName, "drawable", view.getContext().getPackageName())); 114 | } 115 | } 116 | 117 | @ReactProp(name = "phraseBgColor") 118 | public void setPhraseBgColor(MyMojiInputLayout view, @Nullable String color) { 119 | if (color!=null){ 120 | view.phraseBgColor = Color.parseColor(color); 121 | } 122 | } 123 | @ReactProp(name = "headerTextColor") 124 | public void setHeaderTextColor(MyMojiInputLayout view, @Nullable String color) { 125 | if (color!=null){ 126 | view.headerTextColor = Color.parseColor(color); 127 | } 128 | } 129 | 130 | @ReactProp(name = "alwaysShowEmojiBar", defaultBoolean = false) 131 | public void setAlwaysShowEmojiBar(MyMojiInputLayout view, boolean show) { 132 | view.tryAlwaysShowBar=show; 133 | if (show)view.alwaysShowBar = show; 134 | } 135 | @ReactProp(name = "minSendLength", defaultInt = 0) 136 | public void setMinSendLength(MyMojiInputLayout view, int length) { 137 | view.minimumSendLength = length; 138 | if (length==0) view.sendLayout.setEnabled(view.editText.getText().length()>=length); 139 | } 140 | @ReactProp(name ="outsideEditText") 141 | public void setOutsideEditText(final MyMojiInputLayout view, @Nullable String tag){ 142 | if (tag==null || tag.isEmpty()){ 143 | view.detachMojiEditText(); 144 | view.requestRnUpdate(); 145 | return; 146 | } 147 | ViewParent parent = view.getParent(); 148 | while (parent!=null && parent instanceof View && parent.getParent() !=null && parent.getParent() instanceof View){ 149 | parent = parent.getParent(); 150 | } 151 | if (parent!=null) { 152 | EditText et = (EditText) ((View) parent).findViewWithTag(tag); 153 | if (et!=null) { 154 | view.attatchEditText(et); 155 | view.setTag(R.id._makemoji_request_layout_id,true); 156 | } 157 | view.requestRnUpdate(); 158 | } 159 | 160 | } 161 | @ReactProp(name ="channel") 162 | public void setChannel(final MyMojiInputLayout view, @Nullable String channel){ 163 | Moji.setChannel(channel); 164 | } 165 | @Override 166 | public void receiveCommand( 167 | MyMojiInputLayout mojiInputLayout, 168 | int commandId, 169 | @javax.annotation.Nullable ReadableArray args) { 170 | Log.d(getName(),"command "+commandId); 171 | switch (commandId){ 172 | case 85: 173 | mojiInputLayout.onBackPressed(); 174 | mojiInputLayout.requestRnUpdate(); 175 | break; 176 | default: 177 | super.receiveCommand(mojiInputLayout,commandId,args); 178 | } 179 | } 180 | 181 | @Override 182 | protected MyMojiInputLayout createViewInstance(final ThemedReactContext reactContext) { 183 | 184 | final MyMojiInputLayout mojiInputLayout = new MyMojiInputLayout(reactContext); 185 | 186 | UIManagerModule uiManager = reactContext.getNativeModule(UIManagerModule.class); 187 | final UIImplementation uiImplementation = uiManager.getUIImplementation(); 188 | final MojiEditText et = (MojiEditText) mojiInputLayout.findViewById(R.id._mm_edit_text); 189 | OneGridPage.RNDELAY = 100; 190 | et.setOnClickListener(new View.OnClickListener() { 191 | @Override 192 | public void onClick(View v) { 193 | mojiInputLayout.showKeyboard(); 194 | } 195 | }); 196 | et.setOnFocusChangeListener(new View.OnFocusChangeListener() { 197 | @Override 198 | public void onFocusChange(View v, boolean hasFocus) { 199 | if (!hasFocus) et.clearFocus(); 200 | } 201 | }); 202 | mojiInputLayout.setSendLayoutClickListener(new MojiInputLayout.SendClickListener() { 203 | @Override 204 | public boolean onClick(final String html, Spanned spanned) { 205 | eventDispatcher.dispatchEvent(new SendEvent(mojiInputLayout.getId(),html)); 206 | mojiInputLayout.postDelayed(new Runnable() { 207 | @Override 208 | public void run() { 209 | mojiInputLayout.requestRnUpdate();//update view size when collapsing lines 210 | } 211 | },20); 212 | return true; 213 | } 214 | }); 215 | mojiInputLayout.setCameraButtonClickListener(new View.OnClickListener() { 216 | @Override 217 | public void onClick(View v) { 218 | eventDispatcher.dispatchEvent(new CameraEvent(mojiInputLayout.getId())); 219 | } 220 | }); 221 | 222 | mojiInputLayout.editText.addTextChangedListener(new TextWatcher() { 223 | @Override 224 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 225 | 226 | } 227 | 228 | @Override 229 | public void onTextChanged(CharSequence s, int start, int before, int count) { 230 | 231 | } 232 | 233 | @Override 234 | public void afterTextChanged(Editable s) { 235 | mojiInputLayout.requestRnUpdate();//for new lines 236 | } 237 | }); 238 | mojiInputLayout.setRnUpdateListener(new MojiInputLayout.RNUpdateListener() { 239 | MyShadowNode node; 240 | @Override 241 | public void needsUpdate() { 242 | mojiInputLayout.requestLayout(); 243 | 244 | 245 | Runnable r = new Runnable() { 246 | @Override 247 | public void run() { 248 | 249 | if (node ==null){ 250 | try { 251 | node = (MyShadowNode) getShadowNode.invoke(uiImplementation, mojiInputLayout.getId()); 252 | } 253 | catch (Exception e){ 254 | e.printStackTrace(); 255 | } 256 | } 257 | if (node != null) { 258 | int height = (mojiInputLayout.horizontalLayout.getVisibility()==View.VISIBLE? mojiInputLayout.horizontalLayout.getHeight():0) 259 | +(mojiInputLayout.topScroller.getVisibility()==View.VISIBLE? 260 | (int)PixelUtil.toPixelFromDIP(45):0) + 261 | (mojiInputLayout.pages.size()>0 ? 262 | (int) PixelUtil.toPixelFromDIP(250):0); 263 | 264 | mojiInputLayout.requestLayout(); 265 | uiImplementation.updateNodeSize(mojiInputLayout.getId(),mojiInputLayout.getWidth(),height); 266 | } 267 | eventDispatcher.dispatchEvent(new CanGoBackEvent(mojiInputLayout.getId(),mojiInputLayout.canHandleBack())); 268 | } 269 | 270 | }; 271 | reactContext.runOnNativeModulesQueueThread(r); 272 | } 273 | }); 274 | return mojiInputLayout; 275 | } 276 | public LayoutShadowNode createShadowNodeInstance() { 277 | return new MyShadowNode(); 278 | } 279 | 280 | @Override 281 | protected void addEventEmitters(ThemedReactContext reactContext, MyMojiInputLayout view) { 282 | eventDispatcher = reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher(); 283 | } 284 | 285 | @Override 286 | public Map getExportedCustomDirectEventTypeConstants() { 287 | return MapBuilder.of( 288 | SendEvent.EVENT_NAME, (Object) MapBuilder.of("registrationName", "onSendPress"), 289 | HyperMojiEvent.EVENT_NAME, (Object) MapBuilder.of("registrationName", "onHyperMojiPress"), 290 | CameraEvent.EVENT_NAME, (Object) MapBuilder.of("registrationName", "onCameraPress"), 291 | CanGoBackEvent.EVENT_NAME, (Object) MapBuilder.of("registrationName", "onCanGoBackChanged") 292 | 293 | ); 294 | } 295 | public static class SendEvent extends Event{ 296 | String html; 297 | final static String EVENT_NAME = "onSendPress"; 298 | public SendEvent(int viewTag,String html){ 299 | super(viewTag); 300 | this.html = html; 301 | } 302 | @Override 303 | public String getEventName() { 304 | return EVENT_NAME; 305 | } 306 | 307 | @Override 308 | public void dispatch(RCTEventEmitter rctEventEmitter) { 309 | WritableMap eventData = Arguments.createMap(); 310 | eventData.putString("html", html); 311 | eventData.putString("plaintext", Moji.htmlToPlainText(html)); 312 | rctEventEmitter.receiveEvent(getViewTag(),getEventName(),eventData); 313 | } 314 | } 315 | public static class HyperMojiEvent extends Event{ 316 | String url; 317 | final static String EVENT_NAME = "onHyperMojiPress"; 318 | public HyperMojiEvent(int viewTag,String url){ 319 | super(viewTag); 320 | this.url = url; 321 | } 322 | @Override 323 | public String getEventName() { 324 | return EVENT_NAME; 325 | } 326 | 327 | @Override 328 | public void dispatch(RCTEventEmitter rctEventEmitter) { 329 | WritableMap eventData = Arguments.createMap(); 330 | eventData.putString("url", url); 331 | rctEventEmitter.receiveEvent(getViewTag(),getEventName(),eventData); 332 | } 333 | } 334 | public static class CameraEvent extends Event{ 335 | final static String EVENT_NAME = "onCameraPress"; 336 | public CameraEvent(int viewTag){ 337 | super(viewTag); 338 | } 339 | @Override 340 | public String getEventName() { 341 | return EVENT_NAME; 342 | } 343 | 344 | @Override 345 | public void dispatch(RCTEventEmitter rctEventEmitter) { 346 | WritableMap eventData = Arguments.createMap(); 347 | rctEventEmitter.receiveEvent(getViewTag(),getEventName(),eventData); 348 | } 349 | } 350 | public static class CanGoBackEvent extends Event{ 351 | final static String EVENT_NAME = "onCanGoBackChanged"; 352 | boolean canGoBack; 353 | public CanGoBackEvent(int viewTag,boolean canGoBack){ 354 | super(viewTag); 355 | this.canGoBack = canGoBack; 356 | } 357 | @Override 358 | public String getEventName() { 359 | return EVENT_NAME; 360 | } 361 | 362 | @Override 363 | public void dispatch(RCTEventEmitter rctEventEmitter) { 364 | WritableMap eventData = Arguments.createMap(); 365 | eventData.putBoolean("canGoBack",canGoBack); 366 | rctEventEmitter.receiveEvent(getViewTag(),getEventName(),eventData); 367 | } 368 | } 369 | } 370 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/makemoji/mojilib/ReactMojiReactionsViewManager.java: -------------------------------------------------------------------------------- 1 | package com.makemoji.mojilib; 2 | 3 | import android.support.annotation.Nullable; 4 | import android.util.TypedValue; 5 | 6 | import com.facebook.react.bridge.Arguments; 7 | import com.facebook.react.bridge.WritableMap; 8 | import com.facebook.react.common.MapBuilder; 9 | import com.facebook.react.uimanager.BaseViewManager; 10 | import com.facebook.react.uimanager.PixelUtil; 11 | import com.facebook.react.uimanager.SimpleViewManager; 12 | import com.facebook.react.uimanager.ThemedReactContext; 13 | import com.facebook.react.uimanager.UIManagerModule; 14 | import com.facebook.react.uimanager.ViewDefaults; 15 | import com.facebook.react.uimanager.ViewProps; 16 | import com.facebook.react.uimanager.annotations.ReactProp; 17 | import com.facebook.react.uimanager.events.Event; 18 | import com.facebook.react.uimanager.events.EventDispatcher; 19 | import com.facebook.react.uimanager.events.RCTEventEmitter; 20 | import com.facebook.react.views.text.ReactTextShadowNode; 21 | import com.facebook.react.views.text.ReactTextView; 22 | import com.facebook.react.views.text.ReactTextViewManager; 23 | import com.makemoji.mojilib.model.ReactionsData; 24 | 25 | import java.util.Map; 26 | 27 | import csslayout.MyReactTextShadowNode; 28 | 29 | /** 30 | * Created by s_baa on 8/6/2016. 31 | */ 32 | public class ReactMojiReactionsViewManager extends SimpleViewManager { 33 | @Override 34 | public String getName() { 35 | return "RCTMojiReactions"; 36 | } 37 | 38 | @Override 39 | public MyReactionsLayout createViewInstance(final ThemedReactContext reactContext) { 40 | final MyReactionsLayout reactionsLayout =new MyReactionsLayout(reactContext.getCurrentActivity()); 41 | return reactionsLayout; 42 | } 43 | @ReactProp(name = "contentId") 44 | public void setContentId(final MyReactionsLayout view, @Nullable String id) { 45 | if (id == null) return; 46 | ReactionsData data =new ReactionsData(id); 47 | data.rnUpdateListener = new MojiInputLayout.RNUpdateListener() { 48 | @Override 49 | public void needsUpdate() { 50 | view.requestLayout(); 51 | } 52 | }; 53 | view.setReactionsData(data); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/makemoji/mojilib/ReactMojiTextViewManager.java: -------------------------------------------------------------------------------- 1 | package com.makemoji.mojilib; 2 | 3 | import android.support.annotation.Nullable; 4 | import android.util.Log; 5 | import android.util.TypedValue; 6 | 7 | import com.facebook.react.bridge.Arguments; 8 | import com.facebook.react.bridge.WritableMap; 9 | import com.facebook.react.common.MapBuilder; 10 | import com.facebook.react.uimanager.PixelUtil; 11 | import com.facebook.react.uimanager.ThemedReactContext; 12 | import com.facebook.react.uimanager.UIManagerModule; 13 | import com.facebook.react.uimanager.ViewDefaults; 14 | import com.facebook.react.uimanager.ViewProps; 15 | import com.facebook.react.uimanager.annotations.ReactProp; 16 | import com.facebook.react.uimanager.events.Event; 17 | import com.facebook.react.uimanager.events.EventDispatcher; 18 | import com.facebook.react.uimanager.events.RCTEventEmitter; 19 | import com.facebook.react.views.text.ReactTextShadowNode; 20 | import com.facebook.react.views.text.ReactTextView; 21 | import com.facebook.react.views.text.ReactTextViewManager; 22 | import com.facebook.react.views.textinput.ReactEditText; 23 | 24 | import java.util.Map; 25 | 26 | import csslayout.MyReactTextShadowNode; 27 | 28 | /** 29 | * Created by s_baa on 8/6/2016. 30 | */ 31 | public class ReactMojiTextViewManager extends ReactTextViewManager { 32 | EventDispatcher eventDispatcher; 33 | @Override 34 | public String getName() { 35 | return "ReactMojiText"; 36 | } 37 | @Override 38 | protected void addEventEmitters(ThemedReactContext reactContext, ReactTextView view) { 39 | eventDispatcher = reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher(); 40 | } 41 | 42 | @Override 43 | public ReactTextView createViewInstance(final ThemedReactContext reactContext) { 44 | final ReactTextView rtv =super.createViewInstance(reactContext); 45 | /*rtv.setTag(com.makemojireactnative.R.id._makemoji_hypermoji_listener_tag_id, new HyperMojiListener() { 46 | @Override 47 | public void onClick(String url) { 48 | eventDispatcher.dispatchEvent(new HyperMojiEvent (rtv.getId(),url)); 49 | } 50 | });*/ 51 | return rtv; 52 | } 53 | @ReactProp(name = "html") 54 | public void setHtml(ReactTextView view, @Nullable String html) { 55 | if (html!=null && !html.equals("__VOID__")) {//prop can't be empty 56 | view.setTag(R.id._makemoji_rn_html_tag_id,html); 57 | Moji.setText(html, view, true); 58 | } 59 | } 60 | @ReactProp(name = "plaintext") 61 | public void setPlainText(ReactTextView view, @Nullable String plaintext) { 62 | if (plaintext!=null && !plaintext.equals("__VOID__")) 63 | Moji.setText(Moji.plainTextToSpanned(plaintext),view); 64 | } 65 | @ReactProp(name = ViewProps.FONT_SIZE, defaultFloat = ViewDefaults.FONT_SIZE_SP) 66 | public void setFontSize(ReactTextView view, float fontSize) { 67 | view.setTextSize( 68 | TypedValue.COMPLEX_UNIT_PX, 69 | (int) Math.ceil(PixelUtil.toPixelFromSP(fontSize))); 70 | if (view.getTag(R.id._makemoji_rn_html_tag_id)!=null) 71 | Moji.setText((String)view.getTag(R.id._makemoji_rn_html_tag_id),view,true); 72 | } 73 | 74 | @Override 75 | public ReactTextShadowNode createShadowNodeInstance() { 76 | return new MyReactTextShadowNode(false); 77 | 78 | } 79 | 80 | @Override 81 | public void updateExtraData(ReactTextView view, Object extraData) { 82 | 83 | } 84 | 85 | @Override 86 | public Map getExportedCustomDirectEventTypeConstants() { 87 | return MapBuilder.of( 88 | HyperMojiEvent.EVENT_NAME, (Object) MapBuilder.of("registrationName", "onHyperMojiPress") 89 | ); 90 | } 91 | 92 | public static class HyperMojiEvent extends Event{ 93 | String url; 94 | final static String EVENT_NAME = "onHyperMojiPress"; 95 | public HyperMojiEvent(int viewTag,String url){ 96 | super(viewTag); 97 | this.url = url; 98 | } 99 | @Override 100 | public String getEventName() { 101 | return EVENT_NAME; 102 | } 103 | 104 | @Override 105 | public void dispatch(RCTEventEmitter rctEventEmitter) { 106 | WritableMap eventData = Arguments.createMap(); 107 | eventData.putString("url", url); 108 | rctEventEmitter.receiveEvent(getViewTag(),getEventName(),eventData); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/makemojireactnative/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.makemojireactnative; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. 9 | * This is used to schedule rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "MakemojiReactNative"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/makemojireactnative/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.makemojireactnative; 2 | 3 | import android.app.Application; 4 | 5 | import com.facebook.react.ReactApplication; 6 | import com.facebook.react.ReactNativeHost; 7 | import com.facebook.react.ReactPackage; 8 | import com.facebook.react.shell.MainReactPackage; 9 | import com.makemoji.mojilib.MakeMojiReactPackage; 10 | 11 | import java.util.Arrays; 12 | import java.util.List; 13 | 14 | public class MainApplication extends Application implements ReactApplication { 15 | 16 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { 17 | @Override 18 | public boolean getUseDeveloperSupport() { 19 | return BuildConfig.DEBUG; 20 | } 21 | 22 | @Override 23 | protected List getPackages() { 24 | return Arrays.asList( 25 | new MainReactPackage(), 26 | new MakeMojiReactPackage(MainApplication.this)); 27 | } 28 | }; 29 | 30 | @Override 31 | public ReactNativeHost getReactNativeHost() { 32 | return mReactNativeHost; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /android/app/src/main/java/csslayout/MyReactTextShadowNode.java: -------------------------------------------------------------------------------- 1 | package csslayout; 2 | 3 | import com.facebook.react.uimanager.LayoutShadowNode; 4 | import com.facebook.react.views.text.ReactTextShadowNode; 5 | 6 | /** 7 | * Created by s_baa on 8/22/2016. 8 | */ 9 | public class MyReactTextShadowNode extends ReactTextShadowNode { 10 | 11 | public MyReactTextShadowNode(boolean isVirtual) { 12 | super(); 13 | } 14 | public float getFontSize(){ 15 | return mFontSize; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /android/app/src/main/java/csslayout/MyShadowNode.java: -------------------------------------------------------------------------------- 1 | package csslayout; 2 | 3 | import com.facebook.react.uimanager.LayoutShadowNode; 4 | import com.facebook.react.uimanager.ReactShadowNode; 5 | 6 | /** 7 | * Created by s_baa on 8/22/2016. 8 | */ 9 | public class MyShadowNode extends LayoutShadowNode { 10 | @Override 11 | public void markUpdated(){ 12 | super.markUpdated(); 13 | //if (hasNewLayout()) markLayoutSeen(); 14 | //dirty(); 15 | } 16 | /* @Override 17 | public boolean isDirty(){ 18 | return true; 19 | }*/ 20 | 21 | 22 | } 23 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/makemoji/Makemoji-React-Native/d3c2eb11b1eec75402fc8d7c708045894dffc011/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/makemoji/Makemoji-React-Native/d3c2eb11b1eec75402fc8d7c708045894dffc011/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/makemoji/Makemoji-React-Native/d3c2eb11b1eec75402fc8d7c708045894dffc011/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/makemoji/Makemoji-React-Native/d3c2eb11b1eec75402fc8d7c708045894dffc011/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | MakemojiReactNative 3 | 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.3.1' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | mavenLocal() 18 | jcenter() 19 | maven { 20 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 21 | url "$rootDir/../node_modules/react-native/android" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | android.useDeprecatedNdk=true 21 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/makemoji/Makemoji-React-Native/d3c2eb11b1eec75402fc8d7c708045894dffc011/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip 6 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /android/keystores/BUCK: -------------------------------------------------------------------------------- 1 | keystore( 2 | name = "debug", 3 | properties = "debug.keystore.properties", 4 | store = "debug.keystore", 5 | visibility = [ 6 | "PUBLIC", 7 | ], 8 | ) 9 | -------------------------------------------------------------------------------- /android/keystores/debug.keystore.properties: -------------------------------------------------------------------------------- 1 | key.store=debug.keystore 2 | key.alias=androiddebugkey 3 | key.store.password=android 4 | key.alias.password=android 5 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'MakemojiReactNative' 2 | 3 | include ':app' 4 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MakemojiReactNative", 3 | "displayName": "MakemojiReactNative" 4 | } -------------------------------------------------------------------------------- /index.android.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * @flow 5 | */ 6 | 7 | import React, { Component} from 'react'; 8 | import { 9 | AppRegistry, 10 | StyleSheet, 11 | Text, 12 | View, 13 | ViewPagerAndroid, 14 | TouchableHighlight, 15 | ListView, 16 | TextInput, 17 | BackAndroid, 18 | NativeAppEventEmitter, 19 | NativeModules 20 | 21 | } from 'react-native'; 22 | var MakemojiTextInput = require('./MakemojiRN/MakemojiTextInput');// MakemojiTextInput from './MakemojiRN/MakemojiTextInput' 23 | var MakemojiReactions = require('./MakemojiRN/MakemojiReactions'); 24 | import MakemojiEditTextAndroid from './MakemojiRN/MakemojiEditTextAndroid' 25 | import MakemojiTextAndroid from './MakemojiRN/MakemojiTextAndroid' 26 | 27 | const NativeEventEmitter = require('NativeEventEmitter'); 28 | const showDetatchControls = true; // enable to show example of detached input. 29 | class MakemojiReactNative extends Component { 30 | 31 | constructor(props){ 32 | super(props); 33 | 34 | var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}); 35 | this.state = {htmlMessages:[], 36 | dataSource:ds.cloneWithRows([]), 37 | outsideEditText:' ', 38 | showReactions:false, 39 | textSize:17.0}; 40 | 41 | 42 | } 43 | componentWillUnmount(){ 44 | this.subscription.remove(); 45 | this.wallSubscription.remove(); 46 | BackAndroid.removeEventListener(this.backListener); 47 | } 48 | componentWillMount(){ 49 | NativeModules.MakemojiManager.init("yourkey"); 50 | 51 | var emitter = new NativeEventEmitter(NativeModules.MakemojiManager); 52 | this.subscription = emitter.addListener( 53 | 'onHypermojiPress', 54 | (event) => console.log(event.url) 55 | ); 56 | this.wallSubscription = emitter.addListener( 57 | 'onEmojiWallSelect', 58 | (event) => console.log(event) 59 | ); 60 | 61 | this.backListener = BackAndroid.addEventListener('hardwareBackPress', () => { 62 | if (this.refs.mojiInput && this.refs.mojiInput.canGoBack()){ 63 | this.refs.mojiInput.onBackPressed(); 64 | return true;//back handled 65 | } 66 | return false; 67 | }); 68 | } 69 | render() { 70 | return ( 71 | 72 | {showDetatchControls? 73 | 75 | 76 | 77 | 78 | Grab Text from top edit text. 79 | 80 | 81 | 82 | this.setState({outsideEditText:'topEditText'})}> 83 | 84 | Attatch Edit Text 85 | 86 | 87 | this.setState({outsideEditText:null})}> 88 | 89 | Detatch Edit Text 90 | 91 | 92 | 93 | 94 | NativeModules.MakemojiManager.openWall()}> 95 | 96 | Wall 97 | 98 | 99 | this.setState({showReactions:!this.state.showReactions})}> 100 | 101 | {this.state.showReactions?'-Reactions':'+Reactions'} 102 | 103 | 104 | 105 | :null} 106 | 110 | 111 | {this.state.showReactions? :null} 112 | } 113 | /> 114 | 116 | 117 | ); 118 | } 119 | genHtml(){ 120 | this.refs.topEditText.requestHtml(true,true);//args:should clear input;should send text to analytics 121 | } 122 | sendPressed(sendObject){ 123 | console.log('send pressed', sendObject); 124 | var htmlMessages = [...this.state.htmlMessages,sendObject.html]; 125 | var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}); 126 | this.setState({htmlMessages:htmlMessages,dataSource:ds.cloneWithRows(htmlMessages)}); 127 | //this.refs.topEditText.setText(sendObject.plaintext); 128 | } 129 | log(event){ 130 | console.log('',event); 131 | } 132 | 133 | } 134 | 135 | const styles = StyleSheet.create({ 136 | reaction:{ 137 | height:30, 138 | alignSelf: 'stretch', 139 | }, 140 | editText:{ 141 | height:50, 142 | alignSelf: 'stretch', 143 | }, 144 | container: { 145 | flex: 1, 146 | flexDirection:'column', 147 | justifyContent: 'flex-end', 148 | alignItems: 'center', 149 | backgroundColor: '#F5FCFF', 150 | }, 151 | welcome: { 152 | fontSize: 20, 153 | textAlign: 'center', 154 | }, 155 | instructions: { 156 | textAlign: 'center', 157 | color: '#333333', 158 | marginBottom: 5, 159 | fontSize:18, 160 | height:30 161 | }, 162 | moji:{ 163 | 164 | height:100, 165 | justifyContent: 'flex-end', 166 | alignSelf: 'stretch' 167 | } 168 | }); 169 | 170 | AppRegistry.registerComponent('MakemojiReactNative', () => MakemojiReactNative); 171 | -------------------------------------------------------------------------------- /index.ios.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * @flow 5 | */ 6 | 7 | import React, { Component} from 'react'; 8 | import { 9 | AppRegistry, 10 | StyleSheet, 11 | Text, 12 | View, 13 | ViewPagerAndroid, 14 | TouchableHighlight, 15 | ListView, 16 | TextInput, 17 | BackAndroid, 18 | NativeAppEventEmitter, 19 | NativeModules, 20 | processColor, 21 | findNodeHandle 22 | 23 | } from 'react-native'; 24 | var MakemojiTextInput = require('./MakemojiRN/MakemojiTextInput'); 25 | var MakemojiReactions = require('./MakemojiRN/MakemojiReactions'); 26 | 27 | const NativeEventEmitter = require('NativeEventEmitter'); 28 | import MakemojiTextCelliOS from './MakemojiRN/MakemojiTextCelliOS' 29 | 30 | var ReactNative = require('ReactNative'); 31 | const showDetatchControls = true; // not avaible on ios currently 32 | class MakemojiReactNative extends Component { 33 | 34 | constructor(props){ 35 | super(props); 36 | 37 | var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}); 38 | var subscription; 39 | this.state = {htmlMessages:[], 40 | dataSource:ds.cloneWithRows([]), 41 | detatchedInputId:null, 42 | showReactions:false, 43 | textSize:17.0}; 44 | 45 | 46 | NativeModules.MakemojiManager.init("key"); 47 | 48 | } 49 | componentDidMount(){ 50 | var emitter = new NativeEventEmitter(NativeModules.MakemojiManager); 51 | this.subscription = emitter.addListener( 52 | 'onHypermojiPress', 53 | (event) => console.log(event.url) 54 | ); 55 | this.wallSubscription = emitter.addListener( 56 | 'onEmojiWallSelect', 57 | (event) => console.log(event) 58 | ); 59 | } 60 | componentWillMount(){ 61 | } 62 | componentWillUnmount(){ 63 | this.subscription.remove(); 64 | this.wallSubscription.remove(); 65 | } 66 | render() { 67 | return ( 68 | 69 | 70 | {showDetatchControls? 71 | NativeModules.MakemojiManager.openWall()}> 72 | 73 | Wall 74 | 75 | 76 | this.setState({showReactions:!this.state.showReactions})}> 77 | 78 | {this.state.showReactions?'-Reactions':'+Reactions'} 79 | 80 | 81 | :null} 82 | 86 | 87 | {this.state.showReactions? :null} 88 | } 89 | /> 90 | 94 | 95 | ); 96 | } 97 | 98 | sendPressed(sendObject){ 99 | console.log('send pressed', sendObject); 100 | var htmlMessages = [...this.state.htmlMessages,sendObject.html]; 101 | var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}); 102 | this.setState({htmlMessages:htmlMessages,dataSource:ds.cloneWithRows(htmlMessages)}); 103 | //this.refs.topEditText.setText(sendObject.plaintext); 104 | } 105 | log(event){ 106 | console.log('',event); 107 | } 108 | 109 | } 110 | 111 | const styles = StyleSheet.create({ 112 | reaction:{ 113 | height:30, 114 | alignSelf: 'stretch', 115 | }, 116 | editText:{ 117 | height:50, 118 | alignSelf: 'stretch', 119 | }, 120 | container: { 121 | flex: 1, 122 | flexDirection:'column', 123 | justifyContent: 'flex-end', 124 | alignItems: 'center', 125 | backgroundColor: '#F5FCFF', 126 | }, 127 | welcome: { 128 | fontSize: 20, 129 | textAlign: 'center', 130 | margin: 10, 131 | }, 132 | instructions: { 133 | textAlign: 'center', 134 | color: '#333333', 135 | marginBottom: 5, 136 | fontSize:18, 137 | height:30 138 | }, 139 | moji:{ 140 | 141 | height:100, 142 | justifyContent: 'flex-end', 143 | alignSelf: 'stretch' 144 | }, 145 | stretch:{ 146 | alignSelf: 'stretch', 147 | height:30 148 | } 149 | }); 150 | 151 | AppRegistry.registerComponent('MakemojiReactNative', () => MakemojiReactNative); 152 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | /Pods -------------------------------------------------------------------------------- /ios/Makemoji/MakemojiManager.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // RCTMakemojiModule.h 4 | // MakeMojiReactNative 5 | // 6 | // Created by Makemoji on 9/11/16. 7 | // Copyright © 2016 Facebook. All rights reserved. 8 | // 9 | 10 | #ifndef RCTMakemojiModule_h 11 | #define RCTMakemojiModule_h 12 | 13 | 14 | // CalendarManager.h 15 | #import 16 | #import 17 | 18 | @interface MakemojiManager : RCTEventEmitter 19 | @end 20 | 21 | 22 | #endif /* RCTMakemojiModule_h */ 23 | -------------------------------------------------------------------------------- /ios/Makemoji/MakemojiManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // RCTHypermojiModule.m 3 | // MakeMojiReactNative 4 | // 5 | // Created by Makemoji on 9/11/16. 6 | // 7 | 8 | #import 9 | 10 | #import 11 | #import "MakemojiManager.h" 12 | #import "MakemojiSDK.h" 13 | #import "MEEmojiWall.h" 14 | #import 15 | #import 16 | 17 | #import 18 | #import "RCTMETextInputView.h" 19 | #import 20 | 21 | #import "AppDelegate.h" 22 | 23 | 24 | @implementation MakemojiManager 25 | 26 | @synthesize bridge = _bridge; 27 | 28 | RCT_EXPORT_MODULE(); 29 | 30 | RCT_EXPORT_METHOD(init:(NSString *)key) 31 | { 32 | 33 | [MakemojiSDK setSDKKey:key]; 34 | /*[[NSNotificationCenter defaultCenter] addObserver:self 35 | selector:@selector(hypermojiClick:) 36 | name:@"MEHypermojiLinkClicked" 37 | object:nil];*/ 38 | } 39 | RCT_EXPORT_METHOD(openWall){ 40 | MEEmojiWall * emojiWall = [[MEEmojiWall alloc] init]; 41 | emojiWall.delegate = self; 42 | emojiWall.modalPresentationStyle = UIModalPresentationOverCurrentContext; 43 | 44 | UINavigationController *navigationController = 45 | [[UINavigationController alloc] initWithRootViewController:emojiWall]; 46 | 47 | AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; 48 | // present the emoji wall as a modal 49 | [delegate.window.rootViewController presentViewController:navigationController animated:YES completion:nil]; 50 | } 51 | -(void)meEmojiWall:(MEEmojiWall *)emojiWall didSelectEmoji:(NSDictionary*)emoji { 52 | [emojiWall dismissViewControllerAnimated:YES completion:nil]; 53 | NSMutableDictionary *myDictionary = [emoji mutableCopy]; 54 | [myDictionary removeObjectForKey:@"image_object"]; 55 | [self sendEventWithName: @"onEmojiWallSelect" 56 | body:myDictionary]; 57 | NSLog(@"%@", emoji); 58 | } 59 | - (NSArray *)supportedEvents { 60 | return @[@"onEmojiWallSelect",@"onHypermojiPress"]; 61 | } 62 | 63 | RCT_EXPORT_METHOD(setChannel:(NSString *)channel) 64 | { 65 | [MakemojiSDK setChannel:channel]; 66 | } 67 | 68 | - (void)hypermojiClick:(NSNotification *)note { 69 | NSDictionary *theData = [note userInfo]; 70 | if (theData != nil) { 71 | NSString *n = [theData objectForKey:@"url"]; 72 | [self sendEventWithName:@"onHypermojiPress" 73 | body:@{@"url": n}]; 74 | } 75 | } 76 | 77 | //not working currently 78 | RCT_EXPORT_METHOD(detatch:(nonnull NSNumber *)mojiInputTag :(nonnull NSNumber *)detatchTag ){ 79 | 80 | dispatch_async(RCTGetUIManagerQueue(),^{ 81 | [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager,NSDictionary *viewRegistry) { 82 | RCTMETextInputView* view = viewRegistry[mojiInputTag]; 83 | UIView *otherView = viewRegistry[detatchTag]; 84 | [view setTextInputContainerView:otherView]; 85 | [view detachTextInputView:YES]; 86 | 87 | }]; 88 | }); 89 | 90 | 91 | } 92 | 93 | @end 94 | -------------------------------------------------------------------------------- /ios/Makemoji/RCTMESimpleTableViewCellManager.m: -------------------------------------------------------------------------------- 1 | // RCTMapManager.m 2 | 3 | #import 4 | #import "MESimpleTableViewCell.h" 5 | #import 6 | #import "RCTMETextInputView.h" 7 | 8 | @interface MESimpleTableViewCellManager : RCTViewManager 9 | @end 10 | 11 | @implementation MESimpleTableViewCellManager 12 | 13 | RCT_EXPORT_MODULE() 14 | 15 | - (UIView *)view 16 | { 17 | MESimpleTableViewCell* view=[[MESimpleTableViewCell alloc] init]; 18 | 19 | return view; 20 | } 21 | 22 | RCT_CUSTOM_VIEW_PROPERTY(html, NSString, MESimpleTableViewCell) 23 | { 24 | [view setHTMLString:[RCTConvert NSString:json]]; 25 | } 26 | RCT_CUSTOM_VIEW_PROPERTY(plaintext, NSString, MESimpleTableViewCell) 27 | { 28 | [view setHTMLString:[METextInputView convertSubstituedToHTML:json]]; 29 | } 30 | - (void)hypermojiClick:(NSNotification *)note { 31 | NSDictionary *theData = [note userInfo]; 32 | if (theData != nil) { 33 | NSNumber *n = [theData objectForKey:@"MEHypermojiLinkClicked"]; 34 | BOOL isReachable = [n boolValue]; 35 | NSLog(@"reachable: %d", isReachable); 36 | } 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /ios/Makemoji/RCTMETextInputView.h: -------------------------------------------------------------------------------- 1 | // 2 | // RCTMETextInputView.h 3 | // MakeMojiReactNative 4 | // 5 | // Created by Makemoji on 9/6/16. 6 | // Copyright © 2016 Facebook. All rights reserved. 7 | // 8 | 9 | #ifndef RCTMETextInputView_h 10 | #define RCTMETextInputView_h 11 | 12 | 13 | #endif /* RCTMETextInputView_h */ 14 | #import 15 | 16 | 17 | #import 18 | #import "METextInputView.h" 19 | 20 | @interface RCTMETextInputView : METextInputView 21 | 22 | @property (nonatomic, copy) RCTBubblingEventBlock onSendPress; 23 | @property (nonatomic, copy) RCTBubblingEventBlock onHypermojiPress; 24 | //@property (nonatomic, copy) RCTBubblingEventBlock onHyperlinkPress; 25 | @property (nonatomic, copy) RCTBubblingEventBlock onCameraPress; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /ios/Makemoji/RCTMETextInputView.m: -------------------------------------------------------------------------------- 1 | // 2 | // RCTMETextInputView.h 3 | // MakeMojiReactNative 4 | // 5 | // Created by Makemoji on 9/6/16. 6 | // Copyright © 2016 Facebook. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "RCTMETextInputView.h" 11 | 12 | 13 | @implementation RCTMETextInputView 14 | @end 15 | -------------------------------------------------------------------------------- /ios/Makemoji/RCTMoji.h: -------------------------------------------------------------------------------- 1 | // 2 | // RCTMojiInputLayout.h 3 | // MakeMojiReactNative 4 | // 5 | // Created by Makemoji on 8/11/16. 6 | // Copyright © 2016 Facebook. All rights reserved. 7 | // 8 | 9 | #import "METextInputView.h" 10 | #import 11 | 12 | @interface RCTMoji :METextInputView 13 | @property (nonatomic, copy) RCTBubblingEventBlock onCameraPressed; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /ios/Makemoji/RCTMoji.m: -------------------------------------------------------------------------------- 1 | // 2 | // RCTMoji.m 3 | // MakeMojiReactNative 4 | // 5 | // Created by Makemoji on 8/11/16. 6 | // Copyright © 2016 Facebook. All rights reserved. 7 | // 8 | 9 | #import "RCTMoji.h" 10 | 11 | @implementation RCTMoji 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ios/Makemoji/RCTMojiInputLayoutManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // RCTMojiInputLayoutManager.m 3 | // MakeMojiReactNative 4 | // 5 | // Created by Makemoji on 9/6/16. 6 | // Copyright © 2016 Facebook. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MakemojiSDK.h" 11 | #import 12 | #import 13 | #import "RCTMETextInputView.h" 14 | #import 15 | #import 16 | 17 | @interface RCTMojiInputLayoutManager : RCTViewManager 18 | @end 19 | 20 | @implementation RCTMojiInputLayoutManager 21 | 22 | RCT_EXPORT_MODULE() 23 | 24 | RCT_EXPORT_VIEW_PROPERTY(onSendPress, RCTBubblingEventBlock) 25 | RCT_EXPORT_VIEW_PROPERTY(onHypermojiPress, RCTBubblingEventBlock) 26 | //RCT_EXPORT_VIEW_PROPERTY(onHyperlinkPress, RCTBubblingEventBlock) 27 | RCT_EXPORT_VIEW_PROPERTY(onCameraPress, RCTBubblingEventBlock) 28 | 29 | @synthesize bridge = _bridge; 30 | 31 | - (UIView *)view 32 | { 33 | RCTMETextInputView *view = [RCTMETextInputView new]; 34 | view.delegate = self; 35 | return view; 36 | } 37 | 38 | // send button was pressed 39 | -(void)meTextInputView:(RCTMETextInputView *)inputView didTapSend:(NSDictionary *)message { 40 | inputView.onSendPress(@{ 41 | @"html": [message valueForKeyPath:@"html"], 42 | @"plaintext":[message valueForKeyPath:@"substitute"]}); 43 | } 44 | 45 | // handle camera action 46 | -(void)meTextInputView:(RCTMETextInputView *)inputView didTapCameraButton:(UIButton*)cameraButton { 47 | inputView.onCameraPress(NULL); 48 | } 49 | 50 | -(void)meTextInputView:(RCTMETextInputView *)inputView didTapHypermoji:(NSString*)urlString { 51 | inputView.onHypermojiPress(@{@"url":urlString}); 52 | } 53 | 54 | // handle tapping of URLs 55 | /*-(void)meTextInputView:(RCTMETextInputView *)inputView didTapHyperlink:(NSString*)urlString { 56 | inputView.onHyperlinkPress(@{@"url":urlString}); 57 | }*/ 58 | 59 | RCT_CUSTOM_VIEW_PROPERTY(cameraVisible, BOOL, RCTMETextInputView) 60 | { 61 | [view setDisplayCameraButton:[RCTConvert BOOL:json]]; 62 | } 63 | RCT_CUSTOM_VIEW_PROPERTY(sendButtonVisible, BOOL, RCTMETextInputView) 64 | { 65 | [view setDisplaySendButton:[RCTConvert BOOL:json]]; 66 | } 67 | 68 | RCT_CUSTOM_VIEW_PROPERTY(textInputTextColor, UIColor, RCTMETextInputView) 69 | { 70 | [view setTextInputTextColor:[RCTConvert UIColor:json]]; 71 | } 72 | RCT_CUSTOM_VIEW_PROPERTY(placeholderTextColor, UIColor, RCTMETextInputView) 73 | { 74 | view.placeholderLabel.textColor=[RCTConvert UIColor:json]; 75 | } 76 | 77 | RCT_CUSTOM_VIEW_PROPERTY(textSolidBackgroundColor, UIColor, RCTMETextInputView) 78 | { 79 | view.textSolidBackgroundView.backgroundColor=[RCTConvert UIColor:json]; 80 | } 81 | RCT_CUSTOM_VIEW_PROPERTY(textInputContainerColor, UIColor, RCTMETextInputView) 82 | { 83 | view.textInputContainerView.backgroundColor=[RCTConvert UIColor:json]; 84 | } 85 | 86 | RCT_CUSTOM_VIEW_PROPERTY(navigationBackgroundColor, UIColor, RCTMETextInputView) 87 | { 88 | [view.meAccessory setNavigationBackgroundColor:[RCTConvert UIColor:json]]; 89 | } 90 | RCT_CUSTOM_VIEW_PROPERTY(navigationHighlightColor, UIColor, RCTMETextInputView) 91 | { 92 | [view.meAccessory setNavigationHighlightColor:[RCTConvert UIColor:json]]; 93 | } 94 | RCT_CUSTOM_VIEW_PROPERTY(accessoryBackgroundColor, UIColor, RCTMETextInputView) 95 | { 96 | view.meAccessory.backgroundColor=[RCTConvert UIColor:json]; 97 | } 98 | RCT_CUSTOM_VIEW_PROPERTY(flashtagCollectionViewBackgroundColor, UIColor, RCTMETextInputView) 99 | { 100 | view.meAccessory.flashtagCollectionView.backgroundColor=[RCTConvert UIColor:json]; 101 | } 102 | RCT_CUSTOM_VIEW_PROPERTY(emojiPageBackgroundColor, UIColor, RCTMETextInputView) 103 | { 104 | view.meAccessory.meInputView.backgroundColor=[RCTConvert UIColor:json]; 105 | } 106 | RCT_CUSTOM_VIEW_PROPERTY(emojiCollectionBackgroundColor, UIColor, RCTMETextInputView) 107 | { 108 | view.meAccessory.meInputView.emojiView.backgroundColor=[RCTConvert UIColor:json]; 109 | } 110 | RCT_CUSTOM_VIEW_PROPERTY(categoriesBackgroundColor, UIColor, RCTMETextInputView) 111 | { 112 | view.meAccessory.meInputView.collectionView.backgroundColor=[RCTConvert UIColor:json]; 113 | } 114 | 115 | 116 | 117 | 118 | @end 119 | 120 | -------------------------------------------------------------------------------- /ios/MakemojiReactNative-tvOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UIViewControllerBasedStatusBarAppearance 38 | 39 | NSLocationWhenInUseUsageDescription 40 | 41 | NSAppTransportSecurity 42 | 43 | 44 | NSExceptionDomains 45 | 46 | localhost 47 | 48 | NSExceptionAllowsInsecureHTTPLoads 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /ios/MakemojiReactNative-tvOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /ios/MakemojiReactNative.xcodeproj/xcshareddata/xcschemes/MakemojiReactNative-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 61 | 67 | 68 | 69 | 70 | 71 | 77 | 78 | 79 | 80 | 81 | 82 | 92 | 94 | 100 | 101 | 102 | 103 | 104 | 105 | 111 | 113 | 119 | 120 | 121 | 122 | 124 | 125 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /ios/MakemojiReactNative.xcodeproj/xcshareddata/xcschemes/MakemojiReactNative.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 61 | 67 | 68 | 69 | 70 | 71 | 77 | 78 | 79 | 80 | 81 | 82 | 92 | 94 | 100 | 101 | 102 | 103 | 104 | 105 | 111 | 113 | 119 | 120 | 121 | 122 | 124 | 125 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /ios/MakemojiReactNative.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/MakemojiReactNative/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | @interface AppDelegate : UIResponder 13 | 14 | @property (nonatomic, strong) UIWindow *window; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /ios/MakemojiReactNative/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import "AppDelegate.h" 11 | #import "MakemojiSDK.h" 12 | 13 | #import 14 | #import 15 | 16 | @implementation AppDelegate 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 19 | { 20 | NSURL *jsCodeLocation; 21 | 22 | 23 | //[MakemojiSDK setSDKKey:@"940ced93abf2ca4175a4a865b38f1009d8848a58"]; 24 | 25 | jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil]; 26 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation 27 | moduleName:@"MakemojiReactNative" 28 | initialProperties:nil 29 | launchOptions:launchOptions]; 30 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 31 | 32 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 33 | UIViewController *rootViewController = [UIViewController new]; 34 | rootViewController.view = rootView; 35 | self.window.rootViewController = rootViewController; 36 | [self.window makeKeyAndVisible]; 37 | return YES; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /ios/MakemojiReactNative/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /ios/MakemojiReactNative/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /ios/MakemojiReactNative/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | MakemojiReactNative 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UIViewControllerBasedStatusBarAppearance 40 | 41 | NSLocationWhenInUseUsageDescription 42 | 43 | NSAppTransportSecurity 44 | 45 | 46 | NSExceptionDomains 47 | 48 | localhost 49 | 50 | NSExceptionAllowsInsecureHTTPLoads 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /ios/MakemojiReactNative/RCTMojiReactionsManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // RCTMojiReactions.m 3 | // MakemojiReactNative 4 | // 5 | // Created by Makemoji on 11/28/16. 6 | // Copyright © 2016 Facebook. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import 12 | #import "MESimpleTableViewCell.h" 13 | #import "MEReactionView.h" 14 | #import 15 | #import "RCTMETextInputView.h" 16 | #import "AppDelegate.h" 17 | 18 | 19 | @interface RCTMojiReactionsManager : RCTViewManager 20 | @end 21 | 22 | @implementation RCTMojiReactionsManager 23 | 24 | RCT_EXPORT_MODULE() 25 | 26 | - (UIView *)view 27 | { 28 | MEReactionView* view = [[MEReactionView alloc] init]; 29 | AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; 30 | [view setViewController:delegate.window.rootViewController]; 31 | return view; 32 | } 33 | 34 | RCT_CUSTOM_VIEW_PROPERTY(contentId, NSString, MEReactionView) 35 | { 36 | view.contentId = [RCTConvert NSString:json]; 37 | } 38 | @end 39 | 40 | -------------------------------------------------------------------------------- /ios/MakemojiReactNative/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | #import "AppDelegate.h" 13 | 14 | int main(int argc, char * argv[]) { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ios/MakemojiReactNativeTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /ios/MakemojiReactNativeTests/MakemojiReactNativeTests.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | #import 12 | 13 | #import 14 | #import 15 | 16 | #define TIMEOUT_SECONDS 600 17 | #define TEXT_TO_LOOK_FOR @"Welcome to React Native!" 18 | 19 | @interface MakemojiReactNativeTests : XCTestCase 20 | 21 | @end 22 | 23 | @implementation MakemojiReactNativeTests 24 | 25 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 26 | { 27 | if (test(view)) { 28 | return YES; 29 | } 30 | for (UIView *subview in [view subviews]) { 31 | if ([self findSubviewInView:subview matching:test]) { 32 | return YES; 33 | } 34 | } 35 | return NO; 36 | } 37 | 38 | - (void)testRendersWelcomeScreen 39 | { 40 | UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; 41 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 42 | BOOL foundElement = NO; 43 | 44 | __block NSString *redboxError = nil; 45 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 46 | if (level >= RCTLogLevelError) { 47 | redboxError = message; 48 | } 49 | }); 50 | 51 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 52 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 53 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 54 | 55 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 56 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 57 | return YES; 58 | } 59 | return NO; 60 | }]; 61 | } 62 | 63 | RCTSetLogFunction(RCTDefaultLogFunction); 64 | 65 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 66 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 67 | } 68 | 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '8.0' 3 | 4 | target 'MakemojiReactNative’ do 5 | pod "Makemoji-SDK" 6 | pod "MakemojiSDK-KeyboardExtension" 7 | end 8 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - AFNetworking (3.1.0): 3 | - AFNetworking/NSURLSession (= 3.1.0) 4 | - AFNetworking/Reachability (= 3.1.0) 5 | - AFNetworking/Security (= 3.1.0) 6 | - AFNetworking/Serialization (= 3.1.0) 7 | - AFNetworking/UIKit (= 3.1.0) 8 | - AFNetworking/NSURLSession (3.1.0): 9 | - AFNetworking/Reachability 10 | - AFNetworking/Security 11 | - AFNetworking/Serialization 12 | - AFNetworking/Reachability (3.1.0) 13 | - AFNetworking/Security (3.1.0) 14 | - AFNetworking/Serialization (3.1.0) 15 | - AFNetworking/UIKit (3.1.0): 16 | - AFNetworking/NSURLSession 17 | - Makemoji-SDK (1.0.70): 18 | - AFNetworking (>= 2.6.3) 19 | - SDWebImage (>= 3.7.3) 20 | - MakemojiSDK-KeyboardExtension (1.0.37): 21 | - AFNetworking (>= 2.6.3) 22 | - SDWebImage (>= 3.7.3) 23 | - SDWebImage (3.8.2): 24 | - SDWebImage/Core (= 3.8.2) 25 | - SDWebImage/Core (3.8.2) 26 | 27 | DEPENDENCIES: 28 | - Makemoji-SDK 29 | - MakemojiSDK-KeyboardExtension 30 | 31 | SPEC CHECKSUMS: 32 | AFNetworking: 5e0e199f73d8626b11e79750991f5d173d1f8b67 33 | Makemoji-SDK: 10590c5922c5d720cad5bbe3a1dab538ee7f4d3b 34 | MakemojiSDK-KeyboardExtension: 3fed8992fc2ff7a3cfefed918c75351c890f5ffe 35 | SDWebImage: '098e97e6176540799c27e804c96653ee0833d13c' 36 | 37 | PODFILE CHECKSUM: be40d86d7a3fc5dafd5b11be18d8057a5827a08b 38 | 39 | COCOAPODS: 1.2.0.beta.1 40 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MakemojiReactNative", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node node_modules/react-native/local-cli/cli.js start", 7 | "test": "jest" 8 | }, 9 | "dependencies": { 10 | "react": "^16.0.0-alpha.6", 11 | "react-native": "^0.43.0" 12 | }, 13 | "jest": { 14 | "preset": "react-native" 15 | }, 16 | "devDependencies": { 17 | "babel-jest": "17.0.2", 18 | "babel-preset-react-native": "1.9.0", 19 | "jest": "17.0.3", 20 | "react-test-renderer": "15.4.1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /watchman.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/makemoji/Makemoji-React-Native/d3c2eb11b1eec75402fc8d7c708045894dffc011/watchman.exe --------------------------------------------------------------------------------