react-native-mobile-chat 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +235 -0
  3. package/android/build.gradle +77 -0
  4. package/android/gradle.properties +5 -0
  5. package/android/src/main/AndroidManifest.xml +4 -0
  6. package/android/src/main/java/com/appchat/AppChatModule.java +58 -0
  7. package/android/src/main/java/com/appchat/AppChatPackage.java +28 -0
  8. package/ios/AppChat.h +12 -0
  9. package/ios/AppChat.mm +67 -0
  10. package/ios/AppChat.xcodeproj/project.pbxproj +274 -0
  11. package/ios/AppChat.xcodeproj/project.xcworkspace/contents.xcworkspacedata +4 -0
  12. package/lib/commonjs/assets/images/arrow_left.png +0 -0
  13. package/lib/commonjs/assets/images/arrow_left_black.png +0 -0
  14. package/lib/commonjs/common/constants/index.js +20 -0
  15. package/lib/commonjs/common/constants/index.js.map +1 -0
  16. package/lib/commonjs/common/utils/index.js +53 -0
  17. package/lib/commonjs/common/utils/index.js.map +1 -0
  18. package/lib/commonjs/data/local/index.js +28 -0
  19. package/lib/commonjs/data/local/index.js.map +1 -0
  20. package/lib/commonjs/data/remote/index.js +87 -0
  21. package/lib/commonjs/data/remote/index.js.map +1 -0
  22. package/lib/commonjs/data/static/index.js +35 -0
  23. package/lib/commonjs/data/static/index.js.map +1 -0
  24. package/lib/commonjs/index.js +46 -0
  25. package/lib/commonjs/index.js.map +1 -0
  26. package/lib/commonjs/native/component/WebView.js +16 -0
  27. package/lib/commonjs/native/component/WebView.js.map +1 -0
  28. package/lib/commonjs/native/module/index.js +18 -0
  29. package/lib/commonjs/native/module/index.js.map +1 -0
  30. package/lib/commonjs/presentation/component/AppChatComponent.js +236 -0
  31. package/lib/commonjs/presentation/component/AppChatComponent.js.map +1 -0
  32. package/lib/commonjs/presentation/initialization/index.js +148 -0
  33. package/lib/commonjs/presentation/initialization/index.js.map +1 -0
  34. package/lib/commonjs/presentation/notification/index.js +50 -0
  35. package/lib/commonjs/presentation/notification/index.js.map +1 -0
  36. package/lib/module/assets/images/arrow_left.png +0 -0
  37. package/lib/module/assets/images/arrow_left_black.png +0 -0
  38. package/lib/module/common/constants/index.js +13 -0
  39. package/lib/module/common/constants/index.js.map +1 -0
  40. package/lib/module/common/utils/index.js +45 -0
  41. package/lib/module/common/utils/index.js.map +1 -0
  42. package/lib/module/data/local/index.js +20 -0
  43. package/lib/module/data/local/index.js.map +1 -0
  44. package/lib/module/data/remote/index.js +75 -0
  45. package/lib/module/data/remote/index.js.map +1 -0
  46. package/lib/module/data/static/index.js +28 -0
  47. package/lib/module/data/static/index.js.map +1 -0
  48. package/lib/module/index.js +5 -0
  49. package/lib/module/index.js.map +1 -0
  50. package/lib/module/native/component/WebView.js +10 -0
  51. package/lib/module/native/component/WebView.js.map +1 -0
  52. package/lib/module/native/module/index.js +11 -0
  53. package/lib/module/native/module/index.js.map +1 -0
  54. package/lib/module/presentation/component/AppChatComponent.js +227 -0
  55. package/lib/module/presentation/component/AppChatComponent.js.map +1 -0
  56. package/lib/module/presentation/initialization/index.js +141 -0
  57. package/lib/module/presentation/initialization/index.js.map +1 -0
  58. package/lib/module/presentation/notification/index.js +41 -0
  59. package/lib/module/presentation/notification/index.js.map +1 -0
  60. package/lib/typescript/common/constants/index.d.ts +13 -0
  61. package/lib/typescript/common/constants/index.d.ts.map +1 -0
  62. package/lib/typescript/common/utils/index.d.ts +15 -0
  63. package/lib/typescript/common/utils/index.d.ts.map +1 -0
  64. package/lib/typescript/data/local/index.d.ts +4 -0
  65. package/lib/typescript/data/local/index.d.ts.map +1 -0
  66. package/lib/typescript/data/remote/index.d.ts +7 -0
  67. package/lib/typescript/data/remote/index.d.ts.map +1 -0
  68. package/lib/typescript/data/static/index.d.ts +23 -0
  69. package/lib/typescript/data/static/index.d.ts.map +1 -0
  70. package/lib/typescript/index.d.ts +5 -0
  71. package/lib/typescript/index.d.ts.map +1 -0
  72. package/lib/typescript/native/component/WebView.d.ts +8 -0
  73. package/lib/typescript/native/component/WebView.d.ts.map +1 -0
  74. package/lib/typescript/native/module/index.d.ts +2 -0
  75. package/lib/typescript/native/module/index.d.ts.map +1 -0
  76. package/lib/typescript/presentation/component/AppChatComponent.d.ts +22 -0
  77. package/lib/typescript/presentation/component/AppChatComponent.d.ts.map +1 -0
  78. package/lib/typescript/presentation/initialization/index.d.ts +2 -0
  79. package/lib/typescript/presentation/initialization/index.d.ts.map +1 -0
  80. package/lib/typescript/presentation/notification/index.d.ts +5 -0
  81. package/lib/typescript/presentation/notification/index.d.ts.map +1 -0
  82. package/package.json +165 -0
  83. package/react-native-app-chat.podspec +35 -0
  84. package/src/assets/images/arrow_left.png +0 -0
  85. package/src/assets/images/arrow_left_black.png +0 -0
  86. package/src/common/constants/index.tsx +12 -0
  87. package/src/common/utils/index.tsx +58 -0
  88. package/src/data/local/index.tsx +23 -0
  89. package/src/data/remote/index.tsx +80 -0
  90. package/src/data/static/index.tsx +28 -0
  91. package/src/index.tsx +11 -0
  92. package/src/native/component/WebView.tsx +10 -0
  93. package/src/native/module/index.tsx +18 -0
  94. package/src/presentation/component/AppChatComponent.tsx +154 -0
  95. package/src/presentation/initialization/index.tsx +149 -0
  96. package/src/presentation/notification/index.tsx +45 -0
@@ -0,0 +1,154 @@
1
+ import React, { PureComponent, ReactNode, Fragment } from 'react';
2
+ import { ActivityIndicator, StatusBar, View, Dimensions, Text, TouchableOpacity, Image, PermissionsAndroid, Platform } from 'react-native';
3
+ import { AppChatStatic } from '../../data/static';
4
+ import { initAppChat } from '../../presentation/initialization';
5
+ import WebView, { WebViewMessageEvent } from 'react-native-webview';
6
+ import Constant from '../../common/constants';
7
+ import { putString } from '../../data/local';
8
+ import { SafeAreaView } from 'react-native';
9
+
10
+ type AppChatComponentProps = {
11
+ onBackButtonTapped: () => void
12
+ }
13
+
14
+ export default class AppChatComponent extends PureComponent<AppChatComponentProps> {
15
+
16
+ _isMounted = false
17
+
18
+ state = {
19
+ isOverlaid: false,
20
+ toggleRefresh: false
21
+ }
22
+
23
+ constructor(props: AppChatComponentProps) {
24
+ super(props)
25
+ this.state = {
26
+ isOverlaid: false,
27
+ toggleRefresh: false
28
+ }
29
+ }
30
+
31
+ _setState = (state: any) => {
32
+ if (this._isMounted) {
33
+ this.setState(state)
34
+ }
35
+ }
36
+
37
+ _requestPermission = async (permission: any) => {
38
+ if (Platform.OS == 'android') {
39
+ const granted = await PermissionsAndroid.check(permission)
40
+ if (!granted) {
41
+ PermissionsAndroid.request(permission)
42
+ }
43
+ }
44
+ }
45
+
46
+ _onAccessTokenExpired = async () => {
47
+ AppChatStatic.initAppChatResult = ''
48
+ AppChatStatic.accessToken = ''
49
+ AppChatStatic.encryptedUrlParams = ''
50
+ await putString('accessToken', 'default_value')
51
+ this._setState({ toggleRefresh: !this.state.toggleRefresh })
52
+ this.componentDidMount()
53
+ }
54
+
55
+ _onMessage = (event: WebViewMessageEvent) => {
56
+ const data = JSON.parse(event.nativeEvent.data)
57
+ switch (data.key) {
58
+ case 'TOKEN_EXPIRED':
59
+ this._onAccessTokenExpired()
60
+ break;
61
+ case 'OVERLAY':
62
+ this._setState({ isOverlaid: true })
63
+ this._requestPermission('android.permission.CAMERA')
64
+ break;
65
+ case 'REMOVE_OVERLAY':
66
+ this._setState({ isOverlaid: false })
67
+ break;
68
+ case 'CAMERA':
69
+ //this._requestPermission('android.permission.RECORD_AUDIO')
70
+ break;
71
+ case 'GALLERY':
72
+ //this._requestPermission('android.permission.READ_MEDIA_IMAGES')
73
+ //this._requestPermission('android.permission.READ_MEDIA_VIDEO')
74
+ break;
75
+ case 'DOCUMENT':
76
+ //this._requestPermission('android.permission.READ_EXTERNAL_STORAGE')
77
+ break;
78
+ case 'AUDIO':
79
+ //this._requestPermission('android.permission.READ_MEDIA_AUDIO')
80
+ break;
81
+ default:
82
+ break;
83
+ }
84
+ console.log('onMessage', event.nativeEvent.data);
85
+ }
86
+
87
+ componentDidMount(): void {
88
+ this._isMounted = true
89
+ AppChatStatic.appChatOpened = true
90
+ if (AppChatStatic.initAppChatResult === '') {
91
+ setTimeout(() => {
92
+ const { appId, clientId, clientSecret, externalId, fullName } = AppChatStatic
93
+ initAppChat(appId, clientId, clientSecret, externalId, fullName).then((result) => {
94
+ if (result === 'SUCCESS') this._setState({ toggleRefresh: !this.state.toggleRefresh })
95
+ })
96
+ }, 10000);
97
+ }
98
+ }
99
+
100
+ componentWillUnmount(): void {
101
+ this._isMounted = false
102
+ AppChatStatic.appChatOpened = false
103
+ }
104
+
105
+ render(): ReactNode {
106
+ const { onBackButtonTapped } = this.props
107
+ const { initAppChatResult, bundleIdValidation } = AppChatStatic
108
+ const { bgColorTheme, textHeader, textDescription, fontColor } = AppChatStatic.appChatTheme
109
+ const postMessageListener = `window.addEventListener('message',(event) => window.ReactNativeWebView.postMessage(event.data));let meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0'); document.getElementsByTagName('head')[0].appendChild(meta);`;
110
+ if (initAppChatResult === 'SUCCESS' && bundleIdValidation !== 'success') {
111
+ return (
112
+ <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
113
+ <Text style={{ textAlign: 'center', padding: 16 }}>{Constant.BUNDLE_ID_INVALID} </Text>
114
+ </View>
115
+ )
116
+ }
117
+ return (
118
+ <Fragment>
119
+ <SafeAreaView style={{ flex: 0, backgroundColor: this.state.isOverlaid ? AppChatStatic.appChatTheme.bgColorThemeOverlaid : bgColorTheme }} />
120
+ <SafeAreaView style={{ flex: 1, backgroundColor: this.state.isOverlaid ? Constant.WEBVIEW_BACKGROUND_COLOR_OVERLAID : Constant.WEBVIEW_BACKGROUND_COLOR }}>
121
+ <StatusBar
122
+ barStyle={fontColor === 'black' || this.state.isOverlaid ? 'dark-content' : 'light-content'}
123
+ backgroundColor={this.state.isOverlaid ? AppChatStatic.appChatTheme.bgColorThemeOverlaid : bgColorTheme}
124
+ />
125
+ <View style={{ height: 52, backgroundColor: bgColorTheme }}>
126
+ <View style={{ position: 'absolute' }}>
127
+ <View style={{ position: 'absolute', top: 0, flexDirection: 'row', height: 52, width: Dimensions.get('screen').width, backgroundColor: bgColorTheme }}>
128
+ <TouchableOpacity style={{ width: 52, height: 52, justifyContent: 'center', alignItems: 'center' }} onPress={() => onBackButtonTapped()} >
129
+ <Image style={{ height: 16 }} source={fontColor === 'black' ? require('../../assets/images/arrow_left_black.png') : require('../../assets/images/arrow_left.png')} />
130
+ </TouchableOpacity>
131
+ <View style={{ height: 52, justifyContent: 'center', alignItems: 'flex-start' }}>
132
+ <Text style={{ fontSize: 16, color: fontColor, fontWeight: '600' }}>{textHeader}</Text>
133
+ <Text style={{ fontSize: 12, color: fontColor, fontWeight: '400' }}>{textDescription}</Text>
134
+ </View>
135
+ </View>
136
+ {this.state.isOverlaid && <View style={{ position: 'absolute', top: 0, height: 52, width: Dimensions.get('screen').width, backgroundColor: 'rgba(35, 41, 51, 0.8)' }} />}
137
+ </View>
138
+ </View>
139
+ {AppChatStatic.initAppChatResult !== 'SUCCESS' && <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
140
+ <ActivityIndicator size="large" />
141
+ </View>}
142
+ {AppChatStatic.initAppChatResult === 'SUCCESS' && <WebView
143
+ bounces={false}
144
+ overScrollMode='never'
145
+ setBuiltInZoomControls={false}
146
+ injectedJavaScriptBeforeContentLoaded={postMessageListener}
147
+ source={{ uri: `${Constant.BASE_URL_WEBVIEW}${Constant.ENCRYPTED_PARAMS_KEY}${AppChatStatic.encryptedUrlParams}` }}
148
+ onMessage={this._onMessage}
149
+ style={{ flex: 1, backgroundColor: this.state.isOverlaid ? Constant.WEBVIEW_BACKGROUND_COLOR_OVERLAID : Constant.WEBVIEW_BACKGROUND_COLOR }} />}
150
+ </SafeAreaView>
151
+ </Fragment>
152
+ )
153
+ }
154
+ }
@@ -0,0 +1,149 @@
1
+ import { getBundleIdOrPackageName, getString, putString } from "../../data/local"
2
+ import { authenticate, createContact, getWidgetSetting, registerFcmToken, validateBundleId } from "../../data/remote"
3
+ import { AppChatStatic } from "../../data/static"
4
+ import { calculateTextColorOutput, getEncryptedUrlParams, getOverlaidColor } from '../../common/utils';
5
+ import Constant from "../../common/constants";
6
+
7
+ export async function initAppChat(appId: string, clientId: string, clientSecret: string, externalId: string, fullName: string) {
8
+ AppChatStatic.appId = appId
9
+ AppChatStatic.clientId = clientId
10
+ AppChatStatic.clientSecret = clientSecret
11
+ AppChatStatic.externalId = externalId
12
+ AppChatStatic.fullName = fullName
13
+
14
+ //is client app re-login?
15
+ const currentExternalId = await getString('externalId')
16
+ if (currentExternalId !== externalId) {
17
+ AppChatStatic.accessToken = ''
18
+ AppChatStatic.contactId = ''
19
+ AppChatStatic.bundleIdValidation = ''
20
+ AppChatStatic.encryptedUrlParams = ''
21
+ AppChatStatic.initAppChatResult = ''
22
+
23
+ AppChatStatic.appChatTheme.bgColorTheme = 'grey'
24
+ AppChatStatic.appChatTheme.fontColor = 'white'
25
+ AppChatStatic.appChatTheme.textHeader = ''
26
+ AppChatStatic.appChatTheme.textDescription = ''
27
+
28
+ AppChatStatic.fcmToken = ''
29
+
30
+ await putString('accessToken', 'default_value')
31
+ await putString('fcmToken', 'default_value')
32
+ await putString('contactId', 'default_value')
33
+ }
34
+
35
+ try {
36
+ //Authentication - accessToken
37
+ const accessToken = AppChatStatic.accessToken == '' ? await updateAccessToken(clientId, clientSecret) : AppChatStatic.accessToken
38
+
39
+ //Create Contact - contactId
40
+ const contactId = AppChatStatic.contactId == '' ? await updateContactId(externalId, fullName) : AppChatStatic.contactId
41
+
42
+ //Validate Bundle ID or Package Name - bundleIdValidation
43
+ const bundleIdValidation = AppChatStatic.bundleIdValidation !== 'success' ? await validateBundleIdOrPackageName() : AppChatStatic.bundleIdValidation
44
+
45
+ //Encrypt URL Params - encryptedUrlParams
46
+ const encryptedUrlParams = AppChatStatic.encryptedUrlParams == '' ? updateEncryptedUrlParams() : AppChatStatic.encryptedUrlParams
47
+
48
+ //Get theme - appChatTheme
49
+ AppChatStatic.appChatTheme.isAnyEmpty() ? await updateAppChatTheme() : AppChatStatic.appChatTheme
50
+
51
+ //Register notif
52
+ if (AppChatStatic.fcmToken != '') {
53
+ const response = await registerFcmToken(AppChatStatic.fcmToken)
54
+ if (response.ok) {
55
+ await putString('fcmToken', AppChatStatic.fcmToken)
56
+ console.log("FCM token registration success, user can receive notification");
57
+ } else {
58
+ console.log("FCM token registration failed, user can't receive notification");
59
+ }
60
+ }
61
+
62
+
63
+ //Save result- initAppChatResult
64
+ if (accessToken != '' && contactId != '' && bundleIdValidation != '' && encryptedUrlParams != '' && !AppChatStatic.appChatTheme.isAnyEmpty()) AppChatStatic.initAppChatResult = 'SUCCESS'
65
+ else AppChatStatic.initAppChatResult = 'FAILED'
66
+ if (AppChatStatic.initAppChatResult === 'SUCCESS') await putString('externalId', externalId)
67
+ return AppChatStatic.initAppChatResult
68
+ } catch (error) {
69
+ return JSON.stringify(error)
70
+ }
71
+ }
72
+
73
+ async function updateAccessToken(clientId: string, clientSecret: string) {
74
+ const accessToken = await getString('accessToken')
75
+ if (accessToken) {
76
+ AppChatStatic.accessToken = accessToken
77
+ } else {
78
+ const authResponse = await authenticate(clientId, clientSecret)
79
+ const authData = await authResponse.json()
80
+ if (!authResponse.ok) return authResponse.status
81
+ AppChatStatic.accessToken = authData['access_token']
82
+ await putString('accessToken', AppChatStatic.accessToken)
83
+ }
84
+ return AppChatStatic.accessToken
85
+ }
86
+
87
+ async function updateContactId(externalId: string, fullName: string) {
88
+ const contactId = await getString('contactId')
89
+ if (contactId) {
90
+ AppChatStatic.contactId = contactId
91
+ } else {
92
+ const createContactResponse = await createContact(externalId, fullName)
93
+ if (!createContactResponse.ok) return createContactResponse.status
94
+ const createContactData = await createContactResponse.json()
95
+ AppChatStatic.contactId = createContactData.data['id']
96
+ await putString('contactId', AppChatStatic.contactId)
97
+ }
98
+ return AppChatStatic.contactId
99
+ }
100
+
101
+ async function validateBundleIdOrPackageName() {
102
+ const bundleIdValidation = await getString('bundleIdValidation')
103
+ let errorCode = undefined
104
+ let errorMessage = undefined
105
+ if (bundleIdValidation != 'success') {
106
+ const bundleId = await getBundleIdOrPackageName()
107
+ if (bundleId) {
108
+ const validateBundleIdResponse = await validateBundleId(bundleId)
109
+ const validateBundleIdData = await validateBundleIdResponse.json()
110
+ if (validateBundleIdResponse.ok) {
111
+ AppChatStatic.bundleIdValidation = validateBundleIdData?.data?.status === 'success' ? 'success' : 'failed'
112
+ if (AppChatStatic.bundleIdValidation == 'success') await putString('bundleIdValidation', 'success')
113
+ } else {
114
+ errorCode = validateBundleIdResponse.status
115
+ errorMessage = validateBundleIdData?.error?.messages[0]
116
+ AppChatStatic.bundleIdValidation = 'failed'
117
+ }
118
+ } else {
119
+ AppChatStatic.bundleIdValidation = 'not available'
120
+ }
121
+ } else {
122
+ AppChatStatic.bundleIdValidation = bundleIdValidation
123
+ }
124
+ if (AppChatStatic.bundleIdValidation != 'success' && AppChatStatic.bundleIdValidation != '') {
125
+ console.log(`${Constant.BUNDLE_ID_INVALID}\n${errorCode ?? ''} ${errorMessage ?? ''}`)
126
+ }
127
+ return AppChatStatic.bundleIdValidation
128
+ }
129
+
130
+ function updateEncryptedUrlParams() {
131
+ AppChatStatic.encryptedUrlParams = getEncryptedUrlParams({
132
+ access_token: AppChatStatic.accessToken,
133
+ app_id: AppChatStatic.appId,
134
+ contact_id: AppChatStatic.contactId
135
+ })
136
+ return AppChatStatic.encryptedUrlParams
137
+ }
138
+
139
+ async function updateAppChatTheme() {
140
+ const response = await getWidgetSetting()
141
+ const data = (await response.json())?.data
142
+ if (!response.ok) return response.status
143
+ AppChatStatic.appChatTheme.bgColorTheme = data.style?.theme?.bg_color
144
+ AppChatStatic.appChatTheme.bgColorThemeOverlaid = getOverlaidColor(AppChatStatic.appChatTheme.bgColorTheme, Constant.OVERLAY_COLOR, Constant.OVERLAY_OPACITY)
145
+ AppChatStatic.appChatTheme.fontColor = calculateTextColorOutput(AppChatStatic.appChatTheme.bgColorTheme)
146
+ AppChatStatic.appChatTheme.textHeader = data.header?.text
147
+ AppChatStatic.appChatTheme.textDescription = data.description?.text
148
+ return AppChatStatic.appChatTheme
149
+ }
@@ -0,0 +1,45 @@
1
+ import { getString, putString } from "../../data/local"
2
+ import { registerFcmToken, revokeFcmToken } from "../../data/remote"
3
+ import { AppChatStatic } from "../../data/static"
4
+
5
+ export async function registerNotification(fcmToken: string) {
6
+ const localFcmToken = await getString('fcmToken')
7
+ if (fcmToken === localFcmToken) return
8
+ if (AppChatStatic.initAppChatResult !== 'success') {
9
+ AppChatStatic.fcmToken = fcmToken
10
+ return "FCM token registration : waiting initialization success"
11
+ } else {
12
+ const response = await registerFcmToken(fcmToken)
13
+ if (response.ok) {
14
+ await putString('fcmToken', fcmToken)
15
+ return "FCM token registration success, user can receive notification"
16
+ } else {
17
+ return "FCM token registration failed, user can't receive notification"
18
+ }
19
+ }
20
+ }
21
+
22
+ export async function revokeNotification() {
23
+ const localFcmToken = await getString('fcmToken')
24
+ if (localFcmToken == null) return
25
+ const response = await revokeFcmToken(localFcmToken)
26
+ if (response.ok) {
27
+ AppChatStatic.fcmToken = ''
28
+ return "Revoke FCM token success"
29
+ }
30
+ else {
31
+ return "Revoke FCM token failed"
32
+ }
33
+ }
34
+
35
+ export function isAppChatPayload(payload: any): boolean {
36
+ if (payload?.data == null) {
37
+ return false;
38
+ } else {
39
+ return payload.data['notification_source'] == 'app-chat-qontak';
40
+ }
41
+ }
42
+
43
+ export function isAppChatOpened(): boolean {
44
+ return AppChatStatic.appChatOpened;
45
+ }