@stacksjs/mobile 0.75.22 → 0.75.24
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.
- package/README.md +51 -0
- package/dist/gestures.d.ts +38 -0
- package/dist/index.d.ts +140 -4
- package/dist/index.js +1 -1
- package/dist/navigation.d.ts +29 -0
- package/dist/types.d.ts +48 -3
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -55,3 +55,54 @@ The `health` facade reads authorized Apple Health or Android Health Connect
|
|
|
55
55
|
metrics and writes completed workouts using one payload. `watchConnectivity`
|
|
56
56
|
keeps companion controls and recording state synchronized without exposing
|
|
57
57
|
platform bridge globals to application code.
|
|
58
|
+
|
|
59
|
+
### Reading Apple Health
|
|
60
|
+
|
|
61
|
+
`health.getWorkouts()` lists the workouts in Apple Health — the watch's, and
|
|
62
|
+
every app's that writes there — newest first, with duration, distance, energy,
|
|
63
|
+
heart rate and elevation, keyed on HealthKit's UUID so an import can be run
|
|
64
|
+
again without duplicating. `health.getDailyStatistics(type)` returns one value
|
|
65
|
+
per local day: a sum for `steps`, `activeEnergy` and `distance`, an average for
|
|
66
|
+
`heartRate`, `restingHeartRate` and `heartRateVariability`, the latest
|
|
67
|
+
`bodyMass`, and hours asleep for `sleep`, counted on the day you woke.
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { health } from '@stacksjs/mobile'
|
|
71
|
+
|
|
72
|
+
await health.requestAuthorization(['workouts', 'heartRate', 'sleep', 'heartRateVariability'])
|
|
73
|
+
const workouts = await health.getWorkouts({ startDate: Date.now() - 30 * 86400000 })
|
|
74
|
+
const hrv = await health.getDailyStatistics('heartRateVariability', { startDate: Date.now() - 30 * 86400000 })
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Both are iOS only for now; on Android they reject as unavailable.
|
|
78
|
+
|
|
79
|
+
## Before the bridge arrives
|
|
80
|
+
|
|
81
|
+
Craft installs `window.craft` once the page has finished loading, so
|
|
82
|
+
`isNativeMobile()` answers `false` during setup on a phone. A decision made
|
|
83
|
+
then — hide the website's header, show the app's tab bar — needs the answer
|
|
84
|
+
when it is right:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import { hasNativeMobileHost, whenNativeMobile } from '@stacksjs/mobile'
|
|
88
|
+
|
|
89
|
+
hasNativeMobileHost() // true inside the phone shell, even before the bridge
|
|
90
|
+
const native = await whenNativeMobile() // resolves once it knows, false in a browser
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Page gestures and navigation
|
|
94
|
+
|
|
95
|
+
The phone-native behaviour a screen expects, kept out of STX templates:
|
|
96
|
+
|
|
97
|
+
- `observePullToRefresh({ onRefresh, onPull, threshold })` pulls the page down
|
|
98
|
+
from its top. On iOS it rides the system's rubber band; elsewhere the finger's
|
|
99
|
+
travel drives it. Crossing the threshold taps the haptic engine once.
|
|
100
|
+
- `observePageScroll(offset => …)` reports the scroll offset at most once a
|
|
101
|
+
frame, for a bar that collapses as the page scrolls.
|
|
102
|
+
- `goBack()` goes back through history when the app pushed the current screen,
|
|
103
|
+
and answers `false` otherwise so a back button follows its own link and never
|
|
104
|
+
leaves the app.
|
|
105
|
+
|
|
106
|
+
The framework's `NativeNavBar`, `NativePullToRefresh`, `NativeSegmentedControl`
|
|
107
|
+
and `NativeSheet` components are built on these.
|
|
108
|
+
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The page's vertical scroll offset. Negative while iOS rubber-bands past the
|
|
3
|
+
* top, which is what pull-to-refresh reads.
|
|
4
|
+
*/
|
|
5
|
+
export declare function pageScrollTop(host?: ScrollHost | undefined): number;
|
|
6
|
+
/**
|
|
7
|
+
* Calls `callback` with the page's scroll offset now and on every scroll, at
|
|
8
|
+
* most once a frame. Returns the unsubscribe.
|
|
9
|
+
*/
|
|
10
|
+
export declare function observePageScroll(callback: (offset: number) => void, host?: ScrollHost | undefined): () => void;
|
|
11
|
+
/**
|
|
12
|
+
* How far a pull has travelled.
|
|
13
|
+
*
|
|
14
|
+
* iOS rubber-bands the page past its top, so the scroll offset itself goes
|
|
15
|
+
* negative and carries the system's own resistance curve. Android and desktop
|
|
16
|
+
* browsers stop at zero, so there the finger's travel counts, halved to feel
|
|
17
|
+
* like the same resistance.
|
|
18
|
+
*/
|
|
19
|
+
export declare function pullDistance(scrollTop: number, fingerTravel: number): number;
|
|
20
|
+
/**
|
|
21
|
+
* Pull-to-refresh for the whole page.
|
|
22
|
+
*
|
|
23
|
+
* A pull starts only with the page at its top. Crossing the threshold gives a
|
|
24
|
+
* medium haptic tap, the moment a native list does; pulling back under 80% of
|
|
25
|
+
* it disarms. Letting go while armed runs `onRefresh`.
|
|
26
|
+
*/
|
|
27
|
+
export declare function observePullToRefresh(options: PullToRefreshOptions): () => void;
|
|
28
|
+
/** Where a page scrolls: the window, or a stand-in with the same shape. */
|
|
29
|
+
export declare interface ScrollHost extends EventTarget {
|
|
30
|
+
scrollY?: number
|
|
31
|
+
document?: { scrollingElement?: { scrollTop: number } | null }
|
|
32
|
+
}
|
|
33
|
+
export declare interface PullToRefreshOptions {
|
|
34
|
+
threshold?: number
|
|
35
|
+
onPull?: (distance: number, armed: boolean) => void
|
|
36
|
+
onRefresh: () => unknown | Promise<unknown>
|
|
37
|
+
host?: ScrollHost
|
|
38
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Bundled TypeScript declarations
|
|
3
|
-
* Generated from
|
|
3
|
+
* Generated from 4 source files
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
|
|
@@ -27,10 +27,101 @@ export declare const watchConnectivity: WatchConnectivityApi;
|
|
|
27
27
|
/** Reads the current host, including a bridge injected after this module loaded. */
|
|
28
28
|
export declare function getNativeMobileBridge(): CraftMobileBridge | null;
|
|
29
29
|
export declare function isNativeMobile(): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Whether this page runs inside Craft's phone shell, before the bridge exists.
|
|
32
|
+
*
|
|
33
|
+
* Craft installs `window.craft` once the page has finished loading — images
|
|
34
|
+
* and all — so `isNativeMobile()` answers `false` during setup on a phone, and
|
|
35
|
+
* a layout that decides then renders the website's chrome in the app. The
|
|
36
|
+
* transports are there from the start: Android's `CraftAndroid` interface, and
|
|
37
|
+
* on iOS the `craft` message handler. Craft's macOS windows carry that handler
|
|
38
|
+
* too, so on iOS an iPhone or iPad user agent is what makes it the phone app.
|
|
39
|
+
*/
|
|
40
|
+
export declare function hasNativeMobileHost(): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Resolves `true` inside the phone shell and `false` in a browser, at a moment
|
|
43
|
+
* when the answer is right.
|
|
44
|
+
*
|
|
45
|
+
* A browser answers at once. A phone answers at once too when its host is
|
|
46
|
+
* already recognisable, and otherwise once `craftReady` fires. The timeout is
|
|
47
|
+
* a floor for a host that never fires it: a caller waiting on this has to
|
|
48
|
+
* decide something to render anything.
|
|
49
|
+
*/
|
|
50
|
+
export declare function whenNativeMobile(timeoutMs?: number): Promise<boolean>;
|
|
30
51
|
export declare function onMobileReady(callback: (event: CraftReadyEvent) => void): () => void;
|
|
31
52
|
export declare function withNativeFeedback<T>(action: () => T | Promise<T>): Promise<T>;
|
|
32
53
|
export declare const mobile: MobileApi;
|
|
33
54
|
|
|
55
|
+
// From: src/gestures.ts
|
|
56
|
+
/** Where a page scrolls: the window, or a stand-in with the same shape. */
|
|
57
|
+
export declare interface ScrollHost extends EventTarget {
|
|
58
|
+
scrollY?: number
|
|
59
|
+
document?: { scrollingElement?: { scrollTop: number } | null }
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The page's vertical scroll offset. Negative while iOS rubber-bands past the
|
|
63
|
+
* top, which is what pull-to-refresh reads.
|
|
64
|
+
*/
|
|
65
|
+
export declare function pageScrollTop(host?: ScrollHost | undefined): number;
|
|
66
|
+
/**
|
|
67
|
+
* Calls `callback` with the page's scroll offset now and on every scroll, at
|
|
68
|
+
* most once a frame. Returns the unsubscribe.
|
|
69
|
+
*/
|
|
70
|
+
export declare function observePageScroll(callback: (offset: number) => void, host?: ScrollHost | undefined): () => void;
|
|
71
|
+
export declare interface PullToRefreshOptions {
|
|
72
|
+
threshold?: number
|
|
73
|
+
onPull?: (distance: number, armed: boolean) => void
|
|
74
|
+
onRefresh: () => unknown | Promise<unknown>
|
|
75
|
+
host?: ScrollHost
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* How far a pull has travelled.
|
|
79
|
+
*
|
|
80
|
+
* iOS rubber-bands the page past its top, so the scroll offset itself goes
|
|
81
|
+
* negative and carries the system's own resistance curve. Android and desktop
|
|
82
|
+
* browsers stop at zero, so there the finger's travel counts, halved to feel
|
|
83
|
+
* like the same resistance.
|
|
84
|
+
*/
|
|
85
|
+
export declare function pullDistance(scrollTop: number, fingerTravel: number): number;
|
|
86
|
+
/**
|
|
87
|
+
* Pull-to-refresh for the whole page.
|
|
88
|
+
*
|
|
89
|
+
* A pull starts only with the page at its top. Crossing the threshold gives a
|
|
90
|
+
* medium haptic tap, the moment a native list does; pulling back under 80% of
|
|
91
|
+
* it disarms. Letting go while armed runs `onRefresh`.
|
|
92
|
+
*/
|
|
93
|
+
export declare function observePullToRefresh(options: PullToRefreshOptions): () => void;
|
|
94
|
+
|
|
95
|
+
// From: src/navigation.ts
|
|
96
|
+
/** The part of the window back navigation reads. */
|
|
97
|
+
export declare interface HistoryHost {
|
|
98
|
+
history?: { state: unknown, back: () => void }
|
|
99
|
+
}
|
|
100
|
+
/** Whether going back stays inside this app. */
|
|
101
|
+
export declare function canGoBack(host?: HistoryHost | undefined): boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Goes back through history when that stays inside the app, and says whether
|
|
104
|
+
* it did. When it answers `false` the caller follows its own link.
|
|
105
|
+
*/
|
|
106
|
+
export declare function goBack(host?: HistoryHost | undefined): boolean;
|
|
107
|
+
/**
|
|
108
|
+
* The in-app path a deep link opens, or null when it is not this app's.
|
|
109
|
+
*
|
|
110
|
+
* A custom scheme carries the path after it — `myapp://m/workout/5` and
|
|
111
|
+
* `myapp:///m/workout/5` both open `/m/workout/5` — and a universal link on
|
|
112
|
+
* this app's origin opens its own path. A link to anywhere else is not
|
|
113
|
+
* followed from inside the app.
|
|
114
|
+
*/
|
|
115
|
+
export declare function deepLinkPath(link: string, origin?: string | undefined): string | null;
|
|
116
|
+
/**
|
|
117
|
+
* Opens every deep link the app receives with `go`: the one it was launched
|
|
118
|
+
* with, once, and each one after. Returns the unsubscribe.
|
|
119
|
+
*
|
|
120
|
+
* The launch link is remembered for the session, so a full page load inside
|
|
121
|
+
* the app does not open it again.
|
|
122
|
+
*/
|
|
123
|
+
export declare function followDeepLinks(go: (path: string) => void, links: DeepLinksApi): () => void;
|
|
124
|
+
|
|
34
125
|
// From: src/types.ts
|
|
35
126
|
/** Enabled native features, not the hardware report from device.getCapabilities(). */
|
|
36
127
|
export declare interface CraftMobileCapabilities {
|
|
@@ -107,6 +198,7 @@ export declare interface MobileApi {
|
|
|
107
198
|
liveActivities: LiveActivitiesApi
|
|
108
199
|
watchConnectivity: WatchConnectivityApi
|
|
109
200
|
isNativeMobile: () => boolean
|
|
201
|
+
whenNative: (timeoutMs?: number) => Promise<boolean>
|
|
110
202
|
onReady: (callback: (event: CraftReadyEvent) => void) => () => void
|
|
111
203
|
withFeedback: <T>(action: () => T | Promise<T>) => Promise<T>
|
|
112
204
|
}
|
|
@@ -274,7 +366,7 @@ export declare interface KeepAwakeApi {
|
|
|
274
366
|
}
|
|
275
367
|
export declare interface DeepLinksApi {
|
|
276
368
|
getInitialURL: () => Promise<string | null>
|
|
277
|
-
onLink: (callback: (url: string) => void) => () => void
|
|
369
|
+
onLink: (callback: (url: string, link?: { initial: boolean }) => void) => () => void
|
|
278
370
|
}
|
|
279
371
|
export declare interface NetworkStatus {
|
|
280
372
|
type: 'wifi' | 'cellular' | 'ethernet' | 'none' | 'unknown'
|
|
@@ -292,7 +384,17 @@ export declare interface PushNotificationsApi {
|
|
|
292
384
|
onToken: (callback: (token: string) => void) => () => void
|
|
293
385
|
onNotification: (callback: (data: Record<string, unknown>) => void) => () => void
|
|
294
386
|
}
|
|
295
|
-
export type HealthDataType =
|
|
387
|
+
export type HealthDataType = | 'steps'
|
|
388
|
+
| 'heartRate'
|
|
389
|
+
| 'activeEnergy'
|
|
390
|
+
| 'distance'
|
|
391
|
+
| 'workouts'
|
|
392
|
+
| 'restingHeartRate'
|
|
393
|
+
| 'heartRateVariability'
|
|
394
|
+
| 'bodyMass'
|
|
395
|
+
| 'sleep';
|
|
396
|
+
/** The types with one value per day. `sleep` is hours asleep, counted on the day you woke. */
|
|
397
|
+
export type HealthDailyType = Exclude<HealthDataType, 'workouts'>;
|
|
296
398
|
export declare interface HealthDataOptions {
|
|
297
399
|
startDate?: number
|
|
298
400
|
endDate?: number
|
|
@@ -321,10 +423,44 @@ export declare interface HealthWorkout {
|
|
|
321
423
|
export declare interface HealthWorkoutResult {
|
|
322
424
|
id: string
|
|
323
425
|
}
|
|
426
|
+
export declare interface HealthWorkoutQuery {
|
|
427
|
+
startDate?: number
|
|
428
|
+
endDate?: number
|
|
429
|
+
limit?: number
|
|
430
|
+
}
|
|
431
|
+
/** The activity of a workout read from Apple Health, named independently of the SDK. */
|
|
432
|
+
export type HealthWorkoutActivity = | 'running' | 'cycling' | 'walking' | 'hiking' | 'swimming' | 'rowing' | 'elliptical'
|
|
433
|
+
| 'stairClimbing' | 'yoga' | 'pilates' | 'strength' | 'hiit' | 'crossTraining'
|
|
434
|
+
| 'crossCountrySkiing' | 'skiing' | 'paddling' | 'climbing' | 'dance' | 'mobility' | 'other';
|
|
435
|
+
/** A workout recorded by the watch, or by any app that writes to Apple Health. */
|
|
436
|
+
export declare interface HealthWorkoutSample {
|
|
437
|
+
id: string
|
|
438
|
+
type: HealthWorkoutActivity
|
|
439
|
+
startDate: number
|
|
440
|
+
endDate: number
|
|
441
|
+
durationSeconds: number
|
|
442
|
+
sourceName: string
|
|
443
|
+
indoor: boolean
|
|
444
|
+
distanceMeters?: number
|
|
445
|
+
elevationGainMeters?: number
|
|
446
|
+
activeEnergyCalories?: number
|
|
447
|
+
averageHeartRate?: number
|
|
448
|
+
maxHeartRate?: number
|
|
449
|
+
}
|
|
450
|
+
export declare interface HealthDailyValue {
|
|
451
|
+
date: string
|
|
452
|
+
value: number
|
|
453
|
+
unit: string
|
|
454
|
+
}
|
|
455
|
+
export declare interface HealthAuthorizationOptions {
|
|
456
|
+
write?: boolean
|
|
457
|
+
}
|
|
324
458
|
export declare interface HealthApi {
|
|
325
|
-
requestAuthorization: (types: HealthDataType[]) => Promise<boolean>
|
|
459
|
+
requestAuthorization: (types: HealthDataType[], options?: HealthAuthorizationOptions) => Promise<boolean>
|
|
326
460
|
getData: (type: HealthDataType, options?: HealthDataOptions) => Promise<HealthDataResult>
|
|
327
461
|
saveWorkout: (workout: HealthWorkout) => Promise<HealthWorkoutResult>
|
|
462
|
+
getWorkouts: (options?: HealthWorkoutQuery) => Promise<HealthWorkoutSample[]>
|
|
463
|
+
getDailyStatistics: (type: HealthDailyType, options?: HealthDataOptions) => Promise<HealthDailyValue[]>
|
|
328
464
|
}
|
|
329
465
|
export declare interface LiveActivityState {
|
|
330
466
|
status?: string
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{appReview as
|
|
1
|
+
import{appReview as S,biometrics as N,camera as T,deepLinks as x,device as M,haptics as D,health as B,keepAwake as Y,lifecycle as j,liveActivities as U,location as I,network as O,notifications as q,permissions as F,pushNotifications as K,secureStorage as W,share as z,watchConnectivity as G}from"craft-native/mobile";import{haptics as E}from"craft-native/mobile";function d(){if(typeof window>"u")return;return window}function u(t=d()){if(!t)return 0;if(typeof t.scrollY==="number")return t.scrollY;return t.document?.scrollingElement?.scrollTop??0}function he(t,e=d()){if(!e)return()=>{};let r=!1,n=typeof requestAnimationFrame==="function"?requestAnimationFrame:(o)=>setTimeout(o,16),i=()=>{if(r)return;r=!0,n(()=>{r=!1,t(u(e))})};return t(u(e)),e.addEventListener("scroll",i,{passive:!0}),()=>e.removeEventListener("scroll",i)}function H(t,e){let r=Math.max(0,-t),n=t<=0?Math.max(0,e)*0.5:0;return Math.round(Math.max(r,n))}function we(t){let e=t.host??d();if(!e)return()=>{};let r=t.threshold??72,n=null,i=!1,o=!1,c=(s)=>t.onPull?.(s,i),h=(s)=>{let a=s.touches?.[0];return a?a.clientY:null},w=(s)=>{if(o||u(e)>0)return;n=h(s),i=!1},A=(s)=>{if(n===null)return;let a=h(s);if(a===null)return;let p=H(u(e),a-n);if(!i&&p>=r)i=!0,Promise.resolve(E.impact("medium")).catch(()=>{});else if(i&&p<r*0.8)i=!1;c(p)},y=async()=>{if(n===null)return;if(n=null,!i){c(0);return}i=!1,o=!0,t.onPull?.(r,!1);try{await t.onRefresh()}finally{o=!1,c(0)}},b=()=>{if(n===null)return;n=null,i=!1,c(0)};return e.addEventListener("touchstart",w,{passive:!0}),e.addEventListener("touchmove",A,{passive:!0}),e.addEventListener("touchend",y),e.addEventListener("touchcancel",b),()=>{e.removeEventListener("touchstart",w),e.removeEventListener("touchmove",A),e.removeEventListener("touchend",y),e.removeEventListener("touchcancel",b)}}function k(){if(typeof window>"u")return;return window}function R(t=k()){let e=t?.history?.state;return e!==null&&e!==void 0}function ye(t=k()){if(!R(t))return!1;return t.history.back(),!0}function P(t,e=C()){let r;try{r=new URL(t)}catch{return null}let n=`${r.search}${r.hash}`;if(r.protocol==="http:"||r.protocol==="https:"){if(!e||r.origin!==e)return null;return`${r.pathname||"/"}${n}`}return`${`/${[r.host,r.pathname.replace(/^\/+/,"")].filter(Boolean).join("/")}`}${n}`}function C(){if(typeof window>"u")return;return window.location?.origin}var g="stacks-mobile-deep-link";function be(t,e){let r=(n)=>{let i=P(n);if(i)t(i)};return e.getInitialURL().then((n)=>{if(!n)return;try{if(sessionStorage.getItem(g)===n)return;sessionStorage.setItem(g,n)}catch{}r(n)}).catch(()=>{}),e.onLink((n,i)=>{if(i?.initial)return;r(n)})}var _=N,J=T,Q=M,f=D,V=j,X=I,Z=q,ee=F,te=W,ne=z,re=S;function L(t){if(typeof t==="string")return t.trim()||null;if(!t||typeof t!=="object")return null;let e=t.url;return typeof e==="string"&&e.trim()?e:null}var ie={async getInitialURL(){return L(await x.getInitialURL?.())},onLink(t){return x.onLink?.((e,r)=>{let n=L(e);if(n)t(n,{initial:r?.initial===!0})})??(()=>{})}},oe=Y,se=O,ae=K,ce=B,ue=U,fe=G;function v(){if(typeof window>"u")return;return window}function l(){let t=v()?.craft;if(!t||typeof t!=="object")return null;if(!("platform"in t)||t.platform!=="ios"&&t.platform!=="android")return null;let e="capabilities"in t?t.capabilities:void 0,r=e&&typeof e==="object"&&!Array.isArray(e)?Object.fromEntries(Object.entries(e).filter(([,n])=>typeof n==="boolean")):{};return{platform:t.platform,capabilities:r}}function m(){return l()!==null}function le(){if(typeof window>"u")return!1;let t=window;if(t.CraftAndroid)return!0;let e=typeof navigator>"u"?"":navigator.userAgent;return Boolean(t.webkit?.messageHandlers?.craft)&&/iPhone|iPad|iPod/.test(e)}function pe(t=2000){if(m()||le())return Promise.resolve(!0);let e=v(),r=e;if(!e||!(r?.craft||r?.webkit?.messageHandlers?.craft))return Promise.resolve(!1);return new Promise((n)=>{let i=()=>{clearTimeout(o),e.removeEventListener("craftReady",i),n(m())},o=setTimeout(i,t);e.addEventListener("craftReady",i,{once:!0})})}function de(t){let e=v();if(!e)return()=>{};if(l()){let n=!1;return queueMicrotask(()=>{if(!n)t(new Event("craftReady"))}),()=>{n=!0}}let r=(n)=>{if(!l())return;e.removeEventListener("craftReady",r),t(n)};return e.addEventListener("craftReady",r),()=>e.removeEventListener("craftReady",r)}async function me(t){await f.impact("light");try{let e=await t();return await f.notification("success"),e}catch(e){throw await f.notification("error"),e}}var xe={get nativeBridge(){return l()},biometrics:_,camera:J,device:Q,haptics:f,lifecycle:V,location:X,notifications:Z,permissions:ee,secureStorage:te,share:ne,appReview:re,deepLinks:ie,keepAwake:oe,network:se,pushNotifications:ae,health:ce,liveActivities:ue,watchConnectivity:fe,isNativeMobile:m,whenNative:pe,onReady:de,withFeedback:me};export{re as appReview,_ as biometrics,J as camera,R as canGoBack,P as deepLinkPath,ie as deepLinks,Q as device,be as followDeepLinks,l as getNativeMobileBridge,ye as goBack,f as haptics,le as hasNativeMobileHost,ce as health,m as isNativeMobile,oe as keepAwake,V as lifecycle,ue as liveActivities,X as location,xe as mobile,se as network,L as normalizeDeepLinkURL,Z as notifications,he as observePageScroll,we as observePullToRefresh,de as onMobileReady,u as pageScrollTop,ee as permissions,H as pullDistance,ae as pushNotifications,te as secureStorage,ne as share,fe as watchConnectivity,pe as whenNativeMobile,me as withNativeFeedback};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { DeepLinksApi } from './types';
|
|
2
|
+
/** Whether going back stays inside this app. */
|
|
3
|
+
export declare function canGoBack(host?: HistoryHost | undefined): boolean;
|
|
4
|
+
/**
|
|
5
|
+
* Goes back through history when that stays inside the app, and says whether
|
|
6
|
+
* it did. When it answers `false` the caller follows its own link.
|
|
7
|
+
*/
|
|
8
|
+
export declare function goBack(host?: HistoryHost | undefined): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* The in-app path a deep link opens, or null when it is not this app's.
|
|
11
|
+
*
|
|
12
|
+
* A custom scheme carries the path after it — `myapp://m/workout/5` and
|
|
13
|
+
* `myapp:///m/workout/5` both open `/m/workout/5` — and a universal link on
|
|
14
|
+
* this app's origin opens its own path. A link to anywhere else is not
|
|
15
|
+
* followed from inside the app.
|
|
16
|
+
*/
|
|
17
|
+
export declare function deepLinkPath(link: string, origin?: string | undefined): string | null;
|
|
18
|
+
/**
|
|
19
|
+
* Opens every deep link the app receives with `go`: the one it was launched
|
|
20
|
+
* with, once, and each one after. Returns the unsubscribe.
|
|
21
|
+
*
|
|
22
|
+
* The launch link is remembered for the session, so a full page load inside
|
|
23
|
+
* the app does not open it again.
|
|
24
|
+
*/
|
|
25
|
+
export declare function followDeepLinks(go: (path: string) => void, links: DeepLinksApi): () => void;
|
|
26
|
+
/** The part of the window back navigation reads. */
|
|
27
|
+
export declare interface HistoryHost {
|
|
28
|
+
history?: { state: unknown, back: () => void }
|
|
29
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -73,6 +73,7 @@ export declare interface MobileApi {
|
|
|
73
73
|
liveActivities: LiveActivitiesApi
|
|
74
74
|
watchConnectivity: WatchConnectivityApi
|
|
75
75
|
isNativeMobile: () => boolean
|
|
76
|
+
whenNative: (timeoutMs?: number) => Promise<boolean>
|
|
76
77
|
onReady: (callback: (event: CraftReadyEvent) => void) => () => void
|
|
77
78
|
withFeedback: <T>(action: () => T | Promise<T>) => Promise<T>
|
|
78
79
|
}
|
|
@@ -223,7 +224,7 @@ export declare interface KeepAwakeApi {
|
|
|
223
224
|
}
|
|
224
225
|
export declare interface DeepLinksApi {
|
|
225
226
|
getInitialURL: () => Promise<string | null>
|
|
226
|
-
onLink: (callback: (url: string) => void) => () => void
|
|
227
|
+
onLink: (callback: (url: string, link?: { initial: boolean }) => void) => () => void
|
|
227
228
|
}
|
|
228
229
|
export declare interface NetworkStatus {
|
|
229
230
|
type: 'wifi' | 'cellular' | 'ethernet' | 'none' | 'unknown'
|
|
@@ -268,10 +269,40 @@ export declare interface HealthWorkout {
|
|
|
268
269
|
export declare interface HealthWorkoutResult {
|
|
269
270
|
id: string
|
|
270
271
|
}
|
|
272
|
+
export declare interface HealthWorkoutQuery {
|
|
273
|
+
startDate?: number
|
|
274
|
+
endDate?: number
|
|
275
|
+
limit?: number
|
|
276
|
+
}
|
|
277
|
+
/** A workout recorded by the watch, or by any app that writes to Apple Health. */
|
|
278
|
+
export declare interface HealthWorkoutSample {
|
|
279
|
+
id: string
|
|
280
|
+
type: HealthWorkoutActivity
|
|
281
|
+
startDate: number
|
|
282
|
+
endDate: number
|
|
283
|
+
durationSeconds: number
|
|
284
|
+
sourceName: string
|
|
285
|
+
indoor: boolean
|
|
286
|
+
distanceMeters?: number
|
|
287
|
+
elevationGainMeters?: number
|
|
288
|
+
activeEnergyCalories?: number
|
|
289
|
+
averageHeartRate?: number
|
|
290
|
+
maxHeartRate?: number
|
|
291
|
+
}
|
|
292
|
+
export declare interface HealthDailyValue {
|
|
293
|
+
date: string
|
|
294
|
+
value: number
|
|
295
|
+
unit: string
|
|
296
|
+
}
|
|
297
|
+
export declare interface HealthAuthorizationOptions {
|
|
298
|
+
write?: boolean
|
|
299
|
+
}
|
|
271
300
|
export declare interface HealthApi {
|
|
272
|
-
requestAuthorization: (types: HealthDataType[]) => Promise<boolean>
|
|
301
|
+
requestAuthorization: (types: HealthDataType[], options?: HealthAuthorizationOptions) => Promise<boolean>
|
|
273
302
|
getData: (type: HealthDataType, options?: HealthDataOptions) => Promise<HealthDataResult>
|
|
274
303
|
saveWorkout: (workout: HealthWorkout) => Promise<HealthWorkoutResult>
|
|
304
|
+
getWorkouts: (options?: HealthWorkoutQuery) => Promise<HealthWorkoutSample[]>
|
|
305
|
+
getDailyStatistics: (type: HealthDailyType, options?: HealthDataOptions) => Promise<HealthDailyValue[]>
|
|
275
306
|
}
|
|
276
307
|
export declare interface LiveActivityState {
|
|
277
308
|
status?: string
|
|
@@ -312,5 +343,19 @@ export type PermissionType = | 'camera'
|
|
|
312
343
|
export type PermissionStatus = 'granted' | 'denied' | 'undetermined' | 'restricted';
|
|
313
344
|
export type BiometricType = 'faceId' | 'touchId' | 'fingerprint' | 'face' | 'iris';
|
|
314
345
|
export type AppState = 'active' | 'inactive' | 'background';
|
|
315
|
-
export type HealthDataType =
|
|
346
|
+
export type HealthDataType = | 'steps'
|
|
347
|
+
| 'heartRate'
|
|
348
|
+
| 'activeEnergy'
|
|
349
|
+
| 'distance'
|
|
350
|
+
| 'workouts'
|
|
351
|
+
| 'restingHeartRate'
|
|
352
|
+
| 'heartRateVariability'
|
|
353
|
+
| 'bodyMass'
|
|
354
|
+
| 'sleep';
|
|
355
|
+
/** The types with one value per day. `sleep` is hours asleep, counted on the day you woke. */
|
|
356
|
+
export type HealthDailyType = Exclude<HealthDataType, 'workouts'>;
|
|
316
357
|
export type HealthWorkoutType = 'running' | 'walking' | 'hiking' | 'cycling';
|
|
358
|
+
/** The activity of a workout read from Apple Health, named independently of the SDK. */
|
|
359
|
+
export type HealthWorkoutActivity = | 'running' | 'cycling' | 'walking' | 'hiking' | 'swimming' | 'rowing' | 'elliptical'
|
|
360
|
+
| 'stairClimbing' | 'yoga' | 'pilates' | 'strength' | 'hiit' | 'crossTraining'
|
|
361
|
+
| 'crossCountrySkiing' | 'skiing' | 'paddling' | 'climbing' | 'dance' | 'mobility' | 'other';
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/mobile",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.75.
|
|
5
|
+
"version": "0.75.24",
|
|
6
6
|
"description": "Craft-powered native mobile runtime for Stacks and STX applications",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"license": "MIT",
|
|
@@ -32,10 +32,10 @@
|
|
|
32
32
|
"test": "bun test"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"craft-native": ">=0.0.
|
|
35
|
+
"craft-native": ">=0.0.100"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@stacksjs/build": "0.75.
|
|
38
|
+
"@stacksjs/build": "0.75.24",
|
|
39
39
|
"better-dx": "^0.2.24"
|
|
40
40
|
}
|
|
41
41
|
}
|