rn-erxes-sdk 0.1.13 → 0.1.14

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 (92) hide show
  1. package/lib/commonjs/App.js +16 -13
  2. package/lib/commonjs/App.js.map +1 -1
  3. package/lib/commonjs/Widget.js +36 -27
  4. package/lib/commonjs/Widget.js.map +1 -1
  5. package/lib/commonjs/components/Avatar.js +60 -0
  6. package/lib/commonjs/components/Avatar.js.map +1 -0
  7. package/lib/commonjs/components/InputTools.js +3 -2
  8. package/lib/commonjs/components/InputTools.js.map +1 -1
  9. package/lib/commonjs/context/Context.js +11 -0
  10. package/lib/commonjs/context/Context.js.map +1 -0
  11. package/lib/commonjs/graphql/apolloClient.js +2 -0
  12. package/lib/commonjs/graphql/apolloClient.js.map +1 -1
  13. package/lib/commonjs/graphql/query.js +0 -9
  14. package/lib/commonjs/graphql/query.js.map +1 -1
  15. package/lib/commonjs/screen/conversation/ConversationDetail.js +105 -70
  16. package/lib/commonjs/screen/conversation/ConversationDetail.js.map +1 -1
  17. package/lib/commonjs/screen/conversation/Conversations.js +24 -40
  18. package/lib/commonjs/screen/conversation/Conversations.js.map +1 -1
  19. package/lib/commonjs/screen/conversation/Message.js +5 -17
  20. package/lib/commonjs/screen/conversation/Message.js.map +1 -1
  21. package/lib/commonjs/screen/greetings/Greetings.js +13 -9
  22. package/lib/commonjs/screen/greetings/Greetings.js.map +1 -1
  23. package/lib/commonjs/screen/greetings/Social.js +1 -1
  24. package/lib/commonjs/screen/greetings/Social.js.map +1 -1
  25. package/lib/commonjs/utils/utils.js +11 -1
  26. package/lib/commonjs/utils/utils.js.map +1 -1
  27. package/lib/module/App.js +16 -13
  28. package/lib/module/App.js.map +1 -1
  29. package/lib/module/Widget.js +36 -27
  30. package/lib/module/Widget.js.map +1 -1
  31. package/lib/module/components/Avatar.js +52 -0
  32. package/lib/module/components/Avatar.js.map +1 -0
  33. package/lib/module/components/InputTools.js +4 -3
  34. package/lib/module/components/InputTools.js.map +1 -1
  35. package/lib/module/context/Context.js +4 -0
  36. package/lib/module/context/Context.js.map +1 -0
  37. package/lib/module/graphql/apolloClient.js +2 -0
  38. package/lib/module/graphql/apolloClient.js.map +1 -1
  39. package/lib/module/graphql/query.js +0 -9
  40. package/lib/module/graphql/query.js.map +1 -1
  41. package/lib/module/screen/conversation/ConversationDetail.js +108 -73
  42. package/lib/module/screen/conversation/ConversationDetail.js.map +1 -1
  43. package/lib/module/screen/conversation/Conversations.js +23 -41
  44. package/lib/module/screen/conversation/Conversations.js.map +1 -1
  45. package/lib/module/screen/conversation/Message.js +7 -19
  46. package/lib/module/screen/conversation/Message.js.map +1 -1
  47. package/lib/module/screen/greetings/Greetings.js +11 -9
  48. package/lib/module/screen/greetings/Greetings.js.map +1 -1
  49. package/lib/module/screen/greetings/Social.js +1 -1
  50. package/lib/module/screen/greetings/Social.js.map +1 -1
  51. package/lib/module/utils/utils.js +9 -0
  52. package/lib/module/utils/utils.js.map +1 -1
  53. package/lib/typescript/App.d.ts +1 -0
  54. package/lib/typescript/App.d.ts.map +1 -1
  55. package/lib/typescript/Widget.d.ts.map +1 -1
  56. package/lib/typescript/components/Avatar.d.ts +4 -0
  57. package/lib/typescript/components/Avatar.d.ts.map +1 -0
  58. package/lib/typescript/components/InputTools.d.ts.map +1 -1
  59. package/lib/typescript/context/Context.d.ts +4 -0
  60. package/lib/typescript/context/Context.d.ts.map +1 -0
  61. package/lib/typescript/graphql/apolloClient.d.ts.map +1 -1
  62. package/lib/typescript/graphql/query.d.ts.map +1 -1
  63. package/lib/typescript/screen/conversation/ConversationDetail.d.ts +1 -1
  64. package/lib/typescript/screen/conversation/ConversationDetail.d.ts.map +1 -1
  65. package/lib/typescript/screen/conversation/Conversations.d.ts +1 -1
  66. package/lib/typescript/screen/conversation/Conversations.d.ts.map +1 -1
  67. package/lib/typescript/screen/conversation/Message.d.ts.map +1 -1
  68. package/lib/typescript/screen/greetings/Greetings.d.ts +1 -1
  69. package/lib/typescript/screen/greetings/Greetings.d.ts.map +1 -1
  70. package/lib/typescript/utils/utils.d.ts +1 -0
  71. package/lib/typescript/utils/utils.d.ts.map +1 -1
  72. package/package.json +1 -1
  73. package/src/App.tsx +15 -11
  74. package/src/Widget.tsx +50 -30
  75. package/src/components/Avatar.tsx +56 -0
  76. package/src/components/InputTools.tsx +3 -9
  77. package/src/context/Context.ts +5 -0
  78. package/src/graphql/apolloClient.ts +1 -0
  79. package/src/graphql/query.ts +0 -9
  80. package/src/screen/conversation/ConversationDetail.tsx +143 -101
  81. package/src/screen/conversation/Conversations.tsx +24 -35
  82. package/src/screen/conversation/Message.tsx +6 -23
  83. package/src/screen/greetings/Greetings.tsx +12 -8
  84. package/src/screen/greetings/Social.tsx +1 -1
  85. package/src/utils/utils.tsx +9 -0
  86. package/lib/commonjs/FAQ.js +0 -177
  87. package/lib/commonjs/FAQ.js.map +0 -1
  88. package/lib/module/FAQ.js +0 -167
  89. package/lib/module/FAQ.js.map +0 -1
  90. package/lib/typescript/FAQ.d.ts +0 -4
  91. package/lib/typescript/FAQ.d.ts.map +0 -1
  92. package/src/FAQ.tsx +0 -171
@@ -11,57 +11,88 @@ import {
11
11
  TouchableWithoutFeedback,
12
12
  Keyboard,
13
13
  Image,
14
+ SafeAreaView,
14
15
  } from 'react-native';
15
- import React, { useEffect } from 'react';
16
- import { useMutation, useQuery, useSubscription } from '@apollo/client';
16
+ import React, { useContext, useEffect } from 'react';
17
+ import { useMutation, useQuery } from '@apollo/client';
17
18
  import { widgetsConversationDetail } from '../../graphql/query';
18
- import { useSafeAreaInsets } from 'react-native-safe-area-context';
19
19
  import Message from './Message';
20
20
  import InputTools from '../../components/InputTools';
21
21
  import { widgetsInsertMessage } from '../../graphql/mutation';
22
22
  import { conversationMessageInserted } from '../../graphql/subscription';
23
23
  import { getAttachmentUrl } from '../../utils/utils';
24
- import { getLocalStorageItem, setLocalStorageItem } from '../../utils/common';
24
+ import AppContext from '../../context/Context';
25
+ import AsyncStorage from '@react-native-async-storage/async-storage';
25
26
 
26
- const ConversationDetail = (props: any) => {
27
- const { route, navigation } = props;
28
- const { _id, integrationId, bgColor, brand, customerId, visitorId } =
29
- route.params;
27
+ const ConversationDetail = () => {
28
+ const value = useContext(AppContext);
29
+
30
+ const {
31
+ brand,
32
+ conversationId,
33
+ bgColor,
34
+ integrationId,
35
+ customerId,
36
+ visitorId,
37
+ setConnection,
38
+ sendIcon,
39
+ } = value;
30
40
 
31
41
  const [messages, setMessages] = React.useState<any>([]);
32
42
 
33
- const { data, loading } = useQuery(widgetsConversationDetail, {
34
- variables: {
35
- _id,
36
- integrationId,
37
- },
38
- fetchPolicy: 'network-only',
39
- skip: !_id,
40
- });
43
+ // console.log(messages);
41
44
 
42
- const { data: subscriptionData } = useSubscription(
43
- conversationMessageInserted,
45
+ const { data, loading, subscribeToMore } = useQuery(
46
+ widgetsConversationDetail,
44
47
  {
45
- variables: { _id },
48
+ variables: {
49
+ _id: conversationId,
50
+ integrationId,
51
+ },
46
52
  fetchPolicy: 'network-only',
47
- skip: !_id,
53
+ skip: !conversationId,
48
54
  }
49
55
  );
50
56
 
51
57
  useEffect(() => {
52
- let shouldAdd = subscriptionData && data;
53
- if (data?.length > 0) {
54
- shouldAdd =
55
- subscriptionData?.conversationMessageInserted?._id !== data[0]._id;
56
- }
57
- if (shouldAdd) {
58
- setMessages((prev: any) => [
59
- subscriptionData?.conversationMessageInserted,
60
- ...prev,
61
- ]);
62
- }
63
- // eslint-disable-next-line react-hooks/exhaustive-deps
64
- }, [subscriptionData]);
58
+ const unSubsToMore = subscribeToMore({
59
+ document: conversationMessageInserted,
60
+ variables: { _id: conversationId },
61
+ updateQuery: (prev, { subscriptionData }) => {
62
+ const message = subscriptionData.data.conversationMessageInserted;
63
+ const tempWidgetsConversationDetail =
64
+ prev.widgetsConversationDetail || {};
65
+ const tempMessages = tempWidgetsConversationDetail?.messages || [];
66
+
67
+ // check whether or not already inserted
68
+ const prevEntry = tempMessages.find((m: any) => m._id === message?._id);
69
+
70
+ if (prevEntry) {
71
+ return prev;
72
+ }
73
+
74
+ // do not show internal or bot messages
75
+ if (message.internal || message.fromBot) {
76
+ return prev;
77
+ }
78
+
79
+ // add new message to messages list
80
+ const next = {
81
+ ...prev,
82
+ widgetsConversationDetail: {
83
+ ...tempWidgetsConversationDetail,
84
+ messages: [...tempMessages, message],
85
+ },
86
+ };
87
+
88
+ return next;
89
+ },
90
+ });
91
+
92
+ return () => {
93
+ unSubsToMore;
94
+ };
95
+ }, [conversationId, subscribeToMore]);
65
96
 
66
97
  useEffect(() => {
67
98
  if (data?.widgetsConversationDetail?.messages) {
@@ -80,7 +111,7 @@ const ConversationDetail = (props: any) => {
80
111
  integrationId,
81
112
  customerId,
82
113
  visitorId,
83
- conversationId: _id,
114
+ conversationId,
84
115
  contentType: 'text',
85
116
  message: text,
86
117
  },
@@ -90,17 +121,21 @@ const ConversationDetail = (props: any) => {
90
121
  return console.log(res.errors);
91
122
  }
92
123
  if (res.data.widgetsInsertMessage) {
93
- const cachedCustomerId = getLocalStorageItem('customerId');
94
- if (!cachedCustomerId) {
124
+ if (!customerId) {
95
125
  const tempCustomerId = res.data.widgetsInsertMessage.customerId;
96
- setLocalStorageItem('customerId', tempCustomerId);
126
+ const jsonValue = JSON.stringify(tempCustomerId);
127
+ AsyncStorage.setItem('cachedCustomerId', jsonValue);
128
+ setConnection({
129
+ visitorId,
130
+ cachedCustomerId: tempCustomerId,
131
+ });
97
132
  }
98
133
  let shouldAdd = messages?.length === 0;
99
134
  if (!shouldAdd) {
100
- shouldAdd = res.data.widgetsInsertMessage._id !== data[0]._id;
135
+ shouldAdd = res.data.widgetsInsertMessage._id !== messages[0]._id;
101
136
  }
102
137
  if (shouldAdd) {
103
- const newArray = [res.data.widgetsInsertMessage, ...data];
138
+ const newArray = [res.data.widgetsInsertMessage, ...messages];
104
139
  setMessages(newArray);
105
140
  }
106
141
  }
@@ -142,7 +177,6 @@ const ConversationDetail = (props: any) => {
142
177
  return (
143
178
  <View style={{ flex: 1 }}>
144
179
  <Header
145
- navigation={navigation}
146
180
  brand={brand}
147
181
  bgColor={bgColor}
148
182
  users={data?.widgetsConversationDetail?.participatedUsers}
@@ -154,7 +188,7 @@ const ConversationDetail = (props: any) => {
154
188
  keyboardVerticalOffset={100}
155
189
  >
156
190
  <TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
157
- <InputTools onSend={onSend} bgColor={bgColor} />
191
+ <InputTools onSend={onSend} bgColor={bgColor} sendIcon={sendIcon} />
158
192
  </TouchableWithoutFeedback>
159
193
  </KeyboardAvoidingView>
160
194
  </View>
@@ -165,25 +199,83 @@ const ConversationDetail = (props: any) => {
165
199
  export default ConversationDetail;
166
200
 
167
201
  const Header = (props: any) => {
168
- const { navigation, brand, bgColor, users } = props;
202
+ const { brand, bgColor, users } = props;
203
+
204
+ const value = useContext(AppContext);
169
205
 
170
- const insets = useSafeAreaInsets();
206
+ const { backIcon, setConversationId } = value;
171
207
 
172
208
  if (users?.length > 0) {
173
- const url = getAttachmentUrl(users[0]?.details?.avatar);
209
+ let source;
210
+ if (users[0]?.details?.avatar) {
211
+ source = { uri: getAttachmentUrl(users[0]?.details?.avatar) };
212
+ } else {
213
+ source = require('../../assets/images/avatar.png');
214
+ }
174
215
  return (
216
+ <SafeAreaView
217
+ style={{
218
+ backgroundColor: bgColor,
219
+ }}
220
+ >
221
+ <View
222
+ style={{
223
+ flexDirection: 'row',
224
+ alignItems: 'center',
225
+ paddingBottom: 20,
226
+ }}
227
+ >
228
+ <TouchableOpacity
229
+ onPress={() => {
230
+ setConversationId(null);
231
+ }}
232
+ style={[
233
+ styles.backStyle,
234
+ {
235
+ backgroundColor: '#2F1F69',
236
+ },
237
+ ]}
238
+ >
239
+ {backIcon}
240
+ </TouchableOpacity>
241
+ <View
242
+ style={[
243
+ styles.title,
244
+ {
245
+ marginLeft: 10,
246
+ alignItems: 'center',
247
+ flex: 1,
248
+ },
249
+ ]}
250
+ >
251
+ <Image source={source} style={styles.avatar} resizeMode="stretch" />
252
+ <View style={{ marginLeft: 10 }}>
253
+ <Text style={{ fontWeight: '600' }}>
254
+ {users[0]?.details?.fullName}
255
+ </Text>
256
+ </View>
257
+ </View>
258
+ </View>
259
+ </SafeAreaView>
260
+ );
261
+ }
262
+
263
+ return (
264
+ <SafeAreaView
265
+ style={{
266
+ backgroundColor: bgColor,
267
+ }}
268
+ >
175
269
  <View
176
270
  style={{
177
271
  flexDirection: 'row',
178
272
  alignItems: 'center',
179
273
  paddingBottom: 20,
180
- backgroundColor: bgColor || 'green',
181
- paddingTop: insets.top,
182
274
  }}
183
275
  >
184
276
  <TouchableOpacity
185
277
  onPress={() => {
186
- navigation.goBack();
278
+ setConversationId(null);
187
279
  }}
188
280
  style={[
189
281
  styles.backStyle,
@@ -192,63 +284,13 @@ const Header = (props: any) => {
192
284
  },
193
285
  ]}
194
286
  >
195
- <Text>A</Text>
287
+ {backIcon}
196
288
  </TouchableOpacity>
197
- <View
198
- style={[
199
- styles.title,
200
- {
201
- marginLeft: 10,
202
- alignItems: 'center',
203
- flex: 1,
204
- },
205
- ]}
206
- >
207
- <Image
208
- source={{ uri: url, cache: 'force-cache' }}
209
- style={styles.avatar}
210
- resizeMode="stretch"
211
- />
212
- <View style={{ marginLeft: 10 }}>
213
- <Text style={{ fontWeight: '600' }}>
214
- {users[0]?.details?.fullName}
215
- </Text>
216
- <Text style={{ color: '#686868' }}>
217
- {users[0]?.details?.position}
218
- </Text>
219
- </View>
289
+ <View style={[styles.title]}>
290
+ <Text style={{ fontWeight: '600', fontSize: 16 }}>{brand?.name}</Text>
220
291
  </View>
221
292
  </View>
222
- );
223
- }
224
-
225
- return (
226
- <View
227
- style={{
228
- flexDirection: 'row',
229
- alignItems: 'center',
230
- paddingBottom: 20,
231
- backgroundColor: bgColor || 'green',
232
- paddingTop: insets.top,
233
- }}
234
- >
235
- <TouchableOpacity
236
- onPress={() => {
237
- navigation.goBack();
238
- }}
239
- style={[
240
- styles.backStyle,
241
- {
242
- backgroundColor: '#2F1F69',
243
- },
244
- ]}
245
- >
246
- <Text>A</Text>
247
- </TouchableOpacity>
248
- <View style={[styles.title]}>
249
- <Text style={{ fontWeight: '600', fontSize: 16 }}>{brand?.name}</Text>
250
- </View>
251
- </View>
293
+ </SafeAreaView>
252
294
  );
253
295
  };
254
296
 
@@ -4,23 +4,34 @@ import {
4
4
  Text,
5
5
  FlatList,
6
6
  StyleSheet,
7
- Image,
8
7
  TouchableOpacity,
9
8
  RefreshControl,
10
9
  } from 'react-native';
11
- import React from 'react';
10
+ import React, { useContext } from 'react';
12
11
  import { widgetsConversations } from '../../graphql/query';
13
12
  import { useQuery } from '@apollo/client';
14
13
  import dayjs from 'dayjs';
15
- import images from '../../assets/images';
16
14
  import FAB from '../../components/FAB';
17
- import { getAttachmentUrl } from '../../utils/utils';
15
+ import AppContext from '../../context/Context';
16
+ import Avatar from '../../components/Avatar';
18
17
 
19
- const Conversations = (props: any) => {
20
- const { customerId, visitorId, integrationId, bgColor, brand, newChatIcon } =
21
- props;
18
+ const Conversations = () => {
19
+ const value = useContext(AppContext);
22
20
 
23
- console.log(brand);
21
+ const {
22
+ customerId,
23
+ visitorId,
24
+ newChatIcon,
25
+ bgColor,
26
+ integrationId,
27
+ setConversationId,
28
+ } = value;
29
+
30
+ console.log({
31
+ customerId,
32
+ visitorId,
33
+ integrationId,
34
+ });
24
35
 
25
36
  const { data, loading, refetch } = useQuery(widgetsConversations, {
26
37
  variables: {
@@ -45,31 +56,18 @@ const Conversations = (props: any) => {
45
56
  }
46
57
 
47
58
  const renderItem = ({ item }: any) => {
48
- let source = images.avatar;
49
59
  let name = 'Support Staff';
50
60
  if (item?.participatedUsers?.length > 0) {
51
- source = {
52
- uri: getAttachmentUrl(item?.participatedUsers[0]?.details?.avatar),
53
- cache: 'only-if-cached',
54
- };
55
61
  name = item?.participatedUsers[0]?.details?.fullName;
56
62
  }
57
63
  return (
58
64
  <TouchableOpacity
59
65
  style={styles.itemContainer}
60
66
  onPress={() => {
61
- console.log('+');
62
- // navigation.navigate('ConversationDetail', {
63
- // _id: item._id,
64
- // integrationId,
65
- // customerId,
66
- // visitorId,
67
- // bgColor,
68
- // brand,
69
- // });
67
+ setConversationId(item._id);
70
68
  }}
71
69
  >
72
- <Image source={source} style={styles.image} resizeMode="stretch" />
70
+ <Avatar user={item?.participatedUsers[0]} bgColor={bgColor} />
73
71
  <View style={{ flex: 1, marginLeft: 10 }}>
74
72
  <Text style={{ fontWeight: '500' }}>{name}</Text>
75
73
  <Text style={{ color: '#686868' }}>{item?.content}</Text>
@@ -125,18 +123,9 @@ const Conversations = (props: any) => {
125
123
  ItemSeparatorComponent={seperator}
126
124
  />
127
125
  <FAB
128
- onPress={
129
- () => {
130
- console.log('+++++');
131
- }
132
- // navigation.navigate('ConversationDetail', {
133
- // _id: '',
134
- // integrationId,
135
- // customerId,
136
- // bgColor,
137
- // brand,
138
- // })
139
- }
126
+ onPress={() => {
127
+ setConversationId('');
128
+ }}
140
129
  backgroundColor={bgColor}
141
130
  icon={newChatIcon}
142
131
  />
@@ -1,17 +1,11 @@
1
1
  /* eslint-disable react-native/no-inline-styles */
2
- import {
3
- View,
4
- Text,
5
- StyleSheet,
6
- Dimensions,
7
- Image,
8
- Linking,
9
- } from 'react-native';
2
+ import { View, Text, StyleSheet, Dimensions, Linking } from 'react-native';
10
3
  import React from 'react';
11
4
  import dayjs from 'dayjs';
12
5
  import HTML from 'react-native-render-html';
13
- import { getAttachmentUrl, strip_html } from '../../utils/utils';
6
+ import { strip_html } from '../../utils/utils';
14
7
  import Attachment from './Attachment';
8
+ import Avatar from '../../components/Avatar';
15
9
 
16
10
  const { width } = Dimensions.get('window');
17
11
 
@@ -20,19 +14,6 @@ const Message = (props: any) => {
20
14
 
21
15
  const position = item?.customerId ? 'right' : 'left';
22
16
 
23
- const renderAvatar = () => {
24
- const url = getAttachmentUrl(item?.user?.details?.avatar);
25
- return (
26
- <View style={{ marginRight: 10 }}>
27
- <Image
28
- source={{ uri: url, cache: 'force-cache' }}
29
- style={styles.avatar}
30
- resizeMode="stretch"
31
- />
32
- </View>
33
- );
34
- };
35
-
36
17
  const renderContent = () => {
37
18
  const strippedContent = strip_html(item?.content || '');
38
19
  if (strippedContent?.length === 0) {
@@ -43,7 +24,9 @@ const Message = (props: any) => {
43
24
 
44
25
  return (
45
26
  <View style={[customStyles[position].container]}>
46
- {position === 'left' ? renderAvatar() : null}
27
+ {position === 'left' ? (
28
+ <Avatar user={item?.user} bgColor={bgColor} />
29
+ ) : null}
47
30
  <View>
48
31
  <View
49
32
  style={[
@@ -6,20 +6,23 @@ import {
6
6
  SafeAreaView,
7
7
  TouchableOpacity,
8
8
  } from 'react-native';
9
- import React from 'react';
9
+ import React, { useContext } from 'react';
10
10
  import Social from './Social';
11
11
  import Supporters from './Supporters';
12
+ import AppContext from '../../context/Context';
13
+
14
+ const Greetings = () => {
15
+ const value = useContext(AppContext);
12
16
 
13
- const Greetings = (props: any) => {
14
17
  const {
15
18
  greetings,
16
- bgColor,
17
- textColor,
18
- integrationId,
19
19
  hasBack,
20
20
  onBack,
21
21
  backIcon,
22
- } = props;
22
+ bgColor,
23
+ textColor,
24
+ integrationId,
25
+ } = value;
23
26
 
24
27
  return (
25
28
  <SafeAreaView style={{ backgroundColor: bgColor }}>
@@ -41,13 +44,14 @@ const Greetings = (props: any) => {
41
44
  <View style={[styles.title]}>
42
45
  <View style={{ marginTop: 20 }}>
43
46
  <Text style={{ fontWeight: '600', fontSize: 18, color: textColor }}>
44
- {greetings?.messages?.greetings?.title}
47
+ {greetings?.messages?.greetings?.title || 'Тавтай морилно уу'}
45
48
  </Text>
46
49
  <View style={{ marginTop: 20 }}>
47
50
  <Social links={greetings?.links} />
48
51
  </View>
49
52
  <Text style={{ marginTop: 20, color: textColor, opacity: 0.8 }}>
50
- {greetings?.messages?.greetings?.message}
53
+ {greetings?.messages?.greetings?.message ||
54
+ 'Хэрэв танд асуулт байвал бидэнд мэдэгдээрэй! Бид туслахдаа баяртай байх болно.'}
51
55
  </Text>
52
56
  </View>
53
57
  <Supporters integrationId={integrationId} />
@@ -30,7 +30,7 @@ const Social = (props: any) => {
30
30
  {links?.facebook
31
31
  ? renderSocial(links.facebook, images.facebook, 0)
32
32
  : null}
33
- {links?.twitter ? renderSocial(links.twitter, images.twitter, 1) : null}
33
+ {links.twitter ? renderSocial(links.twitter, images.twitter, 1) : null}
34
34
  {links?.youtube ? renderSocial(links.youtube, images.youtube, 2) : null}
35
35
  </View>
36
36
  );
@@ -21,3 +21,12 @@ export const strip_html = (string: any, withoutCut?: boolean) => {
21
21
  return cut;
22
22
  }
23
23
  };
24
+
25
+ // check if valid url
26
+ export const isValidURL = (u: string) => {
27
+ try {
28
+ return Boolean(new URL(u));
29
+ } catch (e) {
30
+ return false;
31
+ }
32
+ };