UNPKG

11.8 kBJavaScriptView Raw
1import { bindActionCreators } from 'redux';
2import { Platform } from 'react-native';
3import { connect } from 'react-redux';
4import * as UIActions from './actions/uiActions';
5import * as AppActions from './actions/appActions';
6import * as ILA from './actions/identityLoginActions';
7import AR from './array';
8
9export function RedConnect(component) {
10 return connect(mapStateToProps, mapDispatchToProps)(component);
11}
12import {
13 Linking
14} from 'react-native';
15
16var ReactNative = require('react-native');
17var {
18 AsyncStorage
19} = ReactNative;
20
21export const BATCH = 'BATCH';
22export function batch(batch) {
23 return {
24 type: BATCH,
25 batch
26 }
27}
28
29export function capitalizeFirstLetter(string) {
30 if (string)
31 return string.charAt(0).toUpperCase() + string.slice(1);
32 return string;
33}
34export function lowercaseFirstLetter(string) {
35 if (string)
36 return string.charAt(0).toLowerCase() + string.slice(1);
37 return string;
38}
39
40export function GUID() {
41 var guid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
42 var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
43 return v.toString(16);
44 });
45 return guid;
46}
47
48let actions = {};
49export function addActions(_actions) {
50 actions = Object.assign(actions, _actions);
51}
52export function mapDispatchToProps(dispatch) {
53 var actionBlob = Object.assign({},
54 ILA,
55 AppActions,
56 UIActions,
57 actions
58 );
59
60 var result = bindActionCreators(actionBlob, dispatch);
61 result.dispatch = dispatch;
62 return result;
63}
64
65export function mapStateToProps(state) {
66 return {
67 state: state
68 };
69};
70
71export function redAsyncStorage(store) {
72 return ((store) => next => action => {
73 var result = next(action);
74 if (action.store)
75 debouncedStoreAsync(store, action);
76 return result;
77 })(store);
78}
79
80
81function storeAsync(store, action) {
82 var res = action;
83 if (res.store) {
84 try {
85 var storeTarget = res.store;
86 var storpart = store.getState()[storeTarget];
87 if (res.storePart) {
88 storpart = storpart[res.storePart];
89 storpart = Object.assign({}, { ___isStorePart: res.storePart, store: res.store, [res.storePart]: storpart });
90 storeTarget = res.storePart;
91 }
92 if (storpart) {
93 var storestring = JSON.stringify(storpart);
94 return AsyncStorage.setItem(storeTarget, storestring);
95 }
96 }
97 catch (e) {
98 UIA.log(e);
99 }
100 }
101}
102
103var debouncedStoreAsync = debounce(storeAsync, 1000, {})
104/* Native method references for those with the same name as other `lodash` methods. */
105var nativeMax = Math.max;
106
107function isObject(value) {
108 // Avoid a V8 JIT bug in Chrome 19-20.
109 // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
110 var type = typeof value;
111 return !!value && (type == 'object' || type == 'function');
112}
113
114function debounce(func, wait, options) {
115 var args,
116 maxTimeoutId,
117 result,
118 stamp,
119 thisArg,
120 now = Date.now,
121 timeoutId,
122 trailingCall,
123 lastCalled = 0,
124 maxWait = false,
125 trailing = true;
126
127 if (typeof func != 'function') {
128 throw new TypeError(FUNC_ERROR_TEXT);
129 }
130 wait = wait < 0 ? 0 : (+wait || 0);
131 if (options === true) {
132 var leading = true;
133 trailing = false;
134 } else if (isObject(options)) {
135 leading = !!options.leading;
136 maxWait = 'maxWait' in options && nativeMax(+options.maxWait || 0, wait);
137 trailing = 'trailing' in options ? !!options.trailing : trailing;
138 }
139
140 function cancel() {
141 if (timeoutId) {
142 clearTimeout(timeoutId);
143 }
144 if (maxTimeoutId) {
145 clearTimeout(maxTimeoutId);
146 }
147 lastCalled = 0;
148 maxTimeoutId = timeoutId = trailingCall = undefined;
149 }
150
151 function complete(isCalled, id) {
152 if (id) {
153 clearTimeout(id);
154 }
155 maxTimeoutId = timeoutId = trailingCall = undefined;
156 if (isCalled) {
157 lastCalled = now();
158 result = func.apply(thisArg, args);
159 if (!timeoutId && !maxTimeoutId) {
160 args = thisArg = undefined;
161 }
162 }
163 }
164
165 function delayed() {
166 var remaining = wait - (now() - stamp);
167 if (remaining <= 0 || remaining > wait) {
168 complete(trailingCall, maxTimeoutId);
169 } else {
170 timeoutId = setTimeout(delayed, remaining);
171 }
172 }
173
174 function maxDelayed() {
175 complete(trailing, timeoutId);
176 }
177
178 function debounced() {
179 args = arguments;
180 stamp = now();
181 thisArg = this;
182 trailingCall = trailing && (timeoutId || !leading);
183
184 if (maxWait === false) {
185 var leadingCall = leading && !timeoutId;
186 } else {
187 if (!maxTimeoutId && !leading) {
188 lastCalled = stamp;
189 }
190 var remaining = maxWait - (stamp - lastCalled),
191 isCalled = remaining <= 0 || remaining > maxWait;
192
193 if (isCalled) {
194 if (maxTimeoutId) {
195 maxTimeoutId = clearTimeout(maxTimeoutId);
196 }
197 lastCalled = stamp;
198 result = func.apply(thisArg, args);
199 }
200 else if (!maxTimeoutId) {
201 maxTimeoutId = setTimeout(maxDelayed, remaining);
202 }
203 }
204 if (isCalled && timeoutId) {
205 timeoutId = clearTimeout(timeoutId);
206 }
207 else if (!timeoutId && wait !== maxWait) {
208 timeoutId = setTimeout(delayed, wait);
209 }
210 if (leadingCall) {
211 isCalled = true;
212 result = func.apply(thisArg, args);
213 }
214 if (isCalled && !timeoutId && !maxTimeoutId) {
215 args = thisArg = undefined;
216 }
217 return result;
218 }
219 debounced.cancel = cancel;
220 return debounced;
221}
222
223/** Extend Number object with method to convert numeric degrees to radians */
224if (Number.prototype.toRadians === undefined) {
225 Number.prototype.toRadians = function () { return this * Math.PI / 180; };
226}
227
228/** Extend Number object with method to convert radians to numeric (signed) degrees */
229if (Number.prototype.toDegrees === undefined) {
230 Number.prototype.toDegrees = function () { return this * 180 / Math.PI; };
231}
232
233export function convertMetersToMiles(dist) {
234 var result = dist;
235 result = (Math.round(100 * result * 0.000621371) / 100);
236 return result;
237}
238
239export function getDistance(coords1, coords2) {
240 if (!coords1 || !coords2) {
241 return null;
242 }
243 var lat1 = coords1.latitude || coords1.lat;
244 var lat2 = coords2.latitude || coords2.lat;
245 var lon1 = coords1.longitude || coords1.lng;
246 var lon2 = coords2.longitude || coords2.lng;
247 if (lat1 !== undefined && lat2 !== undefined && lon1 !== undefined && lon2 !== undefined) {
248 var R = 6371e3; // metres
249 var φ1 = lat1.toRadians();
250 var φ2 = lat2.toRadians();
251 var Δφ = (lat2 - lat1).toRadians();
252 var Δλ = (lon2 - lon1).toRadians();
253
254 var a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
255 Math.cos(φ1) * Math.cos(φ2) *
256 Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
257 var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
258
259 var d = R * c;
260 return d;
261 }
262}
263
264export function estimatedTravelTime(directions) {
265 if (directions && directions.routes && directions.routes[0]) {
266 var legs = directions.routes[0].legs;
267 var result = 0;
268 legs.map(leg => {
269 if (leg && leg.steps) {
270 result += leg.duration.value;
271 }
272 });
273 return Math.round(result / 60);
274 }
275
276 return null;
277}
278
279export function estimatedTravelTimeHM(directions) {
280 var eta = estimatedTravelTime(directions);
281 if (eta !== null) {
282 var hours = Math.floor(eta / 60);
283 var minutes = eta - (hours * 60);
284 return { hours, minutes }
285 }
286 return null;
287}
288
289export function email(to, cc, bcc, subject, body) {
290 let url = 'mailto:';
291 let argLength = arguments.length;
292
293 switch (argLength) {
294 case 0:
295 LaunchURL(url);
296 return;
297 case 5:
298 break;
299 default:
300 console.log('you must supply either 0 or 5 arguments. You supplied ' + argLength);
301 return;
302 }
303
304 // we use this Boolean to keep track of when we add a new parameter to the querystring
305 // it helps us know when we need to add & to separate parameters
306 let valueAdded = false;
307
308 if (isCorrectType('Array', arguments[0])) {
309 let validAddresses = getValidArgumentsFromArray(arguments[0], 'String');
310
311 if (validAddresses.length > 0) {
312 url += encodeURIComponent(validAddresses.join(','));
313 }
314 }
315
316 url += '?';
317
318 if (isCorrectType('Array', arguments[1])) {
319 let validAddresses = getValidArgumentsFromArray(arguments[1], 'String');
320
321 if (validAddresses.length > 0) {
322 valueAdded = true;
323 url += 'cc=' + encodeURIComponent(validAddresses.join(','));
324 }
325 }
326
327 if (isCorrectType('Array', arguments[2])) {
328 if (valueAdded) {
329 url += '&';
330 }
331
332 let validAddresses = getValidArgumentsFromArray(arguments[2], 'String');
333
334 if (validAddresses.length > 0) {
335 valueAdded = true;
336 url += 'bcc=' + encodeURIComponent(validAddresses.join(','));
337 }
338 }
339
340 if (isCorrectType('String', arguments[3])) {
341 if (valueAdded) {
342 url += '&';
343 }
344
345 valueAdded = true;
346 url += 'subject=' + encodeURIComponent(arguments[3]);
347 }
348
349 if (isCorrectType('String', arguments[4])) {
350 if (valueAdded) {
351 url += '&';
352 }
353
354 url += 'body=' + encodeURIComponent(arguments[4]);
355 }
356
357 LaunchURL(url);
358}
359
360export function phonecall(phoneNumber, prompt) {
361 if (arguments.length !== 2) {
362 console.log('you must supply exactly 2 arguments');
363 return;
364 }
365
366 if (!isCorrectType('String', phoneNumber)) {
367 console.log('the phone number must be provided as a String value');
368 return;
369 }
370
371 if (!isCorrectType('Boolean', prompt)) {
372 console.log('the prompt parameter must be a Boolean');
373 return;
374 }
375
376 let url;
377
378 if (Platform.OS !== 'android') {
379 url = prompt ? 'telprompt:' : 'tel:';
380 }
381 else {
382 url = 'tel:';
383 }
384
385 url += phoneNumber;
386
387 LaunchURL(url);
388}
389
390export function text(phoneNumber = null, body = null) {
391 if (arguments.length > 2) {
392 console.log('you supplied too many arguments. You can either supply 0 or 1 or 2');
393 return;
394 }
395
396 let url = 'sms:';
397
398 if (phoneNumber) {
399 if (isCorrectType('String', phoneNumber)) {
400 url += phoneNumber;
401 } else {
402 console.log('the phone number should be provided as a string. It was provided as '
403 + Object.prototype.toString.call(phoneNumber).slice(8, -1)
404 + ',ignoring the value provided');
405 }
406 }
407
408 if (body) {
409 if (isCorrectType('String', body)) {
410 // for some strange reason android seems to have issues with ampersands in the body unless it is encoded twice!
411 // iOS only needs encoding once
412 if (Platform.OS === 'android') body = encodeURIComponent(body);
413 url += Platform.OS === 'ios' ? `&body=${encodeURIComponent(body)}` : `?body=${encodeURIComponent(body)}`;
414 } else {
415 console.log('the body should be provided as a string. It was provided as '
416 + Object.prototype.toString.call(body).slice(8, -1)
417 + ',ignoring the value provided');
418 }
419 }
420
421 LaunchURL(url);
422}
423
424function getValidArgumentsFromArray(array, type) {
425 var validValues = [];
426 array.forEach(function (value) {
427 if (isCorrectType(type, value)) {
428 validValues.push(value);
429 }
430 });
431
432 return validValues;
433};
434
435function isCorrectType(expected, actual) {
436 return Object.prototype.toString.call(actual).slice(8, -1) === expected;
437};
438
439export function LaunchURL(url) {
440 Linking.canOpenURL(url).then(supported => {
441 if (!supported) {
442 console.log('Can\'t handle url: ' + url);
443 } else {
444 Linking.openURL(url)
445 .catch(err => {
446 if (url.includes('telprompt')) {
447 // telprompt was cancelled and Linking openURL method sees this as an error
448 // it is not a true error so ignore it to prevent apps crashing
449 // see https://github.com/anarchicknight/react-native-communications/issues/39
450 } else {
451 console.warn('openURL error', err)
452 }
453 });
454 }
455 }).catch(err => console.warn('An unexpected error happened', err));
456};