expo-live-activity 0.4.1-alpha1 → 0.4.1

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 CHANGED
@@ -153,7 +153,7 @@ The `config` object should include:
153
153
  timerType?: DynamicIslandTimerType; // "circular" | "digital" - defines timer appearance on the dynamic island
154
154
  padding?: Padding // number | {top?: number bottom?: number ...}
155
155
  imagePosition?: ImagePosition; // 'left' | 'right';
156
- imageSize?: ImageSize // 'fullHeight' | 'default';
156
+ imageSize?: ImageSize // 'default' | number (points maxHeight)
157
157
  };
158
158
  ```
159
159
 
@@ -186,7 +186,7 @@ const config: LiveActivity.LiveActivityConfig = {
186
186
  timerType: 'circular',
187
187
  padding: { horizontal: 20, top: 16, bottom: 16 },
188
188
  imagePosition: 'right',
189
- imageSize: 'default',
189
+ imageSize: 'default', // or a number e.g. 80 for custom image height
190
190
  }
191
191
 
192
192
  const activityId = LiveActivity.startActivity(state, config)
package/build/index.d.ts CHANGED
@@ -31,8 +31,9 @@ export type Padding = {
31
31
  vertical?: number;
32
32
  horizontal?: number;
33
33
  } | number;
34
- export type ImagePosition = 'left' | 'right';
35
- export type ImageSize = 'fullHeight' | 'default';
34
+ export type ImagePosition = 'left' | 'right' | 'leftStretch' | 'rightStretch';
35
+ export type ImageAlign = 'top' | 'center' | 'bottom';
36
+ export type ImageSize = number;
36
37
  export type LiveActivityConfig = {
37
38
  backgroundColor?: string;
38
39
  titleColor?: string;
@@ -43,6 +44,7 @@ export type LiveActivityConfig = {
43
44
  timerType?: DynamicIslandTimerType;
44
45
  padding?: Padding;
45
46
  imagePosition?: ImagePosition;
47
+ imageAlign?: ImageAlign;
46
48
  imageSize?: ImageSize;
47
49
  };
48
50
  export type ActivityTokenReceivedEvent = {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/build/index.js CHANGED
@@ -6,21 +6,26 @@ function assertIOS(name) {
6
6
  console.error(`${name} is only available on iOS`);
7
7
  return isIOS;
8
8
  }
9
+ function normalizeConfig(config) {
10
+ if (config === undefined)
11
+ return config;
12
+ const { padding, ...base } = config;
13
+ const normalized = { ...base };
14
+ // Normalize padding: keep number in padding, object in paddingDetails
15
+ if (typeof padding === 'number') {
16
+ normalized.padding = padding;
17
+ }
18
+ else if (typeof padding === 'object') {
19
+ normalized.paddingDetails = padding;
20
+ }
21
+ return normalized;
22
+ }
9
23
  /**
10
24
  * @param {LiveActivityState} state The state for the live activity.
11
25
  * @param {LiveActivityConfig} config Live activity config object.
12
26
  * @returns {string} The identifier of the started activity or undefined if creating live activity failed.
13
27
  */
14
28
  export function startActivity(state, config) {
15
- function normalizeConfig(config) {
16
- if (typeof config?.padding === 'number') {
17
- return { ...config, padding: config.padding, paddingDetails: undefined };
18
- }
19
- if (typeof config?.padding === 'object') {
20
- return { ...config, padding: undefined, paddingDetails: config.padding };
21
- }
22
- return config;
23
- }
24
29
  if (assertIOS('startActivity'))
25
30
  return ExpoLiveActivityModule.startActivity(state, normalizeConfig(config));
26
31
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAEvC,OAAO,sBAAsB,MAAM,0BAA0B,CAAA;AAqF7D,SAAS,SAAS,CAAC,IAAY;IAC7B,MAAM,KAAK,GAAG,QAAQ,CAAC,EAAE,KAAK,KAAK,CAAA;IAEnC,IAAI,CAAC,KAAK;QAAE,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,2BAA2B,CAAC,CAAA;IAE7D,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,KAAwB,EAAE,MAA2B;IACjF,SAAS,eAAe,CAAC,MAA2B;QAClD,IAAI,OAAO,MAAM,EAAE,OAAO,KAAK,QAAQ,EAAE,CAAC;YACxC,OAAO,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,CAAA;QAC1E,CAAC;QACD,IAAI,OAAO,MAAM,EAAE,OAAO,KAAK,QAAQ,EAAE,CAAC;YACxC,OAAO,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,CAAC,OAAO,EAAE,CAAA;QAC1E,CAAC;QACD,OAAO,MAAM,CAAA;IACf,CAAC;IAED,IAAI,SAAS,CAAC,eAAe,CAAC;QAAE,OAAO,sBAAsB,CAAC,aAAa,CAAC,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC,CAAA;AAC7G,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,EAAU,EAAE,KAAwB;IAC/D,IAAI,SAAS,CAAC,cAAc,CAAC;QAAE,OAAO,sBAAsB,CAAC,YAAY,CAAC,EAAE,EAAE,KAAK,CAAC,CAAA;AACtF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,EAAU,EAAE,KAAwB;IACjE,IAAI,SAAS,CAAC,gBAAgB,CAAC;QAAE,OAAO,sBAAsB,CAAC,cAAc,CAAC,EAAE,EAAE,KAAK,CAAC,CAAA;AAC1F,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,QAAqD;IAErD,IAAI,SAAS,CAAC,0BAA0B,CAAC;QAAE,OAAO,sBAAsB,CAAC,WAAW,CAAC,iBAAiB,EAAE,QAAQ,CAAC,CAAA;AACnH,CAAC;AAED,MAAM,UAAU,mCAAmC,CACjD,QAAgE;IAEhE,IAAI,SAAS,CAAC,qCAAqC,CAAC;QAClD,OAAO,sBAAsB,CAAC,WAAW,CAAC,4BAA4B,EAAE,QAAQ,CAAC,CAAA;AACrF,CAAC;AAED,MAAM,UAAU,0BAA0B,CACxC,QAA8C;IAE9C,IAAI,SAAS,CAAC,4BAA4B,CAAC;QAAE,OAAO,sBAAsB,CAAC,WAAW,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAA;AACnH,CAAC","sourcesContent":["import { EventSubscription } from 'expo-modules-core'\nimport { Platform } from 'react-native'\n\nimport ExpoLiveActivityModule from './ExpoLiveActivityModule'\n\ntype Voidable<T> = T | void\n\nexport type DynamicIslandTimerType = 'circular' | 'digital'\n\ntype ProgressBarType =\n | {\n date?: number\n progress?: undefined\n }\n | {\n date?: undefined\n progress?: number\n }\n\nexport type LiveActivityState = {\n title: string\n subtitle?: string\n progressBar?: ProgressBarType\n imageName?: string\n dynamicIslandImageName?: string\n}\n\nexport type NativeLiveActivityState = {\n title: string\n subtitle?: string\n date?: number\n progress?: number\n imageName?: string\n dynamicIslandImageName?: string\n}\n\nexport type Padding =\n | {\n top?: number\n bottom?: number\n left?: number\n right?: number\n vertical?: number\n horizontal?: number\n }\n | number\n\nexport type ImagePosition = 'left' | 'right'\n\nexport type ImageSize = 'fullHeight' | 'default'\n\nexport type LiveActivityConfig = {\n backgroundColor?: string\n titleColor?: string\n subtitleColor?: string\n progressViewTint?: string\n progressViewLabelColor?: string\n deepLinkUrl?: string\n timerType?: DynamicIslandTimerType\n padding?: Padding\n imagePosition?: ImagePosition\n imageSize?: ImageSize\n}\n\nexport type ActivityTokenReceivedEvent = {\n activityID: string\n activityName: string\n activityPushToken: string\n}\n\nexport type ActivityPushToStartTokenReceivedEvent = {\n activityPushToStartToken: string\n}\n\ntype ActivityState = 'active' | 'dismissed' | 'pending' | 'stale' | 'ended'\n\nexport type ActivityUpdateEvent = {\n activityID: string\n activityName: string\n activityState: ActivityState\n}\n\nexport type LiveActivityModuleEvents = {\n onTokenReceived: (params: ActivityTokenReceivedEvent) => void\n onPushToStartTokenReceived: (params: ActivityPushToStartTokenReceivedEvent) => void\n onStateChange: (params: ActivityUpdateEvent) => void\n}\n\nfunction assertIOS(name: string) {\n const isIOS = Platform.OS === 'ios'\n\n if (!isIOS) console.error(`${name} is only available on iOS`)\n\n return isIOS\n}\n\n/**\n * @param {LiveActivityState} state The state for the live activity.\n * @param {LiveActivityConfig} config Live activity config object.\n * @returns {string} The identifier of the started activity or undefined if creating live activity failed.\n */\nexport function startActivity(state: LiveActivityState, config?: LiveActivityConfig): Voidable<string> {\n function normalizeConfig(config?: LiveActivityConfig) {\n if (typeof config?.padding === 'number') {\n return { ...config, padding: config.padding, paddingDetails: undefined }\n }\n if (typeof config?.padding === 'object') {\n return { ...config, padding: undefined, paddingDetails: config.padding }\n }\n return config\n }\n\n if (assertIOS('startActivity')) return ExpoLiveActivityModule.startActivity(state, normalizeConfig(config))\n}\n\n/**\n * @param {string} id The identifier of the activity to stop.\n * @param {LiveActivityState} state The updated state for the live activity.\n */\nexport function stopActivity(id: string, state: LiveActivityState) {\n if (assertIOS('stopActivity')) return ExpoLiveActivityModule.stopActivity(id, state)\n}\n\n/**\n * @param {string} id The identifier of the activity to update.\n * @param {LiveActivityState} state The updated state for the live activity.\n */\nexport function updateActivity(id: string, state: LiveActivityState) {\n if (assertIOS('updateActivity')) return ExpoLiveActivityModule.updateActivity(id, state)\n}\n\nexport function addActivityTokenListener(\n listener: (event: ActivityTokenReceivedEvent) => void\n): Voidable<EventSubscription> {\n if (assertIOS('addActivityTokenListener')) return ExpoLiveActivityModule.addListener('onTokenReceived', listener)\n}\n\nexport function addActivityPushToStartTokenListener(\n listener: (event: ActivityPushToStartTokenReceivedEvent) => void\n): Voidable<EventSubscription> {\n if (assertIOS('addActivityPushToStartTokenListener'))\n return ExpoLiveActivityModule.addListener('onPushToStartTokenReceived', listener)\n}\n\nexport function addActivityUpdatesListener(\n listener: (event: ActivityUpdateEvent) => void\n): Voidable<EventSubscription> {\n if (assertIOS('addActivityUpdatesListener')) return ExpoLiveActivityModule.addListener('onStateChange', listener)\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAEvC,OAAO,sBAAsB,MAAM,0BAA0B,CAAA;AAwF7D,SAAS,SAAS,CAAC,IAAY;IAC7B,MAAM,KAAK,GAAG,QAAQ,CAAC,EAAE,KAAK,KAAK,CAAA;IAEnC,IAAI,CAAC,KAAK;QAAE,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,2BAA2B,CAAC,CAAA;IAE7D,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,eAAe,CAAC,MAA2B;IAClD,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAA;IAEvC,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,CAAA;IAEnC,MAAM,UAAU,GAAqB,EAAE,GAAG,IAAI,EAAE,CAAA;IAEhD,sEAAsE;IACtE,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,UAAU,CAAC,OAAO,GAAG,OAAO,CAAA;IAC9B,CAAC;SAAM,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QACvC,UAAU,CAAC,cAAc,GAAG,OAAO,CAAA;IACrC,CAAC;IAED,OAAO,UAAU,CAAA;AACnB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,KAAwB,EAAE,MAA2B;IACjF,IAAI,SAAS,CAAC,eAAe,CAAC;QAAE,OAAO,sBAAsB,CAAC,aAAa,CAAC,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC,CAAA;AAC7G,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,EAAU,EAAE,KAAwB;IAC/D,IAAI,SAAS,CAAC,cAAc,CAAC;QAAE,OAAO,sBAAsB,CAAC,YAAY,CAAC,EAAE,EAAE,KAAK,CAAC,CAAA;AACtF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,EAAU,EAAE,KAAwB;IACjE,IAAI,SAAS,CAAC,gBAAgB,CAAC;QAAE,OAAO,sBAAsB,CAAC,cAAc,CAAC,EAAE,EAAE,KAAK,CAAC,CAAA;AAC1F,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,QAAqD;IAErD,IAAI,SAAS,CAAC,0BAA0B,CAAC;QAAE,OAAO,sBAAsB,CAAC,WAAW,CAAC,iBAAiB,EAAE,QAAQ,CAAC,CAAA;AACnH,CAAC;AAED,MAAM,UAAU,mCAAmC,CACjD,QAAgE;IAEhE,IAAI,SAAS,CAAC,qCAAqC,CAAC;QAClD,OAAO,sBAAsB,CAAC,WAAW,CAAC,4BAA4B,EAAE,QAAQ,CAAC,CAAA;AACrF,CAAC;AAED,MAAM,UAAU,0BAA0B,CACxC,QAA8C;IAE9C,IAAI,SAAS,CAAC,4BAA4B,CAAC;QAAE,OAAO,sBAAsB,CAAC,WAAW,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAA;AACnH,CAAC","sourcesContent":["import { EventSubscription } from 'expo-modules-core'\nimport { Platform } from 'react-native'\n\nimport ExpoLiveActivityModule from './ExpoLiveActivityModule'\n\ntype Voidable<T> = T | void\n\nexport type DynamicIslandTimerType = 'circular' | 'digital'\n\ntype ProgressBarType =\n | {\n date?: number\n progress?: undefined\n }\n | {\n date?: undefined\n progress?: number\n }\n\nexport type LiveActivityState = {\n title: string\n subtitle?: string\n progressBar?: ProgressBarType\n imageName?: string\n dynamicIslandImageName?: string\n}\n\nexport type NativeLiveActivityState = {\n title: string\n subtitle?: string\n date?: number\n progress?: number\n imageName?: string\n dynamicIslandImageName?: string\n}\n\nexport type Padding =\n | {\n top?: number\n bottom?: number\n left?: number\n right?: number\n vertical?: number\n horizontal?: number\n }\n | number\n\nexport type ImagePosition = 'left' | 'right' | 'leftStretch' | 'rightStretch'\n\nexport type ImageAlign = 'top' | 'center' | 'bottom'\n\nexport type ImageSize = number\n\nexport type LiveActivityConfig = {\n backgroundColor?: string\n titleColor?: string\n subtitleColor?: string\n progressViewTint?: string\n progressViewLabelColor?: string\n deepLinkUrl?: string\n timerType?: DynamicIslandTimerType\n padding?: Padding\n imagePosition?: ImagePosition\n imageAlign?: ImageAlign\n imageSize?: ImageSize\n}\n\nexport type ActivityTokenReceivedEvent = {\n activityID: string\n activityName: string\n activityPushToken: string\n}\n\nexport type ActivityPushToStartTokenReceivedEvent = {\n activityPushToStartToken: string\n}\n\ntype ActivityState = 'active' | 'dismissed' | 'pending' | 'stale' | 'ended'\n\nexport type ActivityUpdateEvent = {\n activityID: string\n activityName: string\n activityState: ActivityState\n}\n\nexport type LiveActivityModuleEvents = {\n onTokenReceived: (params: ActivityTokenReceivedEvent) => void\n onPushToStartTokenReceived: (params: ActivityPushToStartTokenReceivedEvent) => void\n onStateChange: (params: ActivityUpdateEvent) => void\n}\n\nfunction assertIOS(name: string) {\n const isIOS = Platform.OS === 'ios'\n\n if (!isIOS) console.error(`${name} is only available on iOS`)\n\n return isIOS\n}\n\nfunction normalizeConfig(config?: LiveActivityConfig) {\n if (config === undefined) return config\n\n const { padding, ...base } = config\n type NormalizedConfig = LiveActivityConfig & { paddingDetails?: Padding }\n const normalized: NormalizedConfig = { ...base }\n\n // Normalize padding: keep number in padding, object in paddingDetails\n if (typeof padding === 'number') {\n normalized.padding = padding\n } else if (typeof padding === 'object') {\n normalized.paddingDetails = padding\n }\n\n return normalized\n}\n\n/**\n * @param {LiveActivityState} state The state for the live activity.\n * @param {LiveActivityConfig} config Live activity config object.\n * @returns {string} The identifier of the started activity or undefined if creating live activity failed.\n */\nexport function startActivity(state: LiveActivityState, config?: LiveActivityConfig): Voidable<string> {\n if (assertIOS('startActivity')) return ExpoLiveActivityModule.startActivity(state, normalizeConfig(config))\n}\n\n/**\n * @param {string} id The identifier of the activity to stop.\n * @param {LiveActivityState} state The updated state for the live activity.\n */\nexport function stopActivity(id: string, state: LiveActivityState) {\n if (assertIOS('stopActivity')) return ExpoLiveActivityModule.stopActivity(id, state)\n}\n\n/**\n * @param {string} id The identifier of the activity to update.\n * @param {LiveActivityState} state The updated state for the live activity.\n */\nexport function updateActivity(id: string, state: LiveActivityState) {\n if (assertIOS('updateActivity')) return ExpoLiveActivityModule.updateActivity(id, state)\n}\n\nexport function addActivityTokenListener(\n listener: (event: ActivityTokenReceivedEvent) => void\n): Voidable<EventSubscription> {\n if (assertIOS('addActivityTokenListener')) return ExpoLiveActivityModule.addListener('onTokenReceived', listener)\n}\n\nexport function addActivityPushToStartTokenListener(\n listener: (event: ActivityPushToStartTokenReceivedEvent) => void\n): Voidable<EventSubscription> {\n if (assertIOS('addActivityPushToStartTokenListener'))\n return ExpoLiveActivityModule.addListener('onPushToStartTokenReceived', listener)\n}\n\nexport function addActivityUpdatesListener(\n listener: (event: ActivityUpdateEvent) => void\n): Voidable<EventSubscription> {\n if (assertIOS('addActivityUpdatesListener')) return ExpoLiveActivityModule.addListener('onStateChange', listener)\n}\n"]}
@@ -59,7 +59,10 @@ public class ExpoLiveActivityModule: Module {
59
59
  var imagePosition: String?
60
60
 
61
61
  @Field
62
- var imageSize: String?
62
+ var imageSize: Int?
63
+
64
+ @Field
65
+ var imageAlign: String?
63
66
 
64
67
  struct PaddingDetails: Record {
65
68
  @Field var top: Int?
@@ -222,7 +225,8 @@ public class ExpoLiveActivityModule: Module {
222
225
  )
223
226
  },
224
227
  imagePosition: config.imagePosition,
225
- imageSize: config.imageSize
228
+ imageSize: config.imageSize,
229
+ imageAlign: config.imageAlign
226
230
  )
227
231
 
228
232
  let initialState = LiveActivityAttributes.ContentState(
@@ -22,7 +22,8 @@ struct LiveActivityAttributes: ActivityAttributes {
22
22
  var padding: Int?
23
23
  var paddingDetails: PaddingDetails?
24
24
  var imagePosition: String?
25
- var imageSize: String?
25
+ var imageSize: Int?
26
+ var imageAlign: String?
26
27
 
27
28
  enum DynamicIslandTimerType: String, Codable {
28
29
  case circular
@@ -23,6 +23,26 @@ import WidgetKit
23
23
  attributes.progressViewTint.map { Color(hex: $0) }
24
24
  }
25
25
 
26
+ private var imageAlignment: Alignment {
27
+ switch attributes.imageAlign {
28
+ case "center":
29
+ return .center
30
+ case "bottom":
31
+ return .bottom
32
+ default:
33
+ return .top
34
+ }
35
+ }
36
+
37
+ @ViewBuilder
38
+ private func alignedImage(imageName: String) -> some View {
39
+ VStack {
40
+ resizableImage(imageName: imageName)
41
+ .applyImageSize(attributes.imageSize)
42
+ }
43
+ .frame(maxHeight: .infinity, alignment: imageAlignment)
44
+ }
45
+
26
46
  var body: some View {
27
47
  let defaultPadding = 24
28
48
 
@@ -55,11 +75,15 @@ import WidgetKit
55
75
  )
56
76
 
57
77
  VStack(alignment: .leading) {
78
+ let position = attributes.imagePosition ?? "right"
79
+ let isStretch = position.contains("Stretch")
80
+ let isLeftImage = position.hasPrefix("left")
81
+ let hasImage = contentState.imageName != nil
82
+ let effectiveStretch = isStretch && hasImage
58
83
  HStack(alignment: .center) {
59
- if attributes.imagePosition == "left" {
84
+ if hasImage, isLeftImage {
60
85
  if let imageName = contentState.imageName {
61
- resizableImage(imageName: imageName)
62
- .applyImageSize(attributes.imageSize)
86
+ alignedImage(imageName: imageName)
63
87
  }
64
88
  }
65
89
 
@@ -75,7 +99,7 @@ import WidgetKit
75
99
  .modifier(ConditionalForegroundViewModifier(color: attributes.subtitleColor))
76
100
  }
77
101
 
78
- if attributes.imageSize == "fullHeight" {
102
+ if effectiveStretch {
79
103
  if let date = contentState.timerEndDateInMilliseconds {
80
104
  ProgressView(timerInterval: Date.toTimerInterval(miliseconds: date))
81
105
  .tint(progressViewTint)
@@ -88,16 +112,16 @@ import WidgetKit
88
112
  }
89
113
  }
90
114
 
91
- if attributes.imagePosition == "right" || attributes.imagePosition == nil {
115
+ if hasImage, !isLeftImage { // right side (default)
92
116
  Spacer()
93
117
  if let imageName = contentState.imageName {
94
- resizableImage(imageName: imageName)
95
- .applyImageSize(attributes.imageSize)
118
+ alignedImage(imageName: imageName)
96
119
  }
97
120
  }
98
121
  }
99
122
 
100
- if attributes.imageSize != "fullHeight" {
123
+ if !effectiveStretch {
124
+ // Bottom progress (hidden when using Stretch variants where progress is inline)
101
125
  if let date = contentState.timerEndDateInMilliseconds {
102
126
  ProgressView(timerInterval: Date.toTimerInterval(miliseconds: date))
103
127
  .tint(progressViewTint)
@@ -23,7 +23,8 @@ struct LiveActivityAttributes: ActivityAttributes {
23
23
  var padding: Int?
24
24
  var paddingDetails: PaddingDetails?
25
25
  var imagePosition: String?
26
- var imageSize: String?
26
+ var imageSize: Int?
27
+ var imageAlign: String?
27
28
 
28
29
  enum DynamicIslandTimerType: String, Codable {
29
30
  case circular
@@ -8,12 +8,7 @@ func resizableImage(imageName: String) -> some View {
8
8
 
9
9
  extension View {
10
10
  @ViewBuilder
11
- func applyImageSize(_ size: String?) -> some View {
12
- switch size {
13
- case "fullHeight":
14
- frame(maxHeight: .infinity)
15
- default:
16
- frame(maxHeight: 64)
17
- }
11
+ func applyImageSize(_ size: Int?) -> some View {
12
+ frame(maxHeight: CGFloat(size ?? 64))
18
13
  }
19
14
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-live-activity",
3
- "version": "0.4.1-alpha1",
3
+ "version": "0.4.1",
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",
@@ -11,7 +11,7 @@ const withWidgetExtensionEntitlements_1 = require("./withWidgetExtensionEntitlem
11
11
  const withXcode_1 = require("./withXcode");
12
12
  const withWidgetsAndLiveActivities = (config, props) => {
13
13
  const deploymentTarget = '16.2';
14
- const targetName = `${config_plugins_1.IOSConfig.XcodeUtils.sanitizedName(config.name)}LiveActivity`;
14
+ const targetName = 'LiveActivity';
15
15
  const bundleIdentifier = `${config.ios?.bundleIdentifier}.${targetName}`;
16
16
  config.ios = {
17
17
  ...config.ios,
@@ -1,4 +1,4 @@
1
- import { IOSConfig, withPlugins } from 'expo/config-plugins'
1
+ import { withPlugins } from 'expo/config-plugins'
2
2
 
3
3
  import type { LiveActivityConfigPlugin } from './types'
4
4
  import { withConfig } from './withConfig'
@@ -9,7 +9,7 @@ import { withXcode } from './withXcode'
9
9
 
10
10
  const withWidgetsAndLiveActivities: LiveActivityConfigPlugin = (config, props) => {
11
11
  const deploymentTarget = '16.2'
12
- const targetName = `${IOSConfig.XcodeUtils.sanitizedName(config.name)}LiveActivity`
12
+ const targetName = 'LiveActivity'
13
13
  const bundleIdentifier = `${config.ios?.bundleIdentifier}.${targetName}`
14
14
 
15
15
  config.ios = {
package/src/index.ts CHANGED
@@ -45,9 +45,11 @@ export type Padding =
45
45
  }
46
46
  | number
47
47
 
48
- export type ImagePosition = 'left' | 'right'
48
+ export type ImagePosition = 'left' | 'right' | 'leftStretch' | 'rightStretch'
49
49
 
50
- export type ImageSize = 'fullHeight' | 'default'
50
+ export type ImageAlign = 'top' | 'center' | 'bottom'
51
+
52
+ export type ImageSize = number
51
53
 
52
54
  export type LiveActivityConfig = {
53
55
  backgroundColor?: string
@@ -59,6 +61,7 @@ export type LiveActivityConfig = {
59
61
  timerType?: DynamicIslandTimerType
60
62
  padding?: Padding
61
63
  imagePosition?: ImagePosition
64
+ imageAlign?: ImageAlign
62
65
  imageSize?: ImageSize
63
66
  }
64
67
 
@@ -94,22 +97,29 @@ function assertIOS(name: string) {
94
97
  return isIOS
95
98
  }
96
99
 
100
+ function normalizeConfig(config?: LiveActivityConfig) {
101
+ if (config === undefined) return config
102
+
103
+ const { padding, ...base } = config
104
+ type NormalizedConfig = LiveActivityConfig & { paddingDetails?: Padding }
105
+ const normalized: NormalizedConfig = { ...base }
106
+
107
+ // Normalize padding: keep number in padding, object in paddingDetails
108
+ if (typeof padding === 'number') {
109
+ normalized.padding = padding
110
+ } else if (typeof padding === 'object') {
111
+ normalized.paddingDetails = padding
112
+ }
113
+
114
+ return normalized
115
+ }
116
+
97
117
  /**
98
118
  * @param {LiveActivityState} state The state for the live activity.
99
119
  * @param {LiveActivityConfig} config Live activity config object.
100
120
  * @returns {string} The identifier of the started activity or undefined if creating live activity failed.
101
121
  */
102
122
  export function startActivity(state: LiveActivityState, config?: LiveActivityConfig): Voidable<string> {
103
- function normalizeConfig(config?: LiveActivityConfig) {
104
- if (typeof config?.padding === 'number') {
105
- return { ...config, padding: config.padding, paddingDetails: undefined }
106
- }
107
- if (typeof config?.padding === 'object') {
108
- return { ...config, padding: undefined, paddingDetails: config.padding }
109
- }
110
- return config
111
- }
112
-
113
123
  if (assertIOS('startActivity')) return ExpoLiveActivityModule.startActivity(state, normalizeConfig(config))
114
124
  }
115
125