Skip to content

Commit e5a9f86

Browse files
authored
Merge pull request #15 from kingstinct/add-alertkit
Add alertkit
2 parents f34dbdd + cbacecd commit e5a9f86

23 files changed

Lines changed: 978 additions & 34 deletions

.changeset/fuzzy-turkeys-cross.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@kingstinct/react-native-activity-kit": patch
3+
---
4+
5+
Add alertkit

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ jobs:
145145

146146
build-ios:
147147
# Only run on macOS since we need Xcode
148-
runs-on: macos-15
148+
runs-on: macos-26
149149
timeout-minutes: 50
150150

151151
steps:

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,5 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
3333
.DS_Store
3434

3535

36-
generated
36+
generated
37+
/packages/react-native-alarm-kit/android

apps/activity-kit-example/app.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
"userInterfaceStyle": "automatic",
1010
"newArchEnabled": true,
1111
"ios": {
12+
"infoPlist": {
13+
"NSAlarmKitUsageDescription": "This app uses ActivityKit to demonstrate Live Activities."
14+
},
1215
"supportsTablet": true,
1316
"bundleIdentifier": "com.robertherber.activity-kit-example"
1417
},
@@ -40,7 +43,7 @@
4043
"expo-build-properties",
4144
{
4245
"ios": {
43-
"deploymentTarget": "18.0"
46+
"deploymentTarget": "26.0"
4447
}
4548
}
4649
],

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

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ActivityKit } from '@kingstinct/react-native-activity-kit' // Importing the ActivityKit module'
1+
import { ActivityKit, AlarmKit } from '@kingstinct/react-native-activity-kit' // Importing the ActivityKit module'
22
import { Image } from 'expo-image'
33
import * as Notifications from 'expo-notifications'
44
import { useState } from 'react'
@@ -50,6 +50,35 @@ export default function HomeScreen() {
5050
}
5151
title="Push permissions"
5252
></Button>
53+
<Button
54+
onPress={() => AlarmKit.requestAuthorization()}
55+
title="AlarmKit permissions"
56+
></Button>
57+
58+
<Button
59+
onPress={() => {
60+
const countdownDurationInSeconds = 10
61+
AlarmKit.createCountdown({
62+
tintColor: { red: 255, green: 0, blue: 0, alpha: 0.5 },
63+
alert: {
64+
title: 'Pomodoro focus time over!',
65+
stopButton: {
66+
text: 'Ok, cool!',
67+
systemImageName: 'stop.fill',
68+
textColor: { red: 0, green: 255, blue: 0 },
69+
},
70+
},
71+
countdown: {
72+
title: 'Pomodoro focus time!',
73+
},
74+
preAlert: countdownDurationInSeconds,
75+
metadata: {
76+
timerFiringAt: Date.now() + countdownDurationInSeconds * 1000,
77+
},
78+
})
79+
}}
80+
title="Start countdown"
81+
></Button>
5382

5483
{latestActivityId ? (
5584
<Button

apps/activity-kit-example/targets/widget/WidgetLiveActivity.swift

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,62 @@
11
import ActivityKit
2+
import AlarmKit
23
import WidgetKit
34
import SwiftUI
45
import NitroActivityKitCore
56

67
struct WidgetLiveActivity: Widget {
78
var body: some WidgetConfiguration {
8-
ActivityConfiguration(for: ActivityKitModuleAttributes.self) { context in
9-
// Lock screen/banner UI goes here
10-
VStack {
11-
Text("Hello \(context.state.getAsString("name"))")
12-
Text(context.state.getDate("startedTimerAt") ?? Date(), style: .timer)
9+
ActivityConfiguration(for: ActivityKitModuleAttributes.self) { context in
10+
// Changed VStack to mimic built-in timer style
11+
ZStack {
12+
RoundedRectangle(cornerRadius: 18, style: .continuous)
13+
.fill(.ultraThinMaterial)
14+
15+
HStack(spacing: 8) {
16+
Text("Timer")
17+
.font(.subheadline.weight(.medium))
18+
.foregroundColor(.secondary)
19+
20+
Text(context.state.getDate("startedTimerAt") ?? Date(), style: .timer)
21+
.font(.system(size: 36, weight: .medium, design: .monospaced))
22+
.foregroundColor(.orange)
23+
.minimumScaleFactor(0.5)
24+
.lineLimit(1)
25+
}
26+
.padding()
27+
.frame(maxWidth: .infinity, alignment: .leading)
1328
}
14-
.activityBackgroundTint(Color.cyan)
15-
.activitySystemActionForegroundColor(Color.black)
29+
.clipShape(RoundedRectangle(cornerRadius: 18, style: .continuous))
30+
.padding(16)
31+
.activityBackgroundTint(Color.black.opacity(0.1))
32+
.containerBackground(.ultraThinMaterial, for: .widget)
33+
// .activitySystemActionForegroundColor(Color.black)
1634

1735
} dynamicIsland: { context in
1836
DynamicIsland {
19-
// Expanded UI goes here. Compose the expanded UI through
20-
// various regions, like leading/trailing/center/bottom
21-
DynamicIslandExpandedRegion(.leading) {
22-
Text("Leading")
23-
}
24-
DynamicIslandExpandedRegion(.trailing) {
25-
Text("Trailing")
26-
}
27-
DynamicIslandExpandedRegion(.bottom) {
28-
Text("Bottom \(context.state.getString("name"))")
29-
// more content
37+
DynamicIslandExpandedRegion(.leading) { Text("Leading") }
38+
DynamicIslandExpandedRegion(.trailing) { Text("Trailing") }
39+
DynamicIslandExpandedRegion(.center) {
40+
ZStack {
41+
RoundedRectangle(cornerRadius: 18, style: .continuous)
42+
.fill(.ultraThinMaterial)
43+
44+
HStack(spacing: 8) {
45+
Text("Timer")
46+
.font(.subheadline.weight(.medium))
47+
.foregroundColor(.secondary)
48+
49+
Text(context.state.getDate("startedTimerAt") ?? Date(), style: .timer)
50+
.font(.system(size: 36, weight: .medium, design: .monospaced))
51+
.foregroundColor(.orange)
52+
.minimumScaleFactor(0.5)
53+
.lineLimit(1)
54+
}
55+
.padding()
56+
.frame(maxWidth: .infinity, alignment: .leading)
57+
}
58+
.clipShape(RoundedRectangle(cornerRadius: 18, style: .continuous))
59+
.padding(16)
3060
}
3161
} compactLeading: {
3262
Text("L")
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import ActivityKit
2+
import AlarmKit
3+
import WidgetKit
4+
import SwiftUI
5+
import NitroActivityKitCore
6+
7+
@available(iOS 26.0, *)
8+
func getAlarmTimeInterval() -> Date {
9+
do {
10+
let addInterval = try AlarmManager.shared.alarms.first(where: { alarm in
11+
alarm.state == .countdown
12+
})?.countdownDuration?.preAlert ?? 10
13+
14+
let now = Date(timeIntervalSinceNow: addInterval)
15+
16+
// now.addTimeInterval(addInterval)
17+
18+
return now
19+
} catch {
20+
print("AlarmKit: failed to fetch countdown alarm — \(error)")
21+
return Date(timeIntervalSinceNow: 20)
22+
}
23+
24+
}
25+
26+
@available(iOS 26.0, *)
27+
struct WidgetLiveActivityAlarm: Widget {
28+
29+
var body: some WidgetConfiguration {
30+
31+
ActivityConfiguration(
32+
for: AlarmAttributes<GenericDictionaryAlarmStruct>.self) { context in
33+
// Changed VStack to mimic built-in timer style
34+
ZStack {
35+
/*RoundedRectangle(cornerRadius: 18, style: .continuous)
36+
.fill(.ultraThinMaterial)*/
37+
// context.state.mode <- for checking state
38+
HStack(spacing: 8) {
39+
Text("Timer")
40+
.font(.subheadline.weight(.medium))
41+
// .glassEffect(.tint(.orange))
42+
.foregroundColor(.secondary)
43+
44+
Text(context.attributes.metadata?.getDate("timerFiringAt") ?? Date(),
45+
style: .timer)
46+
.font(.system(size: 36, weight: .medium, design: .monospaced))
47+
.foregroundColor(.orange)
48+
.minimumScaleFactor(0.5)
49+
// .glassEffect(.tint(.orange))
50+
.lineLimit(1)
51+
}
52+
.padding()
53+
.activityBackgroundTint(Color.black.opacity(0.1))
54+
.frame(maxWidth: .infinity, alignment: .leading)
55+
}
56+
.clipShape(RoundedRectangle(cornerRadius: 18, style: .continuous))
57+
.padding(16)
58+
.activityBackgroundTint(Color.black.opacity(0.1))
59+
.containerBackground(.ultraThinMaterial, for: .widget)
60+
// .activitySystemActionForegroundColor(Color.black)
61+
62+
} dynamicIsland: { context in
63+
DynamicIsland {
64+
DynamicIslandExpandedRegion(.leading) {
65+
Text("Leading")
66+
.glassEffect()
67+
}
68+
DynamicIslandExpandedRegion(.trailing) {
69+
Text("Trailing")
70+
.glassEffect()
71+
}
72+
DynamicIslandExpandedRegion(.center) {
73+
ZStack {
74+
RoundedRectangle(cornerRadius: 18, style: .continuous)
75+
.fill(.ultraThinMaterial)
76+
77+
HStack(spacing: 8) {
78+
Text("Timer")
79+
.font(.subheadline.weight(.medium))
80+
.foregroundColor(.secondary)
81+
.glassEffect()
82+
Text(context.attributes.metadata?.getDate("timerFiringAt") ?? Date(),
83+
style: .timer,
84+
)
85+
.font(.system(size: 36, weight: .medium, design: .monospaced))
86+
.foregroundColor(.orange)
87+
.minimumScaleFactor(0.5)
88+
.lineLimit(1)
89+
.glassEffect()
90+
}
91+
.padding()
92+
.frame(maxWidth: .infinity, alignment: .leading)
93+
}
94+
.clipShape(RoundedRectangle(cornerRadius: 18, style: .continuous))
95+
.padding(16)
96+
}
97+
} compactLeading: {
98+
Text("L")
99+
.glassEffect()
100+
} compactTrailing: {
101+
Text("Hello")
102+
.glassEffect()
103+
} minimal: {
104+
Text("Hello")
105+
.glassEffect()
106+
}
107+
.widgetURL(URL(string: "https://www.expo.dev"))
108+
.keylineTint(Color.red)
109+
}
110+
}
111+
}

apps/activity-kit-example/targets/widget/index.swift

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,21 @@ import SwiftUI
33

44
@main
55
struct exportWidgets: WidgetBundle {
6-
var body: some Widget {
7-
// Export widgets here
8-
widget()
9-
widgetControl()
10-
WidgetLiveActivity()
11-
}
6+
@WidgetBundleBuilder
7+
var body: some Widget {
8+
widgets()
9+
}
10+
11+
private func widgets() -> some Widget {
12+
if #available(iOS 26, *) {
13+
return WidgetBundleBuilder.buildBlock( widget(),
14+
// widgetControl(),
15+
WidgetLiveActivity(),
16+
WidgetLiveActivityAlarm())
17+
} else {
18+
return WidgetBundleBuilder.buildBlock( widget(),
19+
// widgetControl(),
20+
WidgetLiveActivity())
21+
}
22+
}
1223
}

apps/activity-kit-example/tsconfig.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@
99
],
1010
"@kingstinct/react-native-activity-kit/*": [
1111
"./packages/react-native-activity-kit/src/*"
12-
]
12+
],
13+
"react-native-alarm-kit": [
14+
"./packages/react-native-alarm-kit/src/index.ts"
15+
],
16+
"react-native-alarm-kit/*": ["./packages/react-native-alarm-kit/src/*"]
1317
}
1418
},
1519
"include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"]

0 commit comments

Comments
 (0)