@qikdev/sdk 1.0.10 → 1.0.13

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.
@@ -1,7 +1,13 @@
1
1
  /**
2
- * @name filter
3
- * @classdesc Provides functions helpful for interacting with the filtering system
2
+ * @classdesc Helpers for filter configurations — the { operator, filters } objects used as the `filter` of
3
+ * sdk.content.list. A filter group is { operator: 'and' | 'or' | 'nor', filters: [...] }, where each entry is
4
+ * either a nested group or a condition { key, comparator, value, value2, values }. These helpers work out which
5
+ * conditions are "active" (complete enough to apply), e.g. to show a count of applied filters or to know when
6
+ * a filter has changed. They run locally.
7
+ * @alias filter
4
8
  * @class
9
+ * @example
10
+ * const count = sdk.filter.activeFilters(filter).filter((entry) => !entry.operator).length;
5
11
  */
6
12
 
7
13
 
@@ -40,7 +46,20 @@ function compactFunction(val) {
40
46
 
41
47
  ///////////////////////
42
48
 
49
+ /**
50
+ * @description Normalises a filter key: drops anything after a '|' and a leading '.'.
51
+ * @alias filter.cleanKey
52
+ * @param {String} key The key.
53
+ * @returns {String} The cleaned key ('' when the key is missing).
54
+ * @example
55
+ * sdk.filter.cleanKey('.data.colour|label'); // 'data.colour'
56
+ */
43
57
  service.cleanKey = function(key) {
58
+ // A missing key is empty, not the text 'undefined' or 'null'
59
+ if (key === undefined || key === null) {
60
+ return '';
61
+ }
62
+
44
63
  key = String(key).split('|')[0];
45
64
 
46
65
  if (key[0] == '.') {
@@ -53,16 +72,18 @@ service.cleanKey = function(key) {
53
72
  ///////////////////////
54
73
 
55
74
  /**
56
- * @description Check if an object is a valid filter
75
+ * @description Checks whether a filter condition or group is complete enough to apply. A condition needs a key and
76
+ * either a value, a value2 or a non-empty values array — except comparators that need no value ('empty',
77
+ * 'notempty', 'datetoday', 'datenottoday', 'datebeforetoday', 'dateaftertoday', 'dateweektoday', 'datemonthtoday',
78
+ * 'datebeforenow', 'dateafternow', 'datethisweek', 'datethismonth'). A group is valid when at least one of its entries is.
79
+ * The comparator name itself isn't checked.
57
80
  * @alias filter.isValidFilter
58
- * @param {Object} filter The filter block to check
81
+ * @param {Object} filter A condition { key, comparator, value, value2, values } or a group { operator, filters }.
82
+ * @returns {Boolean} True when it can be applied (a truthy number for a condition that only has `values`).
59
83
  * @example
60
- * // Missing a 'key'
61
- * const invalidFilter = sdk.filter.isValidFilter({comparator:'in', values:['Red', 'Green', 'Blue']})
62
- * // returns false
63
- *
64
- * const validFilter = sdk.filter.isValidFilter({key:'favoriteColor', comparator:'in', values:['Red', 'Green', 'Blue']})
65
- * // returns true
84
+ * sdk.filter.isValidFilter({ key: 'data.colour', comparator: 'in', values: [] }); // falsy: nothing to compare with
85
+ * sdk.filter.isValidFilter({ key: 'data.colour', comparator: 'in', values: ['Red', 'Green'] }); // truthy
86
+ * sdk.filter.isValidFilter({ key: 'data.notes', comparator: 'empty' }); // true
66
87
  */
67
88
  service.isValidFilter = function(filter) {
68
89
  if (!filter) {
@@ -185,51 +206,30 @@ service.isValidFilter = function(filter) {
185
206
  ////////////////////////////////////////////////
186
207
 
187
208
  /**
188
- *
189
- * @description Returns an array of all active filters
209
+ * @description Collects every valid (active) entry in a filter configuration, walking nested groups. Valid groups are
210
+ * included as well as valid conditions, so filter out entries with an `operator` to get just the conditions.
190
211
  * @alias filter.activeFilters
191
- * @param {Object} filterConfiguration A filter configuration object
212
+ * @param {Object} filterConfiguration A filter group { operator, filters }.
213
+ * @returns {Array<Object>} The valid groups and conditions, outermost first.
192
214
  * @example
193
- *
194
- * const filterConfiguration = {
195
- * "operator": "and",
196
- * "filters": [
197
- * {
198
- * "operator": "and",
199
- * "filters": [
200
- * {
201
- * "key": "formData.person.firstName",
202
- * "comparator": "startswith",
203
- * "value": "c"
204
- * },
205
- * {
206
- * "key": "invalid no comparator",
207
- * "value": "e"
208
- * }
209
- * {
210
- * "key": "formData.person.lastName",
211
- * "comparator": "endswith",
212
- * "value": "e"
213
- * }
214
- * ]
215
- * }
216
- * ]
217
- * }
215
+ * const filterConfiguration = {
216
+ * operator: 'and',
217
+ * filters: [
218
+ * {
219
+ * operator: 'or',
220
+ * filters: [
221
+ * { key: 'formData.person.firstName', comparator: 'startswith', value: 'c' },
222
+ * { key: 'formData.person.lastName', comparator: 'endswith' }, // no value, so not active
223
+ * { key: 'formData.person.lastName', comparator: 'endswith', value: 'e' },
224
+ * ],
225
+ * },
226
+ * ],
227
+ * };
218
228
  *
219
- * const activeFilters = sdk.filter.activeFilters(filterConfiguration);
220
- *
221
- * const returns = [
222
- * {
223
- * "key": "formData.person.firstName",
224
- * "comparator": "startswith",
225
- * "value": "c"
226
- * },
227
- * {
228
- * "key": "formData.person.lastName",
229
- * "comparator": "endswith",
230
- * "value": "e"
231
- * }]
232
- *
229
+ * sdk.filter.activeFilters(filterConfiguration);
230
+ * // [outer group, inner group,
231
+ * // { key: 'formData.person.firstName', comparator: 'startswith', value: 'c' },
232
+ * // { key: 'formData.person.lastName', comparator: 'endswith', value: 'e' }]
233
233
  */
234
234
  service.activeFilters = function(filterConfiguration) {
235
235
  var memo = [];
@@ -263,40 +263,30 @@ service.activeFilters = function(filterConfiguration) {
263
263
 
264
264
 
265
265
  /**
266
- *
267
- * @description Returns an array of all keys that are being actively filtered on
266
+ * @description Lists the distinct field keys used by active conditions. Anything after a '|' in a key and a leading '.'
267
+ * are removed, and so are '[]' array markers unless options.trimArrayDelimeters is false.
268
268
  * @alias filter.activeFilterKeys
269
- * @param {Object} filterConfiguration A filter configuration object
269
+ * @param {Object} filterConfiguration A filter group { operator, filters }.
270
+ * @param {Object} [options] Options.
271
+ * @param {Boolean} [options.trimArrayDelimeters] Set to false to keep '[]' in keys such as 'items[].title'.
272
+ * @returns {Array<String>} The keys.
270
273
  * @example
271
- *
272
- * const filterConfiguration = {
273
- * "operator": "and",
274
- * "filters": [
275
- * {
276
- * "operator": "and",
277
- * "filters": [
278
- * {
279
- * "key": "formData.person.firstName",
280
- * "comparator": "startswith",
281
- * "value": "c"
282
- * },
283
- * {
284
- * "key": "invalid no comparator",
285
- * "value": "e"
286
- * }
287
- * {
288
- * "key": "formData.person.lastName",
289
- * "comparator": "endswith",
290
- * "value": "e"
291
- * }
292
- * ]
293
- * }
294
- * ]
295
- * }
274
+ * const filterConfiguration = {
275
+ * operator: 'and',
276
+ * filters: [
277
+ * {
278
+ * operator: 'or',
279
+ * filters: [
280
+ * { key: 'formData.person.firstName', comparator: 'startswith', value: 'c' },
281
+ * { key: 'formData.person.lastName', comparator: 'endswith' }, // no value, so not active
282
+ * { key: 'formData.person.lastName', comparator: 'endswith', value: 'e' },
283
+ * ],
284
+ * },
285
+ * ],
286
+ * };
296
287
  *
297
- * const activeFilters = sdk.filter.activeFilterKeys(filterConfiguration);
298
- * // Returns ['formData.person.firstName', 'formData.person.lastName']
299
- *
288
+ * sdk.filter.activeFilterKeys(filterConfiguration);
289
+ * // ['formData.person.firstName', 'formData.person.lastName']
300
290
  */
301
291
  service.activeFilterKeys = function(filterConfiguration, options) {
302
292
  options = options || {}
@@ -323,40 +313,27 @@ service.activeFilterKeys = function(filterConfiguration, options) {
323
313
 
324
314
 
325
315
  /**
326
- *
327
- * @description Returns an array of all comparators that are being actively filtered
316
+ * @description Lists the distinct comparators used by active conditions.
328
317
  * @alias filter.activeFilterComparators
329
- * @param {Object} filterConfiguration A filter configuration object
318
+ * @param {Object} filterConfiguration A filter group { operator, filters }.
319
+ * @returns {Array<String>} The comparator names.
330
320
  * @example
331
- *
332
- * const filterConfiguration = {
333
- * "operator": "and",
334
- * "filters": [
335
- * {
336
- * "operator": "and",
337
- * "filters": [
338
- * {
339
- * "key": "formData.person.firstName",
340
- * "comparator": "startswith",
341
- * "value": "c"
342
- * },
343
- * {
344
- * "key": "invalid no comparator",
345
- * "value": "e"
346
- * }
347
- * {
348
- * "key": "formData.person.lastName",
349
- * "comparator": "endswith",
350
- * "value": "e"
351
- * }
352
- * ]
353
- * }
354
- * ]
355
- * }
321
+ * const filterConfiguration = {
322
+ * operator: 'and',
323
+ * filters: [
324
+ * {
325
+ * operator: 'or',
326
+ * filters: [
327
+ * { key: 'formData.person.firstName', comparator: 'startswith', value: 'c' },
328
+ * { key: 'formData.person.lastName', comparator: 'endswith' }, // no value, so not active
329
+ * { key: 'formData.person.lastName', comparator: 'endswith', value: 'e' },
330
+ * ],
331
+ * },
332
+ * ],
333
+ * };
356
334
  *
357
- * const activeFilters = sdk.filter.activeFilterKeys(filterConfiguration);
358
- * // Returns ['startswith', 'endswith']
359
- *
335
+ * sdk.filter.activeFilterComparators(filterConfiguration);
336
+ * // ['startswith', 'endswith']
360
337
  */
361
338
  service.activeFilterComparators = function(filterConfiguration) {
362
339
  var comparators = service.activeFilters(filterConfiguration)
@@ -373,40 +350,27 @@ service.activeFilterComparators = function(filterConfiguration) {
373
350
  }
374
351
 
375
352
  /**
376
- *
377
- * @description Returns an array of all comparators that are being actively filtered
353
+ * @description Lists the distinct operators ('and', 'or', 'nor') of the groups that contain at least one active condition.
378
354
  * @alias filter.activeFilterOperators
379
- * @param {Object} filterConfiguration A filter configuration object
355
+ * @param {Object} filterConfiguration A filter group { operator, filters }.
356
+ * @returns {Array<String>} The operators.
380
357
  * @example
381
- *
382
- * const filterConfiguration = {
383
- * "operator": "and",
384
- * "filters": [
385
- * {
386
- * "operator": "nor",
387
- * "filters": [
388
- * {
389
- * "key": "formData.person.firstName",
390
- * "comparator": "startswith",
391
- * "value": "c"
392
- * },
393
- * {
394
- * "key": "invalid no comparator",
395
- * "value": "e"
396
- * }
397
- * {
398
- * "key": "formData.person.lastName",
399
- * "comparator": "endswith",
400
- * "value": "e"
401
- * }
402
- * ]
403
- * }
404
- * ]
405
- * }
358
+ * const filterConfiguration = {
359
+ * operator: 'and',
360
+ * filters: [
361
+ * {
362
+ * operator: 'or',
363
+ * filters: [
364
+ * { key: 'formData.person.firstName', comparator: 'startswith', value: 'c' },
365
+ * { key: 'formData.person.lastName', comparator: 'endswith' }, // no value, so not active
366
+ * { key: 'formData.person.lastName', comparator: 'endswith', value: 'e' },
367
+ * ],
368
+ * },
369
+ * ],
370
+ * };
406
371
  *
407
- * const activeFilterOperators = sdk.filter.activeFilterOperators(filterConfiguration);
408
- * // Returns ['and', 'nor']
409
- *
372
+ * sdk.filter.activeFilterOperators(filterConfiguration);
373
+ * // ['and', 'or']
410
374
  */
411
375
  service.activeFilterOperators = function(filterConfiguration) {
412
376
  var operators = service.activeFilters(filterConfiguration)
@@ -423,40 +387,29 @@ service.activeFilterOperators = function(filterConfiguration) {
423
387
 
424
388
 
425
389
  /**
426
- *
427
- * @description Returns an array of all values that are being actively used by a filter
390
+ * @description Lists the values used by active conditions (value, value2 and every entry of values). Object values are
391
+ * reduced to their _id, id, title or name. Empty values are dropped; duplicates are kept.
428
392
  * @alias filter.activeFilterValues
429
- * @param {Object} filterConfiguration A filter configuration object
393
+ * @param {Object} filterConfiguration A filter group { operator, filters }.
394
+ * @param {Object} [options] Not used; accepted for compatibility.
395
+ * @returns {Array<*>} The values.
430
396
  * @example
431
- *
432
- * const filterConfiguration = {
433
- * "operator": "and",
434
- * "filters": [
435
- * {
436
- * "operator": "nor",
437
- * "filters": [
438
- * {
439
- * "key": "formData.person.firstName",
440
- * "comparator": "startswith",
441
- * "value": "c"
442
- * },
443
- * {
444
- * "key": "invalid no comparator",
445
- * "value": "e"
446
- * }
447
- * {
448
- * "key": "formData.person.lastName",
449
- * "comparator": "endswith",
450
- * "value": "a"
451
- * }
452
- * ]
453
- * }
454
- * ]
455
- * }
397
+ * const filterConfiguration = {
398
+ * operator: 'and',
399
+ * filters: [
400
+ * {
401
+ * operator: 'or',
402
+ * filters: [
403
+ * { key: 'formData.person.firstName', comparator: 'startswith', value: 'c' },
404
+ * { key: 'formData.person.lastName', comparator: 'endswith' }, // no value, so not active
405
+ * { key: 'formData.person.lastName', comparator: 'endswith', value: 'e' },
406
+ * ],
407
+ * },
408
+ * ],
409
+ * };
456
410
  *
457
- * const activeFilterValues = sdk.filter.activeFilterValues(filterConfiguration);
458
- * // Returns ['c', 'a']
459
- *
411
+ * sdk.filter.activeFilterValues(filterConfiguration);
412
+ * // ['c', 'e']
460
413
  */
461
414
  service.activeFilterValues = function(filterConfiguration, options) {
462
415
 
@@ -490,40 +443,28 @@ service.activeFilterValues = function(filterConfiguration, options) {
490
443
 
491
444
 
492
445
  /**
493
- *
494
- * @description Returns an string representation of a filter configuration.
495
- * Often used to check if a user has changed the filter in any way, the string only takes into consideration active valid filters
446
+ * @description Turns the active parts of a filter configuration into a string, so you can tell whether the filter
447
+ * that would actually be applied has changed (incomplete conditions don't affect it).
496
448
  * @alias filter.filterChangeString
497
- * @param {Object} filterConfiguration A filter configuration object
449
+ * @param {Object} config A filter group { operator, filters }.
450
+ * @returns {String} A JSON string of the active entries.
498
451
  * @example
499
- *
500
- * const filterConfiguration = {
501
- * "operator": "and",
502
- * "filters": [
503
- * {
504
- * "operator": "nor",
505
- * "filters": [
506
- * {
507
- * "key": "formData.person.firstName",
508
- * "comparator": "startswith",
509
- * "value": "c"
510
- * },
511
- * {
512
- * "key": "invalid no comparator",
513
- * "value": "e"
514
- * }
515
- * {
516
- * "key": "formData.person.lastName",
517
- * "comparator": "endswith",
518
- * "value": "a"
519
- * }
520
- * ]
521
- * }
522
- * ]
523
- * }
452
+ * const filterConfiguration = {
453
+ * operator: 'and',
454
+ * filters: [
455
+ * {
456
+ * operator: 'or',
457
+ * filters: [
458
+ * { key: 'formData.person.firstName', comparator: 'startswith', value: 'c' },
459
+ * { key: 'formData.person.lastName', comparator: 'endswith' }, // no value, so not active
460
+ * { key: 'formData.person.lastName', comparator: 'endswith', value: 'e' },
461
+ * ],
462
+ * },
463
+ * ],
464
+ * };
524
465
  *
525
- * // Watch this string for changes
526
- * const changeString = sdk.filter.filterChangeString(filterConfiguration);
466
+ * // Re-run the list only when the effective filter changes
467
+ * const changeString = sdk.filter.filterChangeString(filterConfiguration);
527
468
  */
528
469
  service.filterChangeString = function(config) {
529
470
 
@@ -1,8 +1,9 @@
1
1
  ///////////////////////////////////////////////////
2
2
 
3
3
  /**
4
- * Creates a new QikGeo instance.
5
- * This module provides a number of geolocation features and functions
4
+ * Location helpers: the device's position (browser geolocation, which asks the user's permission), the
5
+ * distance between two points, and the organisation's default timezone and country, plus lists of
6
+ * countries and timezones.
6
7
  * @alias geo
7
8
  * @constructor
8
9
  * @hideconstructor
@@ -31,18 +32,18 @@ export default function(qik) {
31
32
  }
32
33
 
33
34
  /**
34
- *
35
- * Get the distance between two geo location points
35
+ * Calculates the straight-line (great-circle) distance between two points, in metres.
36
36
  * @alias geo.distance
37
- * @param {Object} positionOne The first position
38
- * @param {Number} positionOne.lat The first positions latitude
39
- * @param {Number} positionOne.lon The first positions longitude
40
- * @param {Object} positionTwo The second position
41
- * @param {Number} positionTwo.lat The second positions latitude
42
- * @param {Number} positionTwo.lon The second positions longitude
37
+ * @param {Object} positionOne The first point.
38
+ * @param {Number} positionOne.lat Latitude in degrees.
39
+ * @param {Number} positionOne.lon Longitude in degrees.
40
+ * @param {Object} positionTwo The second point.
41
+ * @param {Number} positionTwo.lat Latitude in degrees.
42
+ * @param {Number} positionTwo.lon Longitude in degrees.
43
+ * @returns {Number} The distance in metres.
43
44
  * @example
44
- * const meters = await sdk.geo.distance({lat, lon}, {lat, lon})
45
- * // Returns 634
45
+ * const metres = sdk.geo.distance({ lat: -37.8136, lon: 144.9631 }, { lat: -33.8688, lon: 151.2093 });
46
+ * // about 713000
46
47
  */
47
48
  service.distance = function({ lat: lat1, lon: lon1 }, { lat: lat2, lon: lon2 }) {
48
49
  function deg2rad(deg) {
@@ -62,13 +63,12 @@ export default function(qik) {
62
63
  }
63
64
 
64
65
  /**
65
- *
66
- * Start watching the user's geolocation
66
+ * Asks the browser for the device's current position once (the user may be prompted for permission).
67
67
  * @alias geo.getPosition
68
- * @param {Function} callback A function that will be called with updated position data
68
+ * @returns {Promise<Object>} A GeolocationPosition: { coords: { latitude, longitude, accuracy, altitude, ... }, timestamp }. Rejects with { message: 'Geolocation is not enabled' } outside a browser or without geolocation, or with the browser's error if the user refuses.
69
69
  * @example
70
- * const position = await sdk.geo.getPosition()
71
- * // Returns {coords:{latitude, longitude, altitude, accuracy, ...}}
70
+ * const { coords } = await sdk.geo.getPosition();
71
+ * console.log(coords.latitude, coords.longitude);
72
72
  */
73
73
  service.getPosition = async function() {
74
74
 
@@ -83,13 +83,14 @@ export default function(qik) {
83
83
  }
84
84
 
85
85
  /**
86
- *
87
- * Start watching the user's geolocation
86
+ * Starts watching the device's position; the callback runs every time it changes.
88
87
  * @alias geo.watchPosition
89
- * @param {Function} callback A function that will be called with updated position data
88
+ * @param {Function} positionDataReceived Called with a GeolocationPosition ({ coords, timestamp }) on every update.
89
+ * @returns {Promise<Object>} { id }: the watch id, needed to stop watching. Rejects with { message: 'Geolocation is not enabled' } when geolocation isn't available.
90
90
  * @example
91
- * const result = await sdk.geo.watchPosition(callback)
92
- * // Returns {id:<watcherID>}
91
+ * const { id } = await sdk.geo.watchPosition((position) => {
92
+ * console.log(position.coords.latitude, position.coords.longitude);
93
+ * });
93
94
  */
94
95
  service.watchPosition = async function(positionDataReceived) {
95
96
 
@@ -107,13 +108,14 @@ export default function(qik) {
107
108
  }
108
109
 
109
110
  /**
110
- *
111
- * Start watching the user's geolocation
111
+ * Stops a watch started with watchPosition.
112
112
  * @alias geo.stopWatchPosition
113
- * @param {String} watchID the id of the watcher to stop
113
+ * @param {Number} watchID The id returned by watchPosition (its `id`).
114
+ * @returns {Promise<Boolean>} true. Rejects with { message: 'Geolocation is not enabled' } when geolocation isn't available.
114
115
  * @example
115
- * const result = await sdk.geo.stopWatchPosition(watchID)
116
- * // Returns true after the watcher has been stopped
116
+ * const { id } = await sdk.geo.watchPosition(onPosition);
117
+ * // later
118
+ * await sdk.geo.stopWatchPosition(id);
117
119
  */
118
120
  service.stopWatchPosition = async function(watchID) {
119
121
  if (!isEnabled()) {
@@ -121,7 +123,7 @@ export default function(qik) {
121
123
  }
122
124
 
123
125
  console.log('qik:geo: watch stopped');
124
- navigator.geolocation.clearWatch(this.watchID);
126
+ navigator.geolocation.clearWatch(watchID);
125
127
  return true;
126
128
  }
127
129
 
@@ -129,18 +131,17 @@ export default function(qik) {
129
131
  ///////////////////////////////////////////////////
130
132
 
131
133
  /**
132
- *
133
- * Retrieves the default timezone for the current user/application
134
- * will attempt to retrieve an accurate timezone either from the user's organisation or available services within the browser
134
+ * Returns the timezone to use by default: the signed-in user's organisation timezone, otherwise the
135
+ * browser's (also used when signed out).
135
136
  * @alias geo.getDefaultTimezone
137
+ * @returns {String|undefined} An IANA timezone name such as 'Australia/Melbourne', or undefined outside a browser with no organisation timezone.
136
138
  * @example
137
- * const result = sdk.geo.getDefaultTimezone()
138
- * // Would return something similar to:
139
- * // Australia/Melbourne
139
+ * const timezone = sdk.geo.getDefaultTimezone(); // 'Australia/Melbourne'
140
140
  */
141
141
  service.getDefaultTimezone = function() {
142
142
 
143
- const {session:currentUser} = qik.auth.getCurrentUser();
143
+ // getCurrentUser() is null when signed out
144
+ const currentUser = qik.auth.getCurrentUser()?.session;
144
145
 
145
146
  if(currentUser) {
146
147
  const defaultUserTimezone = currentUser?.organisation?.timezone;
@@ -161,6 +162,12 @@ export default function(qik) {
161
162
 
162
163
  ///////////////////////////////////////////////////
163
164
 
165
+ /**
166
+ * Country names keyed by two-letter ISO code, e.g. countryLookup.AU === 'Australia'.
167
+ * @alias geo.countryLookup
168
+ * @example
169
+ * const name = sdk.geo.countryLookup['NZ']; // 'New Zealand'
170
+ */
164
171
  service.countryLookup = {
165
172
  "AC": "Ascension Island",
166
173
  "AD": "Andorra",
@@ -424,8 +431,20 @@ export default function(qik) {
424
431
  "ZW": "Zimbabwe"
425
432
  }
426
433
 
434
+ /**
435
+ * Every country name from countryLookup, as an array.
436
+ * @alias geo.countryNames
437
+ * @example
438
+ * const names = sdk.geo.countryNames; // ['Ascension Island', 'Andorra', ...]
439
+ */
427
440
  service.countryNames = Object.values(service.countryLookup);
428
441
 
442
+ /**
443
+ * Every IANA timezone name, e.g. for a timezone picker.
444
+ * @alias geo.timezones
445
+ * @example
446
+ * const options = sdk.geo.timezones.map((zone) => ({ title: zone, value: zone }));
447
+ */
429
448
  service.timezones = [
430
449
  "Africa/Abidjan",
431
450
  "Africa/Accra",
@@ -864,17 +883,15 @@ export default function(qik) {
864
883
  ]
865
884
 
866
885
  /**
867
- *
868
- * Retrieves the default country for the current user/application
869
- * will attempt to retrieve the country from the user or application's organisation or from services available within the browser
886
+ * Returns the signed-in user's organisation country.
870
887
  * @alias geo.getDefaultCountry
888
+ * @returns {Object|undefined} { code, name }, e.g. { code: 'AU', name: 'Australia' }, or undefined when signed out or the organisation has no country set.
871
889
  * @example
872
- * const result = sdk.geo.getDefaultCountry()
873
- * // Would return something similar to:
874
- * // { code:'AU', name:'Australia' }
890
+ * const country = sdk.geo.getDefaultCountry(); // { code: 'AU', name: 'Australia' }
875
891
  */
876
892
  service.getDefaultCountry = function() {
877
- const {session:currentUser} = qik.auth.getCurrentUser();
893
+ // getCurrentUser() is null when signed out
894
+ const currentUser = qik.auth.getCurrentUser()?.session;
878
895
 
879
896
  if(currentUser) {
880
897
  const code = currentUser?.organisation?.countryCode;