expo-live-activity 0.2.0-alpha3 → 0.2.0-alpha4

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
@@ -74,8 +74,8 @@ The latter requires adding "App Groups" capability to both "main app" and "live
74
74
  `expo-live-activity` module exports three primary functions to manage live activities:
75
75
 
76
76
  ### Managing Live Activities
77
- - **`startActivity(state: LiveActivityState, styles?: LiveActivityStyles)`**:
78
- Start a new live activity. Takes a `state` configuration object for initial activity state and an optional `styles` object to customize appearance. It returns the `ID` of the created live activity, which should be stored for future reference.
77
+ - **`startActivity(state: LiveActivityState, config?: LiveActivityConfig)`**:
78
+ Start a new live activity. Takes a `state` configuration object for initial activity state and an optional `config` object to customize appearance or behavior. It returns the `ID` of the created live activity, which should be stored for future reference.
79
79
 
80
80
  - **`updateActivity(id: string, state: LiveActivityState)`**:
81
81
  Update an existing live activity. The `state` object should contain updated information. The `activityId` indicates which activity should be updated.
@@ -87,6 +87,9 @@ The latter requires adding "App Groups" capability to both "main app" and "live
87
87
  - **`addActivityTokenListener(listener: (event: ActivityTokenReceivedEvent) => void): EventSubscription)`**:
88
88
  Subscribe to changes in the push notification token associated with live activities.
89
89
 
90
+ ### Deep linking
91
+ When starting a new live activity, it's possible to pass `deepLinkUrl` field in `config` object. This can be any string that you can handle in your main app target.
92
+
90
93
  ### State Object Structure
91
94
  The `state` object should include:
92
95
  ```javascript
@@ -99,8 +102,8 @@ The `state` object should include:
99
102
  };
100
103
  ```
101
104
 
102
- ### Styles Object Structure
103
- The `styles` object should include:
105
+ ### Config Object Structure
106
+ The `config` object should include:
104
107
  ```typescript
105
108
  {
106
109
  backgroundColor?: string;
@@ -108,6 +111,7 @@ The `styles` object should include:
108
111
  subtitleColor?: string;
109
112
  progressViewTint?: string;
110
113
  progressViewLabelColor?: string;
114
+ deepLinkUrl?: string;
111
115
  timerType?: DynamicIslandTimerType; // "circular" | "digital" - defines timer appereance on the dynamic island
112
116
  };
113
117
  ```
@@ -123,16 +127,17 @@ const state = {
123
127
  dynamicIslandImageName: "dynamic_island_image"
124
128
  };
125
129
 
126
- const styles = {
130
+ const config = {
127
131
  backgroundColor: "#FFFFFF",
128
132
  titleColor: "#000000",
129
133
  subtitleColor: "#333333",
130
134
  progressViewTint: "#4CAF50",
131
135
  progressViewLabelColor: "#FFFFFF",
136
+ deepLinkUrl: "/dashboard",
132
137
  timerType: "circular"
133
138
  };
134
139
 
135
- const activityId = LiveActivity.startActivity(state, styles);
140
+ const activityId = LiveActivity.startActivity(state, config);
136
141
  // Store activityId for future reference
137
142
  ```
138
143
  This will initiate a live activity with the specified title, subtitle, image from your configured assets folder and a time to which there will be a countdown in a progress view.
package/build/index.d.ts CHANGED
@@ -7,12 +7,13 @@ export type LiveActivityState = {
7
7
  imageName?: string;
8
8
  dynamicIslandImageName?: string;
9
9
  };
10
- export type LiveActivityStyles = {
10
+ export type LiveActivityConfig = {
11
11
  backgroundColor?: string;
12
12
  titleColor?: string;
13
13
  subtitleColor?: string;
14
14
  progressViewTint?: string;
15
15
  progressViewLabelColor?: string;
16
+ deepLinkUrl?: string;
16
17
  timerType?: DynamicIslandTimerType;
17
18
  };
18
19
  export type ActivityTokenReceivedEvent = {
@@ -24,11 +25,11 @@ export type LiveActivityModuleEvents = {
24
25
  };
25
26
  /**
26
27
  * @param {LiveActivityState} state The state for the live activity.
27
- * @param {LiveActivityStyles} styles Live activity styling object.
28
+ * @param {LiveActivityConfig} config Live activity config object.
28
29
  * @returns {string} The identifier of the started activity.
29
30
  * @throws {Error} When function is called on a platform different from iOS.
30
31
  */
31
- export declare function startActivity(state: LiveActivityState, styles?: LiveActivityStyles): string;
32
+ export declare function startActivity(state: LiveActivityState, config?: LiveActivityConfig): string;
32
33
  /**
33
34
  * @param {string} id The identifier of the activity to stop.
34
35
  * @param {LiveActivityState} state The updated state for the live activity.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,MAAM,sBAAsB,GAAG,UAAU,GAAG,SAAS,CAAA;AAE3D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,SAAS,CAAC,EAAE,sBAAsB,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,eAAe,EAAE,CAAC,MAAM,EAAE,0BAA0B,KAAK,IAAI,CAAC;CAC/D,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,iBAAiB,EAAE,MAAM,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAK3F;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,OAKhE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,OAKlE;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,0BAA0B,KAAK,IAAI,GAAG,iBAAiB,CAKjH"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,MAAM,sBAAsB,GAAG,UAAU,GAAG,SAAS,CAAA;AAE3D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,sBAAsB,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,eAAe,EAAE,CAAC,MAAM,EAAE,0BAA0B,KAAK,IAAI,CAAC;CAC/D,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,iBAAiB,EAAE,MAAM,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAK3F;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,OAKhE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,OAKlE;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,0BAA0B,KAAK,IAAI,GAAG,iBAAiB,CAKjH"}
package/build/index.js CHANGED
@@ -2,15 +2,15 @@ import ExpoLiveActivityModule from "./ExpoLiveActivityModule";
2
2
  import { Platform } from "react-native";
3
3
  /**
4
4
  * @param {LiveActivityState} state The state for the live activity.
5
- * @param {LiveActivityStyles} styles Live activity styling object.
5
+ * @param {LiveActivityConfig} config Live activity config object.
6
6
  * @returns {string} The identifier of the started activity.
7
7
  * @throws {Error} When function is called on a platform different from iOS.
8
8
  */
9
- export function startActivity(state, styles) {
9
+ export function startActivity(state, config) {
10
10
  if (Platform.OS !== "ios") {
11
11
  throw new Error("startActivity is only available on iOS");
12
12
  }
13
- return ExpoLiveActivityModule.startActivity(state, styles);
13
+ return ExpoLiveActivityModule.startActivity(state, config);
14
14
  }
15
15
  /**
16
16
  * @param {string} id The identifier of the activity to stop.
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AA+BxC;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,KAAwB,EAAE,MAA2B;IACjF,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,sBAAsB,CAAC,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;AAC7D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,EAAU,EAAE,KAAwB;IAC/D,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,sBAAsB,CAAC,YAAY,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;AACxD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,EAAU,EAAE,KAAwB;IACjE,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,sBAAsB,CAAC,cAAc,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,QAAqD;IAC5F,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,sBAAsB,CAAC,WAAW,CAAC,iBAAiB,EAAE,QAAQ,CAAC,CAAC;AACzE,CAAC","sourcesContent":["import ExpoLiveActivityModule from \"./ExpoLiveActivityModule\";\nimport { Platform } from \"react-native\";\nimport { EventSubscription } from 'expo-modules-core';\n\nexport type DynamicIslandTimerType = 'circular' | 'digital'\n\nexport type LiveActivityState = {\n title: string;\n subtitle?: string;\n date?: number;\n imageName?: string;\n dynamicIslandImageName?: string;\n};\n\nexport type LiveActivityStyles = {\n backgroundColor?: string;\n titleColor?: string;\n subtitleColor?: string;\n progressViewTint?: string;\n progressViewLabelColor?: string;\n timerType?: DynamicIslandTimerType;\n};\n\nexport type ActivityTokenReceivedEvent = {\n activityID: string;\n activityPushToken: string;\n};\n\nexport type LiveActivityModuleEvents = {\n onTokenReceived: (params: ActivityTokenReceivedEvent) => void;\n};\n\n/**\n * @param {LiveActivityState} state The state for the live activity.\n * @param {LiveActivityStyles} styles Live activity styling object.\n * @returns {string} The identifier of the started activity.\n * @throws {Error} When function is called on a platform different from iOS.\n */\nexport function startActivity(state: LiveActivityState, styles?: LiveActivityStyles): string {\n if (Platform.OS !== \"ios\") {\n throw new Error(\"startActivity is only available on iOS\");\n }\n return ExpoLiveActivityModule.startActivity(state, styles);\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 * @throws {Error} When function is called on a platform different from iOS.\n */\nexport function stopActivity(id: string, state: LiveActivityState) {\n if (Platform.OS !== \"ios\") {\n throw new Error(\"stopActivity is only available on iOS\");\n }\n 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 * @throws {Error} When function is called on a platform different from iOS.\n */\nexport function updateActivity(id: string, state: LiveActivityState) {\n if (Platform.OS !== \"ios\") {\n throw new Error(\"updateActivity is only available on iOS\");\n }\n return ExpoLiveActivityModule.updateActivity(id, state);\n}\n\nexport function addActivityTokenListener(listener: (event: ActivityTokenReceivedEvent) => void): EventSubscription {\n if (Platform.OS !== \"ios\") {\n throw new Error(\"updateActivity is only available on iOS\");\n }\n return ExpoLiveActivityModule.addListener('onTokenReceived', listener);\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAgCxC;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,KAAwB,EAAE,MAA2B;IACjF,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,sBAAsB,CAAC,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;AAC7D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,EAAU,EAAE,KAAwB;IAC/D,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,sBAAsB,CAAC,YAAY,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;AACxD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,EAAU,EAAE,KAAwB;IACjE,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,sBAAsB,CAAC,cAAc,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,QAAqD;IAC5F,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,sBAAsB,CAAC,WAAW,CAAC,iBAAiB,EAAE,QAAQ,CAAC,CAAC;AACzE,CAAC","sourcesContent":["import ExpoLiveActivityModule from \"./ExpoLiveActivityModule\";\nimport { Platform } from \"react-native\";\nimport { EventSubscription } from 'expo-modules-core';\n\nexport type DynamicIslandTimerType = 'circular' | 'digital'\n\nexport type LiveActivityState = {\n title: string;\n subtitle?: string;\n date?: number;\n imageName?: string;\n dynamicIslandImageName?: string;\n};\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};\n\nexport type ActivityTokenReceivedEvent = {\n activityID: string;\n activityPushToken: string;\n};\n\nexport type LiveActivityModuleEvents = {\n onTokenReceived: (params: ActivityTokenReceivedEvent) => void;\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.\n * @throws {Error} When function is called on a platform different from iOS.\n */\nexport function startActivity(state: LiveActivityState, config?: LiveActivityConfig): string {\n if (Platform.OS !== \"ios\") {\n throw new Error(\"startActivity is only available on iOS\");\n }\n return ExpoLiveActivityModule.startActivity(state, 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 * @throws {Error} When function is called on a platform different from iOS.\n */\nexport function stopActivity(id: string, state: LiveActivityState) {\n if (Platform.OS !== \"ios\") {\n throw new Error(\"stopActivity is only available on iOS\");\n }\n 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 * @throws {Error} When function is called on a platform different from iOS.\n */\nexport function updateActivity(id: string, state: LiveActivityState) {\n if (Platform.OS !== \"ios\") {\n throw new Error(\"updateActivity is only available on iOS\");\n }\n return ExpoLiveActivityModule.updateActivity(id, state);\n}\n\nexport function addActivityTokenListener(listener: (event: ActivityTokenReceivedEvent) => void): EventSubscription {\n if (Platform.OS !== \"ios\") {\n throw new Error(\"updateActivity is only available on iOS\");\n }\n return ExpoLiveActivityModule.addListener('onTokenReceived', listener);\n}\n"]}
@@ -24,7 +24,7 @@ public class ExpoLiveActivityModule: Module {
24
24
  var dynamicIslandImageName: String?
25
25
  }
26
26
 
27
- struct LiveActivityStyles: Record {
27
+ struct LiveActivityConfig: Record {
28
28
  @Field
29
29
  var backgroundColor: String?
30
30
 
@@ -39,6 +39,9 @@ public class ExpoLiveActivityModule: Module {
39
39
 
40
40
  @Field
41
41
  var progressViewLabelColor: String?
42
+
43
+ @Field
44
+ var deepLinkUrl: String?
42
45
 
43
46
  @Field
44
47
  var timerType: DynamicIslandTimerType?
@@ -80,19 +83,21 @@ public class ExpoLiveActivityModule: Module {
80
83
 
81
84
  Events("onTokenReceived")
82
85
 
83
- Function("startActivity") { (state: LiveActivityState, styles: LiveActivityStyles?) -> String in
86
+ Function("startActivity") { (state: LiveActivityState, maybeConfig: LiveActivityConfig?) -> String in
84
87
  print("Starting activity")
85
88
  if #available(iOS 16.2, *) {
86
89
  if ActivityAuthorizationInfo().areActivitiesEnabled {
87
90
  do {
91
+ let config = maybeConfig ?? LiveActivityConfig()
88
92
  let attributes = LiveActivityAttributes(
89
93
  name: "ExpoLiveActivity",
90
- backgroundColor: styles?.backgroundColor,
91
- titleColor: styles?.titleColor,
92
- subtitleColor: styles?.subtitleColor,
93
- progressViewTint: styles?.progressViewTint,
94
- progressViewLabelColor: styles?.progressViewLabelColor,
95
- timerType: styles?.timerType == .digital ? .digital : .circular
94
+ backgroundColor: config.backgroundColor,
95
+ titleColor: config.titleColor,
96
+ subtitleColor: config.subtitleColor,
97
+ progressViewTint: config.progressViewTint,
98
+ progressViewLabelColor: config.progressViewLabelColor,
99
+ deepLinkUrl: config.deepLinkUrl,
100
+ timerType: config.timerType == .digital ? .digital : .circular
96
101
  )
97
102
  let initialState = LiveActivityAttributes.ContentState(
98
103
  title: state.title,
@@ -100,11 +105,11 @@ public class ExpoLiveActivityModule: Module {
100
105
  date: toContentStateDate(date: state.date),
101
106
  )
102
107
  let pushNotificationsEnabled =
103
- Bundle.main.object(forInfoDictionaryKey: "ExpoLiveActivity_EnablePushNotifications")
108
+ Bundle.main.object(forInfoDictionaryKey: "ExpoLiveActivity_EnablePushNotifications") as? Bool
104
109
  let activity = try Activity.request(
105
110
  attributes: attributes,
106
111
  content: .init(state: initialState, staleDate: nil),
107
- pushType: pushNotificationsEnabled == nil ? nil : .token
112
+ pushType: pushNotificationsEnabled == true ? .token : nil
108
113
  )
109
114
 
110
115
  Task {
@@ -23,6 +23,7 @@ struct LiveActivityAttributes: ActivityAttributes {
23
23
  var subtitleColor: String?
24
24
  var progressViewTint: String?
25
25
  var progressViewLabelColor: String?
26
+ var deepLinkUrl: String?
26
27
  var timerType: DynamicIslandTimerType
27
28
 
28
29
  enum DynamicIslandTimerType: String, Codable {
@@ -24,6 +24,7 @@ struct LiveActivityAttributes: ActivityAttributes {
24
24
  var subtitleColor: String?
25
25
  var progressViewTint: String?
26
26
  var progressViewLabelColor: String?
27
+ var deepLinkUrl: String?
27
28
  var timerType: DynamicIslandTimerType
28
29
 
29
30
  enum DynamicIslandTimerType: String, Codable {
@@ -37,33 +38,37 @@ struct LiveActivityWidget: Widget {
37
38
  ActivityConfiguration(for: LiveActivityAttributes.self) { context in
38
39
  LiveActivityView(contentState: context.state, attributes: context.attributes)
39
40
  .activityBackgroundTint(
40
- context.attributes.backgroundColor != nil ? Color(hex: context.attributes.backgroundColor!) : nil
41
+ context.attributes.backgroundColor.map { Color(hex: $0) }
41
42
  )
42
43
  .activitySystemActionForegroundColor(Color.black)
43
-
44
+ .applyWidgetURL(from: context.attributes.deepLinkUrl)
44
45
  } dynamicIsland: { context in
45
46
  DynamicIsland {
46
47
  DynamicIslandExpandedRegion(.leading, priority: 1) {
47
48
  dynamicIslandExpandedLeading(title: context.state.title, subtitle: context.state.subtitle)
48
49
  .dynamicIsland(verticalPlacement: .belowIfTooWide)
49
50
  .padding(.leading, 5)
51
+ .applyWidgetURL(from: context.attributes.deepLinkUrl)
50
52
  }
51
53
  DynamicIslandExpandedRegion(.trailing) {
52
54
  if let imageName = context.state.imageName {
53
55
  dynamicIslandExpandedTrailing(imageName: imageName)
54
56
  .padding(.trailing, 5)
57
+ .applyWidgetURL(from: context.attributes.deepLinkUrl)
55
58
  }
56
59
  }
57
60
  DynamicIslandExpandedRegion(.bottom) {
58
61
  if let date = context.state.date {
59
62
  dynamicIslandExpandedBottom(endDate: date, progressViewTint: context.attributes.progressViewTint)
60
63
  .padding(.horizontal, 5)
64
+ .applyWidgetURL(from: context.attributes.deepLinkUrl)
61
65
  }
62
66
  }
63
67
  } compactLeading: {
64
68
  if let dynamicIslandImageName = context.state.dynamicIslandImageName {
65
69
  resizableImage(imageName: dynamicIslandImageName)
66
70
  .frame(maxWidth: 23, maxHeight: 23)
71
+ .applyWidgetURL(from: context.attributes.deepLinkUrl)
67
72
  }
68
73
  } compactTrailing: {
69
74
  if let date = context.state.date {
@@ -71,7 +76,7 @@ struct LiveActivityWidget: Widget {
71
76
  endDate: date,
72
77
  timerType: context.attributes.timerType,
73
78
  progressViewTint: context.attributes.progressViewTint
74
- )
79
+ ).applyWidgetURL(from: context.attributes.deepLinkUrl)
75
80
  }
76
81
  } minimal: {
77
82
  if let date = context.state.date {
@@ -79,7 +84,7 @@ struct LiveActivityWidget: Widget {
79
84
  endDate: date,
80
85
  timerType: context.attributes.timerType,
81
86
  progressViewTint: context.attributes.progressViewTint
82
- )
87
+ ).applyWidgetURL(from: context.attributes.deepLinkUrl)
83
88
  }
84
89
  }
85
90
  }
@@ -100,7 +105,7 @@ struct LiveActivityWidget: Widget {
100
105
  .multilineTextAlignment(.trailing)
101
106
  } else {
102
107
  circularTimer(endDate: endDate)
103
- .tint(progressViewTint != nil ? Color(hex: progressViewTint!) : nil)
108
+ .tint(progressViewTint.map { Color(hex: $0) })
104
109
  }
105
110
  }
106
111
 
@@ -133,7 +138,7 @@ struct LiveActivityWidget: Widget {
133
138
  private func dynamicIslandExpandedBottom(endDate: Double, progressViewTint: String?) -> some View {
134
139
  ProgressView(timerInterval: Date.toTimerInterval(miliseconds: endDate))
135
140
  .foregroundStyle(.white)
136
- .tint(progressViewTint != nil ? Color(hex: progressViewTint!) : nil)
141
+ .tint(progressViewTint.map { Color(hex: $0) })
137
142
  .padding(.top, 5)
138
143
  }
139
144
 
@@ -0,0 +1,19 @@
1
+ //
2
+ // View+applyIfPresent.swift
3
+ //
4
+ //
5
+ // Created by Artur Bilski on 05/08/2025.
6
+ //
7
+
8
+ import SwiftUI
9
+
10
+ extension View {
11
+ @ViewBuilder
12
+ func applyIfPresent<T>(_ value: T?, transform: (Self, T) -> some View) -> some View {
13
+ if let value {
14
+ transform(self, value)
15
+ } else {
16
+ self
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,15 @@
1
+ //
2
+ // View+applyWidgetURL.swift
3
+ //
4
+ //
5
+ // Created by Artur Bilski on 05/08/2025.
6
+ //
7
+
8
+ import SwiftUI
9
+
10
+ extension View {
11
+ @ViewBuilder
12
+ func applyWidgetURL(from urlString: String?) -> some View {
13
+ applyIfPresent(urlString) { view, string in view.widgetURL(URL(string: string))}
14
+ }
15
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-live-activity",
3
- "version": "0.2.0-alpha3",
3
+ "version": "0.2.0-alpha4",
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",
package/src/index.ts CHANGED
@@ -12,12 +12,13 @@ export type LiveActivityState = {
12
12
  dynamicIslandImageName?: string;
13
13
  };
14
14
 
15
- export type LiveActivityStyles = {
15
+ export type LiveActivityConfig = {
16
16
  backgroundColor?: string;
17
17
  titleColor?: string;
18
18
  subtitleColor?: string;
19
19
  progressViewTint?: string;
20
20
  progressViewLabelColor?: string;
21
+ deepLinkUrl?: string;
21
22
  timerType?: DynamicIslandTimerType;
22
23
  };
23
24
 
@@ -32,15 +33,15 @@ export type LiveActivityModuleEvents = {
32
33
 
33
34
  /**
34
35
  * @param {LiveActivityState} state The state for the live activity.
35
- * @param {LiveActivityStyles} styles Live activity styling object.
36
+ * @param {LiveActivityConfig} config Live activity config object.
36
37
  * @returns {string} The identifier of the started activity.
37
38
  * @throws {Error} When function is called on a platform different from iOS.
38
39
  */
39
- export function startActivity(state: LiveActivityState, styles?: LiveActivityStyles): string {
40
+ export function startActivity(state: LiveActivityState, config?: LiveActivityConfig): string {
40
41
  if (Platform.OS !== "ios") {
41
42
  throw new Error("startActivity is only available on iOS");
42
43
  }
43
- return ExpoLiveActivityModule.startActivity(state, styles);
44
+ return ExpoLiveActivityModule.startActivity(state, config);
44
45
  }
45
46
 
46
47
  /**