expo-live-activity 0.4.2 → 0.5.0-alpha1
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/Package.swift +16 -0
- package/README.md +60 -7
- package/build/index.d.ts +41 -5
- package/build/index.d.ts.map +1 -1
- package/build/index.js +57 -33
- package/build/index.js.map +1 -1
- package/ios/ExpoLiveActivityModule.swift +96 -8
- package/ios/LiveActivityAttributes.swift +10 -0
- package/ios-files/LiveActivityHelpers.swift +58 -0
- package/ios-files/LiveActivityMediumView.swift +127 -0
- package/ios-files/LiveActivitySmallView.swift +178 -0
- package/ios-files/LiveActivityView.swift +137 -108
- package/ios-files/LiveActivityWidget.swift +204 -10
- package/ios-files/ViewHelpers.swift +41 -0
- package/package.json +8 -4
- package/plugin/build/index.js +2 -0
- package/plugin/build/lib/getWidgetFiles.js +1 -1
- package/plugin/build/types.d.ts +1 -0
- package/plugin/build/withUnsupportedOS.d.ts +4 -0
- package/plugin/build/withUnsupportedOS.js +9 -0
- package/plugin/src/index.ts +3 -0
- package/plugin/src/lib/getWidgetFiles.ts +1 -1
- package/plugin/src/types.ts +1 -0
- package/plugin/src/withUnsupportedOS.ts +10 -0
- package/plugin/tsconfig.tsbuildinfo +1 -1
- package/src/index.ts +88 -33
- package/.prettierignore +0 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-live-activity",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0-alpha1",
|
|
4
4
|
"description": "A module for adding Live Activity to a React Native app for iOS.",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"build": "expo-module build",
|
|
9
9
|
"clean": "expo-module clean",
|
|
10
10
|
"clean:plugin": "rm -rf plugin/build plugin/tsconfig.tsbuildinfo",
|
|
11
|
-
"
|
|
11
|
+
"lint:fix": "expo-module eslint --fix && swiftformat --exclude \"**/node_modules/\" .",
|
|
12
12
|
"lint": "expo-module eslint",
|
|
13
13
|
"lint:libOnly": "expo-module eslint --ignore-pattern 'example/*'",
|
|
14
14
|
"test": "expo-module test",
|
|
@@ -16,7 +16,10 @@
|
|
|
16
16
|
"prepublishOnly": "expo-module prepublishOnly",
|
|
17
17
|
"expo-module": "expo-module",
|
|
18
18
|
"open:ios": "xed example/ios",
|
|
19
|
-
"typecheck": "tsc"
|
|
19
|
+
"typecheck": "tsc",
|
|
20
|
+
"generateTests": "node ./example/tests/scripts/generateFlows.js",
|
|
21
|
+
"runAllTests": "./example/tests/scripts/runAllTests.sh",
|
|
22
|
+
"generateTestReport": "node ./example/tests/scripts/generateReport.js"
|
|
20
23
|
},
|
|
21
24
|
"keywords": [
|
|
22
25
|
"react-native",
|
|
@@ -47,7 +50,8 @@
|
|
|
47
50
|
"expo": "~54.0.0",
|
|
48
51
|
"expo-module-scripts": "^5.0.3",
|
|
49
52
|
"prettier": "^3.6.2",
|
|
50
|
-
"react-native": "0.81.4"
|
|
53
|
+
"react-native": "0.81.4",
|
|
54
|
+
"pdf-lib": "^1.17.1"
|
|
51
55
|
},
|
|
52
56
|
"peerDependencies": {
|
|
53
57
|
"expo": "*",
|
package/plugin/build/index.js
CHANGED
|
@@ -7,6 +7,7 @@ const config_plugins_1 = require("expo/config-plugins");
|
|
|
7
7
|
const withConfig_1 = require("./withConfig");
|
|
8
8
|
const withPlist_1 = __importDefault(require("./withPlist"));
|
|
9
9
|
const withPushNotifications_1 = require("./withPushNotifications");
|
|
10
|
+
const withUnsupportedOS_1 = require("./withUnsupportedOS");
|
|
10
11
|
const withWidgetExtensionEntitlements_1 = require("./withWidgetExtensionEntitlements");
|
|
11
12
|
const withXcode_1 = require("./withXcode");
|
|
12
13
|
const withWidgetsAndLiveActivities = (config, props) => {
|
|
@@ -37,6 +38,7 @@ const withWidgetsAndLiveActivities = (config, props) => {
|
|
|
37
38
|
if (props?.enablePushNotifications) {
|
|
38
39
|
config = (0, withPushNotifications_1.withPushNotifications)(config);
|
|
39
40
|
}
|
|
41
|
+
config = (0, withUnsupportedOS_1.withUnsupportedOS)(config, { silentOnUnsupportedOS: props?.silentOnUnsupportedOS ?? false });
|
|
40
42
|
return config;
|
|
41
43
|
};
|
|
42
44
|
exports.default = withWidgetsAndLiveActivities;
|
|
@@ -102,7 +102,7 @@ function getWidgetFiles(targetPath) {
|
|
|
102
102
|
const imagesXcassetsTarget = path.join(targetPath, 'Assets.xcassets');
|
|
103
103
|
const files = fs.readdirSync(imageAssetsPath);
|
|
104
104
|
files.forEach((file) => {
|
|
105
|
-
if (path.extname(file).match(/\.(png|jpg|jpeg)$/)) {
|
|
105
|
+
if (path.extname(file).match(/\.(png|jpg|jpeg|svg)$/)) {
|
|
106
106
|
const source = path.join(imageAssetsPath, file);
|
|
107
107
|
const imageSetDir = path.join(imagesXcassetsTarget, `${path.basename(file, path.extname(file))}.imageset`);
|
|
108
108
|
// Create the .imageset directory if it doesn't exist
|
package/plugin/build/types.d.ts
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.withUnsupportedOS = void 0;
|
|
4
|
+
const config_plugins_1 = require("@expo/config-plugins");
|
|
5
|
+
const withUnsupportedOS = (config, { silentOnUnsupportedOS }) => (0, config_plugins_1.withInfoPlist)(config, (mod) => {
|
|
6
|
+
mod.modResults['ExpoLiveActivity_SilentOnUnsupportedOS'] = silentOnUnsupportedOS;
|
|
7
|
+
return mod;
|
|
8
|
+
});
|
|
9
|
+
exports.withUnsupportedOS = withUnsupportedOS;
|
package/plugin/src/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { LiveActivityConfigPlugin } from './types'
|
|
|
4
4
|
import { withConfig } from './withConfig'
|
|
5
5
|
import withPlist from './withPlist'
|
|
6
6
|
import { withPushNotifications } from './withPushNotifications'
|
|
7
|
+
import { withUnsupportedOS } from './withUnsupportedOS'
|
|
7
8
|
import { withWidgetExtensionEntitlements } from './withWidgetExtensionEntitlements'
|
|
8
9
|
import { withXcode } from './withXcode'
|
|
9
10
|
|
|
@@ -39,6 +40,8 @@ const withWidgetsAndLiveActivities: LiveActivityConfigPlugin = (config, props) =
|
|
|
39
40
|
config = withPushNotifications(config)
|
|
40
41
|
}
|
|
41
42
|
|
|
43
|
+
config = withUnsupportedOS(config, { silentOnUnsupportedOS: props?.silentOnUnsupportedOS ?? false })
|
|
44
|
+
|
|
42
45
|
return config
|
|
43
46
|
}
|
|
44
47
|
|
|
@@ -78,7 +78,7 @@ export function getWidgetFiles(targetPath: string) {
|
|
|
78
78
|
const files = fs.readdirSync(imageAssetsPath)
|
|
79
79
|
|
|
80
80
|
files.forEach((file) => {
|
|
81
|
-
if (path.extname(file).match(/\.(png|jpg|jpeg)$/)) {
|
|
81
|
+
if (path.extname(file).match(/\.(png|jpg|jpeg|svg)$/)) {
|
|
82
82
|
const source = path.join(imageAssetsPath, file)
|
|
83
83
|
const imageSetDir = path.join(imagesXcassetsTarget, `${path.basename(file, path.extname(file))}.imageset`)
|
|
84
84
|
|
package/plugin/src/types.ts
CHANGED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ConfigPlugin, withInfoPlist } from '@expo/config-plugins'
|
|
2
|
+
|
|
3
|
+
export const withUnsupportedOS: ConfigPlugin<{ silentOnUnsupportedOS: boolean }> = (
|
|
4
|
+
config,
|
|
5
|
+
{ silentOnUnsupportedOS }
|
|
6
|
+
) =>
|
|
7
|
+
withInfoPlist(config, (mod) => {
|
|
8
|
+
mod.modResults['ExpoLiveActivity_SilentOnUnsupportedOS'] = silentOnUnsupportedOS
|
|
9
|
+
return mod
|
|
10
|
+
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["./src/index.ts","./src/types.ts","./src/withConfig.ts","./src/withPlist.ts","./src/withPushNotifications.ts","./src/withWidgetExtensionEntitlements.ts","./src/withXcode.ts","./src/lib/getWidgetExtensionEntitlements.ts","./src/lib/getWidgetFiles.ts","./src/xcode/addBuildPhases.ts","./src/xcode/addPbxGroup.ts","./src/xcode/addProductFile.ts","./src/xcode/addTargetDependency.ts","./src/xcode/addToPbxNativeTargetSection.ts","./src/xcode/addToPbxProjectSection.ts","./src/xcode/addXCConfigurationList.ts"],"version":"5.8.3"}
|
|
1
|
+
{"root":["./src/index.ts","./src/types.ts","./src/withConfig.ts","./src/withPlist.ts","./src/withPushNotifications.ts","./src/withUnsupportedOS.ts","./src/withWidgetExtensionEntitlements.ts","./src/withXcode.ts","./src/lib/getWidgetExtensionEntitlements.ts","./src/lib/getWidgetFiles.ts","./src/xcode/addBuildPhases.ts","./src/xcode/addPbxGroup.ts","./src/xcode/addProductFile.ts","./src/xcode/addTargetDependency.ts","./src/xcode/addToPbxNativeTargetSection.ts","./src/xcode/addToPbxProjectSection.ts","./src/xcode/addXCConfigurationList.ts"],"version":"5.8.3"}
|
package/src/index.ts
CHANGED
|
@@ -7,14 +7,38 @@ type Voidable<T> = T | void
|
|
|
7
7
|
|
|
8
8
|
export type DynamicIslandTimerType = 'circular' | 'digital'
|
|
9
9
|
|
|
10
|
+
export type ElapsedTimer = {
|
|
11
|
+
startDate: number // milliseconds timestamp (past time when timer started)
|
|
12
|
+
}
|
|
13
|
+
|
|
10
14
|
type ProgressBarType =
|
|
11
15
|
| {
|
|
12
16
|
date?: number
|
|
13
17
|
progress?: undefined
|
|
18
|
+
elapsedTimer?: undefined
|
|
19
|
+
currentStep?: undefined
|
|
20
|
+
totalSteps?: undefined
|
|
14
21
|
}
|
|
15
22
|
| {
|
|
16
23
|
date?: undefined
|
|
17
24
|
progress?: number
|
|
25
|
+
elapsedTimer?: undefined
|
|
26
|
+
currentStep?: undefined
|
|
27
|
+
totalSteps?: undefined
|
|
28
|
+
}
|
|
29
|
+
| {
|
|
30
|
+
date?: undefined
|
|
31
|
+
progress?: undefined
|
|
32
|
+
elapsedTimer?: ElapsedTimer
|
|
33
|
+
currentStep?: undefined
|
|
34
|
+
totalSteps?: undefined
|
|
35
|
+
}
|
|
36
|
+
| {
|
|
37
|
+
date?: undefined
|
|
38
|
+
progress?: undefined
|
|
39
|
+
elapsedTimer?: undefined
|
|
40
|
+
currentStep?: number
|
|
41
|
+
totalSteps?: number
|
|
18
42
|
}
|
|
19
43
|
|
|
20
44
|
export type LiveActivityState = {
|
|
@@ -23,6 +47,7 @@ export type LiveActivityState = {
|
|
|
23
47
|
progressBar?: ProgressBarType
|
|
24
48
|
imageName?: string
|
|
25
49
|
dynamicIslandImageName?: string
|
|
50
|
+
smallImageName?: string
|
|
26
51
|
}
|
|
27
52
|
|
|
28
53
|
export type NativeLiveActivityState = {
|
|
@@ -51,8 +76,8 @@ export type ImageAlign = 'top' | 'center' | 'bottom'
|
|
|
51
76
|
|
|
52
77
|
export type ImageDimension = number | `${number}%`
|
|
53
78
|
export type ImageSize = {
|
|
54
|
-
width
|
|
55
|
-
height
|
|
79
|
+
width?: ImageDimension
|
|
80
|
+
height?: ImageDimension
|
|
56
81
|
}
|
|
57
82
|
|
|
58
83
|
export type ImageContentFit = 'cover' | 'contain' | 'fill' | 'none' | 'scale-down'
|
|
@@ -69,7 +94,10 @@ export type LiveActivityConfig = {
|
|
|
69
94
|
imagePosition?: ImagePosition
|
|
70
95
|
imageAlign?: ImageAlign
|
|
71
96
|
imageSize?: ImageSize
|
|
97
|
+
smallImageSize?: ImageSize
|
|
72
98
|
contentFit?: ImageContentFit
|
|
99
|
+
progressSegmentActiveColor?: string
|
|
100
|
+
progressSegmentInactiveColor?: string
|
|
73
101
|
}
|
|
74
102
|
|
|
75
103
|
export type ActivityTokenReceivedEvent = {
|
|
@@ -79,7 +107,7 @@ export type ActivityTokenReceivedEvent = {
|
|
|
79
107
|
}
|
|
80
108
|
|
|
81
109
|
export type ActivityPushToStartTokenReceivedEvent = {
|
|
82
|
-
activityPushToStartToken: string
|
|
110
|
+
activityPushToStartToken: string | null
|
|
83
111
|
}
|
|
84
112
|
|
|
85
113
|
type ActivityState = 'active' | 'dismissed' | 'pending' | 'stale' | 'ended'
|
|
@@ -107,15 +135,24 @@ function assertIOS(name: string) {
|
|
|
107
135
|
function normalizeConfig(config?: LiveActivityConfig) {
|
|
108
136
|
if (config === undefined) return config
|
|
109
137
|
|
|
110
|
-
const { padding, imageSize, ...base } =
|
|
138
|
+
const { padding, imageSize, smallImageSize, progressSegmentActiveColor, progressSegmentInactiveColor, ...base } =
|
|
139
|
+
config
|
|
111
140
|
type NormalizedConfig = LiveActivityConfig & {
|
|
112
141
|
paddingDetails?: Padding
|
|
113
142
|
imageWidth?: number
|
|
114
143
|
imageHeight?: number
|
|
115
144
|
imageWidthPercent?: number
|
|
116
145
|
imageHeightPercent?: number
|
|
146
|
+
smallImageWidth?: number
|
|
147
|
+
smallImageHeight?: number
|
|
148
|
+
smallImageWidthPercent?: number
|
|
149
|
+
smallImageHeightPercent?: number
|
|
150
|
+
}
|
|
151
|
+
const normalized: NormalizedConfig = {
|
|
152
|
+
...base,
|
|
153
|
+
progressSegmentActiveColor,
|
|
154
|
+
progressSegmentInactiveColor,
|
|
117
155
|
}
|
|
118
|
-
const normalized: NormalizedConfig = { ...base }
|
|
119
156
|
|
|
120
157
|
// Normalize padding: keep number in padding, object in paddingDetails
|
|
121
158
|
if (typeof padding === 'number') {
|
|
@@ -124,33 +161,38 @@ function normalizeConfig(config?: LiveActivityConfig) {
|
|
|
124
161
|
normalized.paddingDetails = padding
|
|
125
162
|
}
|
|
126
163
|
|
|
127
|
-
//
|
|
128
|
-
|
|
164
|
+
// Helper to parse a dimension value (number or percent string like '50%')
|
|
165
|
+
const parseDimension = (
|
|
166
|
+
value: ImageDimension | undefined,
|
|
167
|
+
fieldName: string
|
|
168
|
+
): { absolute?: number; percent?: number } => {
|
|
169
|
+
if (value === undefined) return {}
|
|
170
|
+
|
|
171
|
+
if (typeof value === 'number') return { absolute: value }
|
|
129
172
|
const regExp = /^(100(?:\.0+)?|\d{1,2}(?:\.\d+)?)%$/ // Matches 0.0% to 100.0%
|
|
173
|
+
const match = value.trim().match(regExp)
|
|
174
|
+
if (match) return { percent: Number(match[1]) }
|
|
175
|
+
throw new Error(`${fieldName} percent string must be in format "0%" to "100%"`)
|
|
176
|
+
}
|
|
130
177
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
throw new Error('imageSize.width percent string must be in format "0%" to "100%"')
|
|
141
|
-
}
|
|
142
|
-
}
|
|
178
|
+
// Normalize imageSize
|
|
179
|
+
if (imageSize) {
|
|
180
|
+
const w = parseDimension(imageSize.width, 'imageSize.width')
|
|
181
|
+
const h = parseDimension(imageSize.height, 'imageSize.height')
|
|
182
|
+
if (w.absolute !== undefined) normalized.imageWidth = w.absolute
|
|
183
|
+
if (w.percent !== undefined) normalized.imageWidthPercent = w.percent
|
|
184
|
+
if (h.absolute !== undefined) normalized.imageHeight = h.absolute
|
|
185
|
+
if (h.percent !== undefined) normalized.imageHeightPercent = h.percent
|
|
186
|
+
}
|
|
143
187
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
}
|
|
188
|
+
// Normalize smallImageSize
|
|
189
|
+
if (smallImageSize) {
|
|
190
|
+
const w = parseDimension(smallImageSize.width, 'smallImageSize.width')
|
|
191
|
+
const h = parseDimension(smallImageSize.height, 'smallImageSize.height')
|
|
192
|
+
if (w.absolute !== undefined) normalized.smallImageWidth = w.absolute
|
|
193
|
+
if (w.percent !== undefined) normalized.smallImageWidthPercent = w.percent
|
|
194
|
+
if (h.absolute !== undefined) normalized.smallImageHeight = h.absolute
|
|
195
|
+
if (h.percent !== undefined) normalized.smallImageHeightPercent = h.percent
|
|
154
196
|
}
|
|
155
197
|
|
|
156
198
|
return normalized
|
|
@@ -181,12 +223,21 @@ export function updateActivity(id: string, state: LiveActivityState) {
|
|
|
181
223
|
if (assertIOS('updateActivity')) return ExpoLiveActivityModule.updateActivity(id, state)
|
|
182
224
|
}
|
|
183
225
|
|
|
226
|
+
/**
|
|
227
|
+
* @param {function} updateTokenListener The listener function that will be called when an update token is received.
|
|
228
|
+
*/
|
|
184
229
|
export function addActivityTokenListener(
|
|
185
|
-
|
|
230
|
+
updateTokenListener: (event: ActivityTokenReceivedEvent) => void
|
|
186
231
|
): Voidable<EventSubscription> {
|
|
187
|
-
if (assertIOS('addActivityTokenListener'))
|
|
232
|
+
if (assertIOS('addActivityTokenListener'))
|
|
233
|
+
return ExpoLiveActivityModule.addListener('onTokenReceived', updateTokenListener)
|
|
188
234
|
}
|
|
189
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Adds a listener that is called when a push-to-start token is received. Supported only on iOS > 17.2.
|
|
238
|
+
* On earlier iOS versions, the listener will return null as a token.
|
|
239
|
+
* @param {function} listener The listener function that will be called when the observer starts and then when a push-to-start token is received.
|
|
240
|
+
*/
|
|
190
241
|
export function addActivityPushToStartTokenListener(
|
|
191
242
|
listener: (event: ActivityPushToStartTokenReceivedEvent) => void
|
|
192
243
|
): Voidable<EventSubscription> {
|
|
@@ -194,8 +245,12 @@ export function addActivityPushToStartTokenListener(
|
|
|
194
245
|
return ExpoLiveActivityModule.addListener('onPushToStartTokenReceived', listener)
|
|
195
246
|
}
|
|
196
247
|
|
|
248
|
+
/**
|
|
249
|
+
* @param {function} statusListener The listener function that will be called when an activity status changes.
|
|
250
|
+
*/
|
|
197
251
|
export function addActivityUpdatesListener(
|
|
198
|
-
|
|
252
|
+
statusListener: (event: ActivityUpdateEvent) => void
|
|
199
253
|
): Voidable<EventSubscription> {
|
|
200
|
-
if (assertIOS('addActivityUpdatesListener'))
|
|
254
|
+
if (assertIOS('addActivityUpdatesListener'))
|
|
255
|
+
return ExpoLiveActivityModule.addListener('onStateChange', statusListener)
|
|
201
256
|
}
|