Skip to content

Commit 26eaadd

Browse files
committed
feature complete?
1 parent 2090a0b commit 26eaadd

File tree

8 files changed

+203
-497
lines changed

8 files changed

+203
-497
lines changed

apps/activity-kit-example/app/(tabs)/index.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ActivityKit, AlarmKit } from '@kingstinct/react-native-activity-kit' // Importing the ActivityKit module'
2+
import { SecondaryButtonBehavior } from '@kingstinct/react-native-activity-kit/lib/specs/AlarmKit.nitro'
23
import { Image } from 'expo-image'
34
import * as Notifications from 'expo-notifications'
45
import { useState } from 'react'
@@ -57,7 +58,25 @@ export default function HomeScreen() {
5758

5859
<Button
5960
onPress={() => {
60-
AlarmKit.createCountdown('Test Countdown', 'Done', 'Countdown Title')
61+
const countdownDurationInSeconds = 10
62+
AlarmKit.createCountdown({
63+
tintColor: { red: 255, green: 0, blue: 0, alpha: 0.5 },
64+
alert: {
65+
title: 'Pomodoro focus time over!',
66+
stopButton: {
67+
text: 'Ok, cool!',
68+
systemImageName: 'stop.fill',
69+
textColor: { red: 0, green: 255, blue: 0 },
70+
},
71+
},
72+
countdown: {
73+
title: 'Pomodoro focus time!',
74+
},
75+
preAlert: countdownDurationInSeconds,
76+
metadata: {
77+
timerFiringAt: Date.now() + countdownDurationInSeconds * 1000,
78+
},
79+
})
6180
}}
6281
title="Start countdown"
6382
></Button>

packages/react-native-activity-kit/ios/AlarmKitModule.swift

Lines changed: 101 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,30 @@ import SwiftUI
33
import NitroModules
44

55
func createColor(_ color: RGBColor) -> Color {
6-
return Color.init(red: color.red, green: color.green, blue: color.blue)
6+
if let alpha = color.alpha {
7+
return Color.init(
8+
red: color.red * 255,
9+
green: color.green * 255,
10+
blue: color.blue * 255,
11+
opacity: alpha
12+
)
13+
}
14+
return Color.init(
15+
red: color.red * 255,
16+
green: color.green * 255,
17+
blue: color.blue * 255
18+
)
19+
}
20+
21+
func createPausedPresentation(_ paused: PausedPresentation?) -> AlarmPresentation.Paused? {
22+
if let paused = paused {
23+
return AlarmPresentation.Paused(
24+
title: LocalizedStringResource(stringLiteral: paused.title),
25+
resumeButton: createAlarmButton(paused.resumeButton),
26+
)
27+
}
28+
29+
return nil
730
}
831

932
@available(iOS 26.0, *)
@@ -24,101 +47,121 @@ func createAlarmButton(_ props: AlarmButtonProps) -> AlarmButton {
2447
)
2548
}
2649

50+
func createSecondButtonBehavior(_ behavior: SecondaryButtonBehavior?) -> AlarmPresentation.Alert.SecondaryButtonBehavior? {
51+
if let behavior = behavior {
52+
53+
switch behavior {
54+
case .countdown:
55+
return .countdown
56+
case .custom:
57+
return .custom
58+
case .none:
59+
return .none
60+
}
61+
}
62+
return nil
63+
}
64+
65+
func createAlertPresentation(_ alertPresentation: AlertPresentation) -> AlarmPresentation.Alert {
66+
let alertContent = AlarmPresentation.Alert(
67+
title: LocalizedStringResource(stringLiteral: alertPresentation.title),
68+
stopButton: createAlarmButton(alertPresentation.stopButton),
69+
secondaryButton: createAlarmButtonNullable(alertPresentation.secondaryButton),
70+
secondaryButtonBehavior: createSecondButtonBehavior(alertPresentation.secondaryButtonBehavior)
71+
)
72+
73+
return alertContent
74+
}
75+
76+
func scheduleAlarm(alarmConfiguration: AlarmManager.AlarmConfiguration<GenericDictionaryAlarmStruct>) -> Promise<any HybridAlarmProxySpec> {
77+
return Promise.async {
78+
try await withCheckedThrowingContinuation { continuation in
79+
Task {
80+
do {
81+
let id = UUID()
82+
let alarm = try await AlarmManager.shared.schedule(
83+
id: id,
84+
configuration: alarmConfiguration
85+
)
86+
let alarmModule = AlarmModule(alarm: alarm)
87+
88+
continuation.resume(returning: alarmModule)
89+
} catch {
90+
continuation.resume(throwing: error)
91+
}
92+
}
93+
}
94+
}
95+
}
96+
97+
func createAttributes(presentation: AlarmPresentation, metadata: AnyMap, tintColor: RGBColor) -> AlarmAttributes<GenericDictionaryAlarmStruct> {
98+
99+
let attributes = AlarmAttributes<GenericDictionaryAlarmStruct>.init(
100+
presentation: presentation,
101+
metadata: try? GenericDictionaryAlarmStruct(state: anyMapToDictionary(metadata)),
102+
tintColor: createColor(tintColor)
103+
)
104+
105+
return attributes
106+
}
107+
27108
@available(iOS 26.0, *)
28109
class AlarmKitModule: HybridAlarmKitModuleSpec {
29-
func createCountdown(props: CountdownProps) throws -> NitroModules.Promise<any HybridAlarmSpec> {
30-
let alertContent = AlarmPresentation.Alert(
31-
title: LocalizedStringResource(stringLiteral: props.alert.title),
32-
stopButton: createAlarmButton(props.alert.stopButton),
33-
)
110+
func createCountdown(props: CountdownProps) throws -> Promise<any HybridAlarmProxySpec> {
111+
let alertContent = createAlertPresentation(props.alert)
34112

35113
let countdownContent = AlarmPresentation.Countdown(
36114
title: LocalizedStringResource(stringLiteral: props.countdown.title),
37115
pauseButton: createAlarmButtonNullable(props.countdown.pauseButton),
38116
)
39117

40-
var pausedPresentation: AlarmPresentation.Paused?
41-
42-
if let paused = props.paused {
43-
pausedPresentation = AlarmPresentation.Paused(
44-
title: "Paused",
45-
resumeButton: createAlarmButton(paused.resumeButton),
46-
)
47-
}
118+
let pausedPresentation = createPausedPresentation(props.paused)
48119

49120
let presentation = AlarmPresentation(
50121
alert: alertContent,
51122
countdown: countdownContent,
52123
paused: pausedPresentation
53124
)
54125

55-
let id = UUID()
126+
let attributes = createAttributes(
127+
presentation: presentation,
128+
metadata: props.metadata,
129+
tintColor: props.tintColor
130+
)
56131

57132
let preAlert = TimeInterval(floatLiteral: props.preAlert)
58133
let postAlert = props.postAlert != nil ? TimeInterval(floatLiteral: props.postAlert!) : nil
59134

60-
let attributes = AlarmAttributes<GenericDictionaryAlarmStruct>.init(
61-
presentation: presentation,
62-
metadata: try? GenericDictionaryAlarmStruct(state: anyMapToDictionary(props.metadata)),
63-
tintColor: createColor(props.tintColor)
64-
)
65-
66135
let alarmConfiguration = AlarmManager.AlarmConfiguration(
67136
countdownDuration: Alarm.CountdownDuration.init(
68137
preAlert: preAlert,
69138
postAlert: postAlert
70139
),
71140
// schedule: Alarm.Schedule.fixed(Date.now.addingTimeInterval(countdownDuration)),
72141
attributes: attributes,
142+
73143
// stopIntent: Intent.unspecified (alarmID: id),
74144
// secondaryIntent: secondaryIntent(alarmID: id, userInput: userInput)
75145
sound: props.sound != nil ? .named(props.sound!) : .default
76146
)
77147

78-
return Promise.async {
79-
try await withCheckedThrowingContinuation { continuation in
80-
Task {
81-
do {
82-
let alarm = try await AlarmManager.shared.schedule(
83-
id: id,
84-
configuration: alarmConfiguration
85-
)
86-
let alarmModule = AlarmModule(alarm: alarm)
87-
continuation.resume(returning: alarmModule)
88-
} catch {
89-
continuation.resume(throwing: error)
90-
}
91-
}
92-
}
93-
}
148+
return scheduleAlarm(alarmConfiguration: alarmConfiguration)
94149
}
95150

96-
func createAlarm(props: AlarmProps) throws -> NitroModules.Promise<any HybridAlarmSpec> {
97-
let alertContent = AlarmPresentation.Alert(
98-
title: LocalizedStringResource(stringLiteral: props.alert.title),
99-
stopButton: createAlarmButton(props.alert.stopButton),
100-
)
151+
func createAlarm(props: AlarmProps) throws -> NitroModules.Promise<any HybridAlarmProxySpec> {
152+
let alertContent = createAlertPresentation(props.alert)
101153

102-
var pausedPresentation: AlarmPresentation.Paused?
103-
104-
if let paused = props.paused {
105-
pausedPresentation = AlarmPresentation.Paused(
106-
title: "Paused",
107-
resumeButton: createAlarmButton(paused.resumeButton),
108-
)
109-
}
154+
let pausedPresentation = createPausedPresentation(props.paused)
110155

111156
let presentation = AlarmPresentation(
112157
alert: alertContent,
113158
paused: pausedPresentation
114159
)
115160

116-
let id = UUID()
117-
118-
let attributes = AlarmAttributes<GenericDictionaryAlarmStruct>.init(
161+
let attributes = createAttributes(
119162
presentation: presentation,
120-
metadata: try? GenericDictionaryAlarmStruct(state: anyMapToDictionary(props.metadata)),
121-
tintColor: createColor(props.tintColor)
163+
metadata: props.metadata,
164+
tintColor: props.tintColor
122165
)
123166

124167
let alarmConfiguration = AlarmManager.AlarmConfiguration(
@@ -129,22 +172,7 @@ class AlarmKitModule: HybridAlarmKitModuleSpec {
129172
sound: props.sound != nil ? .named(props.sound!) : .default
130173
)
131174

132-
return Promise.async {
133-
try await withCheckedThrowingContinuation { continuation in
134-
Task {
135-
do {
136-
let alarm = try await AlarmManager.shared.schedule(
137-
id: id,
138-
configuration: alarmConfiguration
139-
)
140-
let alarmModule = AlarmModule(alarm: alarm)
141-
continuation.resume(returning: alarmModule)
142-
} catch {
143-
continuation.resume(throwing: error)
144-
}
145-
}
146-
}
147-
}
175+
return scheduleAlarm(alarmConfiguration: alarmConfiguration)
148176
}
149177

150178
func requestAuthorization() throws -> NitroModules.Promise<AuthStatus> {
@@ -171,7 +199,7 @@ class AlarmKitModule: HybridAlarmKitModuleSpec {
171199
}
172200
}
173201

174-
func alarmUpdates(callback: @escaping ([any HybridAlarmSpec]) -> Void) throws {
202+
func alarmUpdates(callback: @escaping ([any HybridAlarmProxySpec]) -> Void) throws {
175203
Task {
176204
for await alarms in AlarmManager.shared.alarmUpdates {
177205
callback(alarms.map { alarm in
@@ -181,7 +209,7 @@ class AlarmKitModule: HybridAlarmKitModuleSpec {
181209
}
182210
}
183211

184-
func alarms() throws -> [any HybridAlarmSpec] {
212+
func alarms() throws -> [any HybridAlarmProxySpec] {
185213
return try AlarmManager.shared.alarms.map { alarm in
186214
return AlarmModule(alarm: alarm)
187215
}

packages/react-native-activity-kit/ios/AlarmModule.swift

Lines changed: 0 additions & 20 deletions
This file was deleted.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import AlarmKit
2+
import NitroModules
3+
4+
@available(iOS 26.0, *)
5+
class AlarmModule: HybridAlarmProxySpec {
6+
func countdown() throws {
7+
try AlarmManager.shared.countdown(id: alarm.id)
8+
}
9+
10+
func pause() throws {
11+
try AlarmManager.shared.pause(id: alarm.id)
12+
}
13+
14+
func resume() throws {
15+
try AlarmManager.shared.resume(id: alarm.id)
16+
}
17+
18+
func stop() throws {
19+
try AlarmManager.shared.stop(id: alarm.id)
20+
}
21+
22+
func cancel() throws {
23+
try AlarmManager.shared.cancel(id: alarm.id)
24+
}
25+
26+
private let alarm: Alarm
27+
28+
var id: String {
29+
return alarm.id.uuidString
30+
}
31+
32+
var state: AlarmState {
33+
return convertAlarmState(alarm.state)
34+
}
35+
36+
var postAlert: Double? {
37+
return alarm.countdownDuration?.postAlert
38+
}
39+
40+
var preAlert: Double? {
41+
return alarm.countdownDuration?.preAlert
42+
}
43+
44+
init(alarm: Alarm) {
45+
self.alarm = alarm
46+
}
47+
}

0 commit comments

Comments
 (0)