expo-live-activity 0.4.3-alpha1 → 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/README.md +59 -6
- package/build/index.d.ts +27 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +42 -29
- package/build/index.js.map +1 -1
- package/ios/ExpoLiveActivityModule.swift +87 -6
- 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 +130 -104
- package/ios-files/LiveActivityWidget.swift +95 -6
- package/ios-files/ViewHelpers.swift +41 -0
- package/package.json +2 -3
- package/plugin/build/index.js +2 -0
- 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/types.ts +1 -0
- package/plugin/src/withUnsupportedOS.ts +10 -0
- package/plugin/tsconfig.tsbuildinfo +1 -1
- package/src/index.ts +70 -28
- package/.prettierignore +0 -5
|
@@ -10,6 +10,10 @@ public struct LiveActivityAttributes: ActivityAttributes {
|
|
|
10
10
|
var progress: Double?
|
|
11
11
|
var imageName: String?
|
|
12
12
|
var dynamicIslandImageName: String?
|
|
13
|
+
var smallImageName: String?
|
|
14
|
+
var elapsedTimerStartDateInMilliseconds: Double?
|
|
15
|
+
var currentStep: Int?
|
|
16
|
+
var totalSteps: Int?
|
|
13
17
|
|
|
14
18
|
public init(
|
|
15
19
|
title: String,
|
|
@@ -17,7 +21,11 @@ public struct LiveActivityAttributes: ActivityAttributes {
|
|
|
17
21
|
timerEndDateInMilliseconds: Double? = nil,
|
|
18
22
|
progress: Double? = nil,
|
|
19
23
|
imageName: String? = nil,
|
|
20
|
-
dynamicIslandImageName: String? = nil
|
|
24
|
+
dynamicIslandImageName: String? = nil,
|
|
25
|
+
smallImageName: String? = nil,
|
|
26
|
+
elapsedTimerStartDateInMilliseconds: Double? = nil,
|
|
27
|
+
currentStep: Int? = nil,
|
|
28
|
+
totalSteps: Int? = nil
|
|
21
29
|
) {
|
|
22
30
|
self.title = title
|
|
23
31
|
self.subtitle = subtitle
|
|
@@ -25,6 +33,10 @@ public struct LiveActivityAttributes: ActivityAttributes {
|
|
|
25
33
|
self.progress = progress
|
|
26
34
|
self.imageName = imageName
|
|
27
35
|
self.dynamicIslandImageName = dynamicIslandImageName
|
|
36
|
+
self.smallImageName = smallImageName
|
|
37
|
+
self.elapsedTimerStartDateInMilliseconds = elapsedTimerStartDateInMilliseconds
|
|
38
|
+
self.currentStep = currentStep
|
|
39
|
+
self.totalSteps = totalSteps
|
|
28
40
|
}
|
|
29
41
|
}
|
|
30
42
|
|
|
@@ -43,8 +55,14 @@ public struct LiveActivityAttributes: ActivityAttributes {
|
|
|
43
55
|
var imageHeight: Int?
|
|
44
56
|
var imageWidthPercent: Double?
|
|
45
57
|
var imageHeightPercent: Double?
|
|
58
|
+
var smallImageWidth: Int?
|
|
59
|
+
var smallImageHeight: Int?
|
|
60
|
+
var smallImageWidthPercent: Double?
|
|
61
|
+
var smallImageHeightPercent: Double?
|
|
46
62
|
var imageAlign: String?
|
|
47
63
|
var contentFit: String?
|
|
64
|
+
var progressSegmentActiveColor: String?
|
|
65
|
+
var progressSegmentInactiveColor: String?
|
|
48
66
|
|
|
49
67
|
public init(
|
|
50
68
|
name: String,
|
|
@@ -62,8 +80,14 @@ public struct LiveActivityAttributes: ActivityAttributes {
|
|
|
62
80
|
imageHeight: Int? = nil,
|
|
63
81
|
imageWidthPercent: Double? = nil,
|
|
64
82
|
imageHeightPercent: Double? = nil,
|
|
83
|
+
smallImageWidth: Int? = nil,
|
|
84
|
+
smallImageHeight: Int? = nil,
|
|
85
|
+
smallImageWidthPercent: Double? = nil,
|
|
86
|
+
smallImageHeightPercent: Double? = nil,
|
|
65
87
|
imageAlign: String? = nil,
|
|
66
|
-
contentFit: String? = nil
|
|
88
|
+
contentFit: String? = nil,
|
|
89
|
+
progressSegmentActiveColor: String? = nil,
|
|
90
|
+
progressSegmentInactiveColor: String? = nil
|
|
67
91
|
) {
|
|
68
92
|
self.name = name
|
|
69
93
|
self.backgroundColor = backgroundColor
|
|
@@ -80,8 +104,14 @@ public struct LiveActivityAttributes: ActivityAttributes {
|
|
|
80
104
|
self.imageHeight = imageHeight
|
|
81
105
|
self.imageWidthPercent = imageWidthPercent
|
|
82
106
|
self.imageHeightPercent = imageHeightPercent
|
|
107
|
+
self.smallImageWidth = smallImageWidth
|
|
108
|
+
self.smallImageHeight = smallImageHeight
|
|
109
|
+
self.smallImageWidthPercent = smallImageWidthPercent
|
|
110
|
+
self.smallImageHeightPercent = smallImageHeightPercent
|
|
83
111
|
self.imageAlign = imageAlign
|
|
84
112
|
self.contentFit = contentFit
|
|
113
|
+
self.progressSegmentActiveColor = progressSegmentActiveColor
|
|
114
|
+
self.progressSegmentInactiveColor = progressSegmentInactiveColor
|
|
85
115
|
}
|
|
86
116
|
|
|
87
117
|
public enum DynamicIslandTimerType: String, Codable {
|
|
@@ -118,7 +148,7 @@ public struct LiveActivityAttributes: ActivityAttributes {
|
|
|
118
148
|
@available(iOS 16.1, *)
|
|
119
149
|
public struct LiveActivityWidget: Widget {
|
|
120
150
|
public var body: some WidgetConfiguration {
|
|
121
|
-
ActivityConfiguration(for: LiveActivityAttributes.self) { context in
|
|
151
|
+
let baseConfiguration = ActivityConfiguration(for: LiveActivityAttributes.self) { context in
|
|
122
152
|
LiveActivityView(contentState: context.state, attributes: context.attributes)
|
|
123
153
|
.activityBackgroundTint(
|
|
124
154
|
context.attributes.backgroundColor.map { Color(hex: $0) }
|
|
@@ -141,7 +171,17 @@ public struct LiveActivityWidget: Widget {
|
|
|
141
171
|
}
|
|
142
172
|
}
|
|
143
173
|
DynamicIslandExpandedRegion(.bottom) {
|
|
144
|
-
if let
|
|
174
|
+
if let startDate = context.state.elapsedTimerStartDateInMilliseconds {
|
|
175
|
+
ElapsedTimerText(
|
|
176
|
+
startTimeMilliseconds: startDate,
|
|
177
|
+
color: context.attributes.progressViewTint.map { Color(hex: $0) } ?? .white
|
|
178
|
+
)
|
|
179
|
+
.font(.title2)
|
|
180
|
+
.fontWeight(.semibold)
|
|
181
|
+
.padding(.top, 5)
|
|
182
|
+
.padding(.horizontal, 5)
|
|
183
|
+
.applyWidgetURL(from: context.attributes.deepLinkUrl)
|
|
184
|
+
} else if let date = context.state.timerEndDateInMilliseconds {
|
|
145
185
|
dynamicIslandExpandedBottom(
|
|
146
186
|
endDate: date, progressViewTint: context.attributes.progressViewTint
|
|
147
187
|
)
|
|
@@ -162,7 +202,18 @@ public struct LiveActivityWidget: Widget {
|
|
|
162
202
|
.applyWidgetURL(from: context.attributes.deepLinkUrl)
|
|
163
203
|
}
|
|
164
204
|
} compactTrailing: {
|
|
165
|
-
if let
|
|
205
|
+
if let startDate = context.state.elapsedTimerStartDateInMilliseconds {
|
|
206
|
+
ElapsedTimerText(
|
|
207
|
+
startTimeMilliseconds: startDate,
|
|
208
|
+
color: nil
|
|
209
|
+
)
|
|
210
|
+
.font(.system(size: 15))
|
|
211
|
+
.minimumScaleFactor(0.8)
|
|
212
|
+
.fontWeight(.semibold)
|
|
213
|
+
.frame(maxWidth: 60)
|
|
214
|
+
.multilineTextAlignment(.trailing)
|
|
215
|
+
.applyWidgetURL(from: context.attributes.deepLinkUrl)
|
|
216
|
+
} else if let date = context.state.timerEndDateInMilliseconds {
|
|
166
217
|
compactTimer(
|
|
167
218
|
endDate: date,
|
|
168
219
|
timerType: context.attributes.timerType ?? .circular,
|
|
@@ -175,7 +226,15 @@ public struct LiveActivityWidget: Widget {
|
|
|
175
226
|
).applyWidgetURL(from: context.attributes.deepLinkUrl)
|
|
176
227
|
}
|
|
177
228
|
} minimal: {
|
|
178
|
-
if let
|
|
229
|
+
if let startDate = context.state.elapsedTimerStartDateInMilliseconds {
|
|
230
|
+
ElapsedTimerText(
|
|
231
|
+
startTimeMilliseconds: startDate,
|
|
232
|
+
color: context.attributes.progressViewTint.map { Color(hex: $0) }
|
|
233
|
+
)
|
|
234
|
+
.font(.system(size: 11))
|
|
235
|
+
.minimumScaleFactor(0.6)
|
|
236
|
+
.applyWidgetURL(from: context.attributes.deepLinkUrl)
|
|
237
|
+
} else if let date = context.state.timerEndDateInMilliseconds {
|
|
179
238
|
compactTimer(
|
|
180
239
|
endDate: date,
|
|
181
240
|
timerType: context.attributes.timerType ?? .circular,
|
|
@@ -189,6 +248,12 @@ public struct LiveActivityWidget: Widget {
|
|
|
189
248
|
}
|
|
190
249
|
}
|
|
191
250
|
}
|
|
251
|
+
|
|
252
|
+
if #available(iOS 18.0, *) {
|
|
253
|
+
return baseConfiguration.supplementalActivityFamilies([.small])
|
|
254
|
+
} else {
|
|
255
|
+
return baseConfiguration
|
|
256
|
+
}
|
|
192
257
|
}
|
|
193
258
|
|
|
194
259
|
public init() {}
|
|
@@ -272,3 +337,27 @@ public struct LiveActivityWidget: Widget {
|
|
|
272
337
|
.padding(.top, 5)
|
|
273
338
|
}
|
|
274
339
|
}
|
|
340
|
+
|
|
341
|
+
// MARK: - Elapsed Timer View
|
|
342
|
+
|
|
343
|
+
struct ElapsedTimerText: View {
|
|
344
|
+
let startTimeMilliseconds: Double
|
|
345
|
+
let color: Color?
|
|
346
|
+
|
|
347
|
+
private var startTime: Date {
|
|
348
|
+
Date(timeIntervalSince1970: startTimeMilliseconds / 1000)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
var body: some View {
|
|
352
|
+
// Use Text with timerInterval for Live Activities - iOS handles the updates automatically
|
|
353
|
+
// The range goes from startTime to a far future date, with countsDown: false to count UP
|
|
354
|
+
Text(
|
|
355
|
+
timerInterval: startTime ... Date.distantFuture,
|
|
356
|
+
pauseTime: nil,
|
|
357
|
+
countsDown: false,
|
|
358
|
+
showsHours: true
|
|
359
|
+
)
|
|
360
|
+
.monospacedDigit()
|
|
361
|
+
.foregroundStyle(color ?? .primary)
|
|
362
|
+
}
|
|
363
|
+
}
|
|
@@ -11,6 +11,47 @@ func resizableImage(imageName: String, height: CGFloat?, width: CGFloat?) -> som
|
|
|
11
11
|
.frame(width: width, height: height)
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
@ViewBuilder
|
|
15
|
+
func fixedSizeImage(name: String, size: CGFloat) -> some View {
|
|
16
|
+
Image.dynamic(assetNameOrPath: name)
|
|
17
|
+
.resizable()
|
|
18
|
+
.scaledToFit()
|
|
19
|
+
.frame(width: size, height: size)
|
|
20
|
+
.layoutPriority(0)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@ViewBuilder
|
|
24
|
+
func smallTimerText(
|
|
25
|
+
endDate: Double,
|
|
26
|
+
isSubtitleDisplayed: Bool,
|
|
27
|
+
carPlayView: Bool = false,
|
|
28
|
+
labelColor: String?
|
|
29
|
+
) -> some View {
|
|
30
|
+
Text(timerInterval: Date.toTimerInterval(miliseconds: endDate))
|
|
31
|
+
.font(carPlayView
|
|
32
|
+
? (isSubtitleDisplayed ? .footnote : .title2)
|
|
33
|
+
: (isSubtitleDisplayed ? .footnote : .callout))
|
|
34
|
+
.fontWeight(carPlayView && !isSubtitleDisplayed ? .semibold : .medium)
|
|
35
|
+
.modifier(ConditionalForegroundViewModifier(color: labelColor))
|
|
36
|
+
.padding(.top, isSubtitleDisplayed ? 3 : 0)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@ViewBuilder
|
|
40
|
+
func styledLinearProgressView<Content: View>(
|
|
41
|
+
tint: Color?,
|
|
42
|
+
labelColor: String?,
|
|
43
|
+
@ViewBuilder content: () -> Content
|
|
44
|
+
) -> some View {
|
|
45
|
+
content()
|
|
46
|
+
.progressViewStyle(.linear)
|
|
47
|
+
.tint(tint)
|
|
48
|
+
.frame(height: 8.0)
|
|
49
|
+
.scaleEffect(x: 1, y: 2, anchor: .center)
|
|
50
|
+
.clipShape(RoundedRectangle(cornerRadius: 10))
|
|
51
|
+
.modifier(ConditionalForegroundViewModifier(color: labelColor))
|
|
52
|
+
.padding(.bottom, 6)
|
|
53
|
+
}
|
|
54
|
+
|
|
14
55
|
private struct ContainerSizeKey: PreferenceKey {
|
|
15
56
|
static var defaultValue: CGSize?
|
|
16
57
|
static func reduce(value: inout CGSize?, nextValue: () -> CGSize?) {
|
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,8 +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
|
-
"
|
|
12
|
-
"format:fix": "prettier --write . && swiftformat --exclude \"**/node_modules/\" .",
|
|
11
|
+
"lint:fix": "expo-module eslint --fix && swiftformat --exclude \"**/node_modules/\" .",
|
|
13
12
|
"lint": "expo-module eslint",
|
|
14
13
|
"lint:libOnly": "expo-module eslint --ignore-pattern 'example/*'",
|
|
15
14
|
"test": "expo-module test",
|
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;
|
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
|
|
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 = {
|
|
@@ -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
|