Skip to content

Commit 1062480

Browse files
committed
Add more notification actions and multiple shield actions
1 parent c119e34 commit 1062480

File tree

4 files changed

+165
-109
lines changed

4 files changed

+165
-109
lines changed

packages/react-native-device-activity/ios/Shared.swift

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,126 @@ func notifyAppWithName(name: String) {
9797
CFNotificationCenterPostNotification(notificationCenter, notificationName, nil, nil, false)
9898
}
9999

100+
func executeGenericAction(action: [String: Any], placeholders: [String: String?], eventKey: String) {
101+
let type = action["type"] as? String
102+
103+
if let sleepBefore = action["sleepBefore"] as? Int {
104+
sleep(ms: sleepBefore)
105+
}
106+
107+
if type == "blockSelection" {
108+
if let familyActivitySelectionId = action["familyActivitySelectionId"] as? String {
109+
if let activitySelection = getFamilyActivitySelectionById(id: familyActivitySelectionId) {
110+
updateShield(
111+
shieldId: action["shieldId"] as? String,
112+
triggeredBy: eventKey,
113+
activitySelectionId: familyActivitySelectionId
114+
)
115+
116+
sleep(ms: 50)
117+
118+
blockSelectedApps(
119+
blockSelection: activitySelection,
120+
triggeredBy: eventKey
121+
)
122+
} else {
123+
logger.log("No familyActivitySelection found with ID: \(familyActivitySelectionId)")
124+
}
125+
}
126+
} else if type == "unblockSelection" {
127+
if let familyActivitySelectionId = action["familyActivitySelectionId"] as? String {
128+
if let activitySelection = getFamilyActivitySelectionById(id: familyActivitySelectionId) {
129+
130+
unblockSelection(
131+
removeSelection: activitySelection,
132+
triggeredBy: eventKey
133+
)
134+
135+
userDefaults?
136+
.removeObject(
137+
forKey: SHIELD_CONFIGURATION_FOR_SELECTION_PREFIX + "_" + familyActivitySelectionId)
138+
}
139+
}
140+
} else if type == "addSelectionToWhitelist" {
141+
if let familyActivitySelectionInput = action["familyActivitySelection"] as? [String: Any] {
142+
let selection = parseActivitySelectionInput(input: familyActivitySelectionInput)
143+
addSelectionToWhitelistAndUpdateBlock(
144+
whitelistSelection: selection,
145+
triggeredBy: eventKey
146+
)
147+
}
148+
} else if type == "removeSelectionFromWhitelist" {
149+
if let familyActivitySelectionInput = action["familyActivitySelection"] as? [String: Any] {
150+
let selection = parseActivitySelectionInput(input: familyActivitySelectionInput)
151+
removeSelectionFromWhitelistAndUpdateBlock(
152+
selection: selection,
153+
triggeredBy: eventKey
154+
)
155+
}
156+
} else if type == "clearWhitelistAndUpdateBlock" {
157+
logger.info("should clearWhitelistAndUpdateBlock")
158+
clearWhitelist()
159+
updateBlock(triggeredBy: eventKey)
160+
logger.info("done")
161+
} else if type == "resetBlocks" {
162+
resetBlocks(triggeredBy: eventKey)
163+
} else if type == "clearWhitelist" {
164+
clearWhitelist()
165+
} else if type == "disableBlockAllMode" {
166+
disableBlockAllMode(triggeredBy: eventKey)
167+
} else if type == "openApp" {
168+
// todo: replace with general string
169+
openUrl(urlString: "device-activity://")
170+
171+
sleep(ms: 1000)
172+
} else if type == "enableBlockAllMode" {
173+
updateShield(
174+
shieldId: action["shieldId"] as? String,
175+
triggeredBy: eventKey,
176+
activitySelectionId: nil
177+
)
178+
179+
// sometimes the shield doesn't pick up the shield config change above, trying a sleep to get around it
180+
sleep(ms: 50)
181+
182+
enableBlockAllMode(triggeredBy: eventKey)
183+
} else if type == "removeAllPendingNotificationRequests" {
184+
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
185+
} else if type == "removePendingNotificationRequests" {
186+
if let identifiers = action["identifiers"] as? [String] {
187+
UNUserNotificationCenter
188+
.current()
189+
.removePendingNotificationRequests(withIdentifiers: identifiers)
190+
}
191+
} else if type == "setBadgeCount" {
192+
let actionWithReplacedPlaceholders = replacePlaceholdersInObject(action, with: placeholders)
193+
if let count = actionWithReplacedPlaceholders["count"] as? Int {
194+
if #available(iOS 16.0, *) {
195+
UNUserNotificationCenter
196+
.current()
197+
.setBadgeCount(count)
198+
}
199+
}
200+
} else if type == "sendNotification" {
201+
if let notification = action["payload"] as? [String: Any] {
202+
sendNotification(contents: notification, placeholders: placeholders)
203+
}
204+
} else if type == "sendHttpRequest" {
205+
if let url = action["url"] as? String {
206+
let config = action["options"] as? [String: Any] ?? [:]
207+
208+
task = sendHttpRequest(with: url, config: config, placeholders: placeholders)
209+
210+
// required for it to have time to trigger before process/callback ends
211+
sleep(ms: 1000)
212+
}
213+
}
214+
215+
if let sleepAfter = action["sleepAfter"] as? Int {
216+
sleep(ms: sleepAfter)
217+
}
218+
}
219+
100220
func sendNotification(contents: [String: Any], placeholders: [String: String?]) {
101221
let content = UNMutableNotificationContent()
102222

packages/react-native-device-activity/src/ReactNativeDeviceActivity.types.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,14 +170,16 @@ export type ShieldActionType =
170170
| "openApp";
171171

172172
export type ShieldAction = {
173-
type: ShieldActionType;
173+
/** @deprecated use actions instead */
174+
type?: ShieldActionType;
174175
delay?: number;
175176
payload?: NotificationPayload;
176177
/**
177178
* defaults to true
178179
*/
179180
onlyFamilySelectionIdsContainingMonitoredActivityNames?: boolean;
180181
behavior: "close" | "defer";
182+
actions?: Action[];
181183
};
182184

183185
export type ShieldActions = {
@@ -267,6 +269,17 @@ export type Action =
267269
body?: Record<string, any>;
268270
headers?: Record<string, string>;
269271
};
272+
} & CommonTypeParams)
273+
| ({
274+
type: "setBadgeCount";
275+
count: number | string; // string for placeholder
276+
} & CommonTypeParams)
277+
| ({
278+
type: "removeAllPendingNotificationRequests";
279+
} & CommonTypeParams)
280+
| ({
281+
type: "removePendingNotificationRequests";
282+
identifiers: string[];
270283
} & CommonTypeParams);
271284

272285
export type DeviceActivityEventRaw = Omit<

packages/react-native-device-activity/targets/ActivityMonitorExtension/DeviceActivityMonitorExtension.swift

Lines changed: 2 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -9,109 +9,9 @@ import DeviceActivity
99
import FamilyControls
1010
import Foundation
1111
import ManagedSettings
12+
import NotificationCenter
1213
import os
1314

14-
func executeAction(action: [String: Any], placeholders: [String: String?], eventKey: String) {
15-
let type = action["type"] as? String
16-
17-
if let sleepBefore = action["sleepBefore"] as? Int {
18-
sleep(ms: sleepBefore)
19-
}
20-
21-
if type == "blockSelection" {
22-
if let familyActivitySelectionId = action["familyActivitySelectionId"] as? String {
23-
if let activitySelection = getFamilyActivitySelectionById(id: familyActivitySelectionId) {
24-
updateShield(
25-
shieldId: action["shieldId"] as? String,
26-
triggeredBy: eventKey,
27-
activitySelectionId: familyActivitySelectionId
28-
)
29-
30-
sleep(ms: 50)
31-
32-
blockSelectedApps(
33-
blockSelection: activitySelection,
34-
triggeredBy: eventKey
35-
)
36-
} else {
37-
logger.log("No familyActivitySelection found with ID: \(familyActivitySelectionId)")
38-
}
39-
}
40-
} else if type == "unblockSelection" {
41-
if let familyActivitySelectionId = action["familyActivitySelectionId"] as? String {
42-
if let activitySelection = getFamilyActivitySelectionById(id: familyActivitySelectionId) {
43-
44-
unblockSelection(
45-
removeSelection: activitySelection,
46-
triggeredBy: eventKey
47-
)
48-
49-
userDefaults?
50-
.removeObject(
51-
forKey: SHIELD_CONFIGURATION_FOR_SELECTION_PREFIX + "_" + familyActivitySelectionId)
52-
}
53-
}
54-
} else if type == "addSelectionToWhitelist" {
55-
if let familyActivitySelectionInput = action["familyActivitySelection"] as? [String: Any] {
56-
let selection = parseActivitySelectionInput(input: familyActivitySelectionInput)
57-
addSelectionToWhitelistAndUpdateBlock(
58-
whitelistSelection: selection,
59-
triggeredBy: eventKey
60-
)
61-
}
62-
} else if type == "removeSelectionFromWhitelist" {
63-
if let familyActivitySelectionInput = action["familyActivitySelection"] as? [String: Any] {
64-
let selection = parseActivitySelectionInput(input: familyActivitySelectionInput)
65-
removeSelectionFromWhitelistAndUpdateBlock(
66-
selection: selection,
67-
triggeredBy: eventKey
68-
)
69-
}
70-
} else if type == "clearWhitelistAndUpdateBlock" {
71-
clearWhitelist()
72-
updateBlock(triggeredBy: eventKey)
73-
} else if type == "resetBlocks" {
74-
resetBlocks(triggeredBy: eventKey)
75-
} else if type == "clearWhitelist" {
76-
clearWhitelist()
77-
} else if type == "disableBlockAllMode" {
78-
disableBlockAllMode(triggeredBy: eventKey)
79-
} else if type == "openApp" {
80-
// todo: replace with general string
81-
openUrl(urlString: "device-activity://")
82-
83-
sleep(ms: 1000)
84-
} else if type == "enableBlockAllMode" {
85-
updateShield(
86-
shieldId: action["shieldId"] as? String,
87-
triggeredBy: eventKey,
88-
activitySelectionId: nil
89-
)
90-
91-
// sometimes the shield doesn't pick up the shield config change above, trying a sleep to get around it
92-
sleep(ms: 50)
93-
94-
enableBlockAllMode(triggeredBy: eventKey)
95-
} else if type == "sendNotification" {
96-
if let notification = action["payload"] as? [String: Any] {
97-
sendNotification(contents: notification, placeholders: placeholders)
98-
}
99-
} else if type == "sendHttpRequest" {
100-
if let url = action["url"] as? String {
101-
let config = action["options"] as? [String: Any] ?? [:]
102-
103-
task = sendHttpRequest(with: url, config: config, placeholders: placeholders)
104-
105-
// required for it to have time to trigger before process/callback ends
106-
sleep(ms: 1000)
107-
}
108-
}
109-
110-
if let sleepAfter = action["sleepAfter"] as? Int {
111-
sleep(ms: sleepAfter)
112-
}
113-
}
114-
11515
class DeviceActivityMonitorExtension: DeviceActivityMonitor {
11616
override func intervalDidStart(for activity: DeviceActivityName) {
11717
super.intervalDidStart(for: activity)
@@ -200,7 +100,7 @@ class DeviceActivityMonitorExtension: DeviceActivityMonitor {
200100
callbackName: callbackName,
201101
eventName: eventName
202102
) {
203-
executeAction(
103+
executeGenericAction(
204104
action: action,
205105
placeholders: placeholders,
206106
eventKey: key

packages/react-native-device-activity/targets/ShieldAction/ShieldActionExtension.swift

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,19 @@ import FamilyControls
99
import ManagedSettings
1010
import UIKit
1111

12-
func handleAction(
12+
func handleShieldAction(
1313
configForSelectedAction: [String: Any],
14+
placeholders: [String: String?],
1415
applicationToken: ApplicationToken?,
1516
webdomainToken: WebDomainToken?,
1617
categoryToken: ActivityCategoryToken?
1718
) -> ShieldActionResponse {
1819
logger.log("handleAction")
19-
if let type = configForSelectedAction["type"] as? String {
20+
if let actions = configForSelectedAction["actions"] as? [[String: Any]] {
21+
for action in actions {
22+
executeGenericAction(action: action, placeholders: placeholders, eventKey: "shieldAction")
23+
}
24+
} else if let type = configForSelectedAction["type"] as? String {
2025
logger.log("type: \(type)")
2126
if type == "disableBlockAllMode" {
2227
disableBlockAllMode(triggeredBy: "shieldAction")
@@ -120,7 +125,6 @@ func handleAction(
120125
}
121126

122127
if type == "whitelistCurrent" {
123-
logger.log("whitelistCurrent!!!")
124128
var selection = getCurrentWhitelist()
125129

126130
if let applicationToken = applicationToken {
@@ -167,10 +171,29 @@ func handleAction(
167171
webDomainToken: webdomainToken,
168172
categoryToken: categoryToken
169173
) {
170-
let actionKey = action == .primaryButtonPressed ? "primary" : "secondary"
171-
if let configForSelectedAction = shieldActionConfig[actionKey] as? [String: Any] {
172-
let response = handleAction(
174+
let actionButton = action == .primaryButtonPressed ? "primary" : "secondary"
175+
let familyActivitySelectionId = getPossibleFamilyActivitySelectionIds(
176+
applicationToken: applicationToken,
177+
webDomainToken: webdomainToken,
178+
categoryToken: categoryToken,
179+
onlyFamilySelectionIdsContainingMonitoredActivityNames: true,
180+
sortByGranularity: true
181+
).first
182+
if let configForSelectedAction = shieldActionConfig[actionButton] as? [String: Any] {
183+
let placeholders: [String: String?] = [
184+
"action": actionButton,
185+
"applicationName": applicationToken != nil
186+
? Application(token: applicationToken!).localizedDisplayName : nil,
187+
"webDomain": webdomainToken != nil
188+
? WebDomain(
189+
token: webdomainToken!
190+
).domain : nil,
191+
"familyActivitySelectionId": familyActivitySelectionId?.id
192+
]
193+
194+
let response = handleShieldAction(
173195
configForSelectedAction: configForSelectedAction,
196+
placeholders: placeholders,
174197
applicationToken: applicationToken,
175198
webdomainToken: webdomainToken,
176199
categoryToken: categoryToken

0 commit comments

Comments
 (0)