palmier 0.7.2 → 0.7.4
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 +43 -22
- package/dist/commands/serve.js +14 -1
- package/dist/device-capabilities.d.ts +9 -0
- package/dist/device-capabilities.js +36 -0
- package/dist/mcp-handler.js +4 -1
- package/dist/mcp-tools.js +414 -7
- package/dist/pwa/assets/{index-C6Lz09EY.css → index-B-ByUHPS.css} +1 -1
- package/dist/pwa/assets/index-BirmfPUC.js +118 -0
- package/dist/pwa/assets/{web-HDs03L2B.js → web-Dc9-IiRD.js} +1 -1
- package/dist/pwa/assets/{web-CBI458eN.js → web-_b3Dvcvz.js} +1 -1
- package/dist/pwa/index.html +2 -2
- package/dist/pwa/service-worker.js +1 -1
- package/dist/rpc-handler.js +19 -4
- package/dist/sms-store.d.ts +11 -0
- package/dist/sms-store.js +19 -0
- package/dist/transports/http-transport.js +16 -1
- package/package.json +1 -1
- package/palmier-server/README.md +11 -3
- package/palmier-server/pwa/src/App.css +3 -0
- package/palmier-server/pwa/src/components/HostMenu.tsx +465 -0
- package/palmier-server/pwa/src/constants.ts +1 -1
- package/palmier-server/server/src/index.ts +306 -0
- package/palmier-server/server/src/routes/device.ts +168 -0
- package/palmier-server/spec.md +32 -3
- package/src/commands/serve.ts +14 -1
- package/src/device-capabilities.ts +55 -0
- package/src/mcp-handler.ts +4 -1
- package/src/mcp-tools.ts +473 -7
- package/src/rpc-handler.ts +19 -4
- package/src/sms-store.ts +28 -0
- package/src/transports/http-transport.ts +16 -1
- package/test/agent-instructions.test.ts +1 -1
- package/dist/location-device.d.ts +0 -8
- package/dist/location-device.js +0 -32
- package/dist/pwa/assets/index-DLxrL0hR.js +0 -118
- package/src/location-device.ts +0 -35
|
@@ -15,9 +15,74 @@ interface LocationPermissionPlugin {
|
|
|
15
15
|
check(): Promise<LocationPermissionResult>;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
interface NotificationListenerResult {
|
|
19
|
+
enabled: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface NotificationListenerPlugin {
|
|
23
|
+
request(): Promise<NotificationListenerResult>;
|
|
24
|
+
check(): Promise<NotificationListenerResult>;
|
|
25
|
+
}
|
|
26
|
+
|
|
18
27
|
const LocationPermission = Capacitor.isNativePlatform()
|
|
19
28
|
? registerPlugin<LocationPermissionPlugin>("LocationPermission")
|
|
20
29
|
: null;
|
|
30
|
+
|
|
31
|
+
interface SmsPermissionResult {
|
|
32
|
+
granted: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface SmsPermissionPlugin {
|
|
36
|
+
request(): Promise<SmsPermissionResult>;
|
|
37
|
+
check(): Promise<SmsPermissionResult>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface ContactsPermissionResult {
|
|
41
|
+
granted: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface ContactsPermissionPlugin {
|
|
45
|
+
request(): Promise<ContactsPermissionResult>;
|
|
46
|
+
check(): Promise<ContactsPermissionResult>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface CalendarPermissionResult {
|
|
50
|
+
granted: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface CalendarPermissionPlugin {
|
|
54
|
+
request(): Promise<CalendarPermissionResult>;
|
|
55
|
+
check(): Promise<CalendarPermissionResult>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface DndAccessResult {
|
|
59
|
+
enabled: boolean;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface DndAccessPlugin {
|
|
63
|
+
request(): Promise<DndAccessResult>;
|
|
64
|
+
check(): Promise<DndAccessResult>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const NotificationListener = Capacitor.isNativePlatform()
|
|
68
|
+
? registerPlugin<NotificationListenerPlugin>("NotificationListener")
|
|
69
|
+
: null;
|
|
70
|
+
|
|
71
|
+
const SmsPermission = Capacitor.isNativePlatform()
|
|
72
|
+
? registerPlugin<SmsPermissionPlugin>("SmsPermission")
|
|
73
|
+
: null;
|
|
74
|
+
|
|
75
|
+
const ContactsPermission = Capacitor.isNativePlatform()
|
|
76
|
+
? registerPlugin<ContactsPermissionPlugin>("ContactsPermission")
|
|
77
|
+
: null;
|
|
78
|
+
|
|
79
|
+
const CalendarPermission = Capacitor.isNativePlatform()
|
|
80
|
+
? registerPlugin<CalendarPermissionPlugin>("CalendarPermission")
|
|
81
|
+
: null;
|
|
82
|
+
|
|
83
|
+
const DndAccess = Capacitor.isNativePlatform()
|
|
84
|
+
? registerPlugin<DndAccessPlugin>("DndAccess")
|
|
85
|
+
: null;
|
|
21
86
|
import { useHostStore } from "../contexts/HostStoreContext";
|
|
22
87
|
import { useMediaQuery } from "../hooks/useMediaQuery";
|
|
23
88
|
|
|
@@ -44,6 +109,20 @@ export default function HostMenu({ daemonVersion, locationClientToken, activeCli
|
|
|
44
109
|
const [renameValue, setRenameValue] = useState("");
|
|
45
110
|
const [confirmingDeleteId, setConfirmingDeleteId] = useState<string | null>(null);
|
|
46
111
|
const [togglingLocation, setTogglingLocation] = useState(false);
|
|
112
|
+
const [notificationListenerEnabled, setNotificationListenerEnabled] = useState(false);
|
|
113
|
+
const [togglingNotificationListener, setTogglingNotificationListener] = useState(false);
|
|
114
|
+
const [smsEnabled, setSmsEnabled] = useState(false);
|
|
115
|
+
const [togglingSms, setTogglingSms] = useState(false);
|
|
116
|
+
const [contactsEnabled, setContactsEnabled] = useState(false);
|
|
117
|
+
const [togglingContacts, setTogglingContacts] = useState(false);
|
|
118
|
+
const [calendarEnabled, setCalendarEnabled] = useState(false);
|
|
119
|
+
const [togglingCalendar, setTogglingCalendar] = useState(false);
|
|
120
|
+
const [dndEnabled, setDndEnabled] = useState(false);
|
|
121
|
+
const [togglingDnd, setTogglingDnd] = useState(false);
|
|
122
|
+
const [alarmEnabled, setAlarmEnabled] = useState(false);
|
|
123
|
+
const [togglingAlarm, setTogglingAlarm] = useState(false);
|
|
124
|
+
const [batteryEnabled, setBatteryEnabled] = useState(false);
|
|
125
|
+
const [togglingBattery, setTogglingBattery] = useState(false);
|
|
47
126
|
|
|
48
127
|
const locationEnabled = !!(activeClientToken && locationClientToken === activeClientToken);
|
|
49
128
|
|
|
@@ -72,6 +151,308 @@ export default function HostMenu({ daemonVersion, locationClientToken, activeCli
|
|
|
72
151
|
return () => { listener.then((h) => h.remove()); };
|
|
73
152
|
}, [locationEnabled, activeClientToken]);
|
|
74
153
|
|
|
154
|
+
// Sync notification listener toggle with system state — on mount and when app resumes
|
|
155
|
+
useEffect(() => {
|
|
156
|
+
if (!isNative || !NotificationListener) return;
|
|
157
|
+
|
|
158
|
+
function syncNotificationListenerState() {
|
|
159
|
+
Promise.all([
|
|
160
|
+
NotificationListener!.check(),
|
|
161
|
+
Preferences.get({ key: "notificationListenerEnabled" }),
|
|
162
|
+
]).then(([{ enabled: systemEnabled }, { value: prefValue }]) => {
|
|
163
|
+
// Enabled only if both system permission is granted AND user toggled on
|
|
164
|
+
setNotificationListenerEnabled(systemEnabled && prefValue !== "false");
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
syncNotificationListenerState();
|
|
169
|
+
|
|
170
|
+
const listener = CapApp.addListener("resume", () => {
|
|
171
|
+
syncNotificationListenerState();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
return () => { listener.then((h) => h.remove()); };
|
|
175
|
+
}, []);
|
|
176
|
+
|
|
177
|
+
async function handleNotificationListenerToggle() {
|
|
178
|
+
if (!NotificationListener || !request) return;
|
|
179
|
+
setTogglingNotificationListener(true);
|
|
180
|
+
try {
|
|
181
|
+
if (notificationListenerEnabled) {
|
|
182
|
+
await Preferences.set({ key: "notificationListenerEnabled", value: "false" });
|
|
183
|
+
await request("device.capability.disable", { capability: "notifications" });
|
|
184
|
+
setNotificationListenerEnabled(false);
|
|
185
|
+
} else {
|
|
186
|
+
const { enabled: systemEnabled } = await NotificationListener.check();
|
|
187
|
+
if (!systemEnabled) {
|
|
188
|
+
const result = await NotificationListener.request();
|
|
189
|
+
if (!result.enabled) return;
|
|
190
|
+
}
|
|
191
|
+
const { value: fcmToken } = await Preferences.get({ key: "fcmToken" });
|
|
192
|
+
if (!fcmToken) { console.warn("No FCM token available"); return; }
|
|
193
|
+
await Preferences.set({ key: "notificationListenerEnabled", value: "true" });
|
|
194
|
+
await request("device.capability.enable", { capability: "notifications", fcmToken });
|
|
195
|
+
setNotificationListenerEnabled(true);
|
|
196
|
+
}
|
|
197
|
+
} catch (err) {
|
|
198
|
+
console.error("Failed to toggle notification listener:", err);
|
|
199
|
+
} finally {
|
|
200
|
+
setTogglingNotificationListener(false);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Sync SMS toggle with permission state — on mount and when app resumes
|
|
205
|
+
useEffect(() => {
|
|
206
|
+
if (!isNative || !SmsPermission) return;
|
|
207
|
+
|
|
208
|
+
function syncSmsState() {
|
|
209
|
+
Promise.all([
|
|
210
|
+
SmsPermission!.check(),
|
|
211
|
+
Preferences.get({ key: "smsListenerEnabled" }),
|
|
212
|
+
]).then(([{ granted }, { value: prefValue }]) => {
|
|
213
|
+
setSmsEnabled(granted && prefValue !== "false");
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
syncSmsState();
|
|
218
|
+
|
|
219
|
+
const listener = CapApp.addListener("resume", () => {
|
|
220
|
+
syncSmsState();
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
return () => { listener.then((h) => h.remove()); };
|
|
224
|
+
}, []);
|
|
225
|
+
|
|
226
|
+
async function handleSmsToggle() {
|
|
227
|
+
if (!SmsPermission || !request) return;
|
|
228
|
+
setTogglingSms(true);
|
|
229
|
+
try {
|
|
230
|
+
if (smsEnabled) {
|
|
231
|
+
await Preferences.set({ key: "smsListenerEnabled", value: "false" });
|
|
232
|
+
await request("device.capability.disable", { capability: "sms" });
|
|
233
|
+
setSmsEnabled(false);
|
|
234
|
+
} else {
|
|
235
|
+
const { granted } = await SmsPermission.check();
|
|
236
|
+
if (!granted) {
|
|
237
|
+
const result = await SmsPermission.request();
|
|
238
|
+
if (!result.granted) return;
|
|
239
|
+
}
|
|
240
|
+
const { value: fcmToken } = await Preferences.get({ key: "fcmToken" });
|
|
241
|
+
if (!fcmToken) { console.warn("No FCM token available"); return; }
|
|
242
|
+
await Preferences.set({ key: "smsListenerEnabled", value: "true" });
|
|
243
|
+
await request("device.capability.enable", { capability: "sms", fcmToken });
|
|
244
|
+
setSmsEnabled(true);
|
|
245
|
+
}
|
|
246
|
+
} catch (err) {
|
|
247
|
+
console.error("Failed to toggle SMS access:", err);
|
|
248
|
+
} finally {
|
|
249
|
+
setTogglingSms(false);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Sync contacts toggle with permission state — on mount and when app resumes
|
|
254
|
+
useEffect(() => {
|
|
255
|
+
if (!isNative || !ContactsPermission) return;
|
|
256
|
+
|
|
257
|
+
function syncContactsState() {
|
|
258
|
+
Promise.all([
|
|
259
|
+
ContactsPermission!.check(),
|
|
260
|
+
Preferences.get({ key: "contactsAccessEnabled" }),
|
|
261
|
+
]).then(([{ granted }, { value: prefValue }]) => {
|
|
262
|
+
setContactsEnabled(granted && prefValue !== "false");
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
syncContactsState();
|
|
267
|
+
|
|
268
|
+
const listener = CapApp.addListener("resume", () => {
|
|
269
|
+
syncContactsState();
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
return () => { listener.then((h) => h.remove()); };
|
|
273
|
+
}, []);
|
|
274
|
+
|
|
275
|
+
async function handleContactsToggle() {
|
|
276
|
+
if (!ContactsPermission || !request) return;
|
|
277
|
+
setTogglingContacts(true);
|
|
278
|
+
try {
|
|
279
|
+
if (contactsEnabled) {
|
|
280
|
+
await Preferences.set({ key: "contactsAccessEnabled", value: "false" });
|
|
281
|
+
await request("device.capability.disable", { capability: "contacts" });
|
|
282
|
+
setContactsEnabled(false);
|
|
283
|
+
} else {
|
|
284
|
+
const { granted } = await ContactsPermission.check();
|
|
285
|
+
if (!granted) {
|
|
286
|
+
const result = await ContactsPermission.request();
|
|
287
|
+
if (!result.granted) return;
|
|
288
|
+
}
|
|
289
|
+
const { value: fcmToken } = await Preferences.get({ key: "fcmToken" });
|
|
290
|
+
if (!fcmToken) { console.warn("No FCM token available"); return; }
|
|
291
|
+
await Preferences.set({ key: "contactsAccessEnabled", value: "true" });
|
|
292
|
+
await request("device.capability.enable", { capability: "contacts", fcmToken });
|
|
293
|
+
setContactsEnabled(true);
|
|
294
|
+
}
|
|
295
|
+
} catch (err) {
|
|
296
|
+
console.error("Failed to toggle contacts access:", err);
|
|
297
|
+
} finally {
|
|
298
|
+
setTogglingContacts(false);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Sync calendar toggle with permission state — on mount and when app resumes
|
|
303
|
+
useEffect(() => {
|
|
304
|
+
if (!isNative || !CalendarPermission) return;
|
|
305
|
+
|
|
306
|
+
function syncCalendarState() {
|
|
307
|
+
Promise.all([
|
|
308
|
+
CalendarPermission!.check(),
|
|
309
|
+
Preferences.get({ key: "calendarAccessEnabled" }),
|
|
310
|
+
]).then(([{ granted }, { value: prefValue }]) => {
|
|
311
|
+
setCalendarEnabled(granted && prefValue !== "false");
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
syncCalendarState();
|
|
316
|
+
|
|
317
|
+
const listener = CapApp.addListener("resume", () => {
|
|
318
|
+
syncCalendarState();
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
return () => { listener.then((h) => h.remove()); };
|
|
322
|
+
}, []);
|
|
323
|
+
|
|
324
|
+
async function handleCalendarToggle() {
|
|
325
|
+
if (!CalendarPermission || !request) return;
|
|
326
|
+
setTogglingCalendar(true);
|
|
327
|
+
try {
|
|
328
|
+
if (calendarEnabled) {
|
|
329
|
+
await Preferences.set({ key: "calendarAccessEnabled", value: "false" });
|
|
330
|
+
await request("device.capability.disable", { capability: "calendar" });
|
|
331
|
+
setCalendarEnabled(false);
|
|
332
|
+
} else {
|
|
333
|
+
const { granted } = await CalendarPermission.check();
|
|
334
|
+
if (!granted) {
|
|
335
|
+
const result = await CalendarPermission.request();
|
|
336
|
+
if (!result.granted) return;
|
|
337
|
+
}
|
|
338
|
+
const { value: fcmToken } = await Preferences.get({ key: "fcmToken" });
|
|
339
|
+
if (!fcmToken) { console.warn("No FCM token available"); return; }
|
|
340
|
+
await Preferences.set({ key: "calendarAccessEnabled", value: "true" });
|
|
341
|
+
await request("device.capability.enable", { capability: "calendar", fcmToken });
|
|
342
|
+
setCalendarEnabled(true);
|
|
343
|
+
}
|
|
344
|
+
} catch (err) {
|
|
345
|
+
console.error("Failed to toggle calendar access:", err);
|
|
346
|
+
} finally {
|
|
347
|
+
setTogglingCalendar(false);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Sync DND access toggle with system state — on mount and when app resumes
|
|
352
|
+
useEffect(() => {
|
|
353
|
+
if (!isNative || !DndAccess) return;
|
|
354
|
+
|
|
355
|
+
function syncDndState() {
|
|
356
|
+
DndAccess!.check().then(({ enabled }) => {
|
|
357
|
+
setDndEnabled(enabled);
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
syncDndState();
|
|
362
|
+
|
|
363
|
+
const listener = CapApp.addListener("resume", () => {
|
|
364
|
+
syncDndState();
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
return () => { listener.then((h) => h.remove()); };
|
|
368
|
+
}, []);
|
|
369
|
+
|
|
370
|
+
async function handleDndToggle() {
|
|
371
|
+
if (!DndAccess || !request) return;
|
|
372
|
+
setTogglingDnd(true);
|
|
373
|
+
try {
|
|
374
|
+
if (dndEnabled) {
|
|
375
|
+
// DND access can only be revoked in system settings, but we unregister from host
|
|
376
|
+
await request("device.capability.disable", { capability: "dnd" });
|
|
377
|
+
setDndEnabled(false);
|
|
378
|
+
} else {
|
|
379
|
+
const { enabled: systemEnabled } = await DndAccess.check();
|
|
380
|
+
if (!systemEnabled) {
|
|
381
|
+
const result = await DndAccess.request();
|
|
382
|
+
if (!result.enabled) return;
|
|
383
|
+
}
|
|
384
|
+
const { value: fcmToken } = await Preferences.get({ key: "fcmToken" });
|
|
385
|
+
if (!fcmToken) { console.warn("No FCM token available"); return; }
|
|
386
|
+
await request("device.capability.enable", { capability: "dnd", fcmToken });
|
|
387
|
+
setDndEnabled(true);
|
|
388
|
+
}
|
|
389
|
+
} catch (err) {
|
|
390
|
+
console.error("Failed to toggle DND access:", err);
|
|
391
|
+
} finally {
|
|
392
|
+
setTogglingDnd(false);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Sync alarm toggle — no permission needed, just device registration
|
|
397
|
+
useEffect(() => {
|
|
398
|
+
if (!isNative) return;
|
|
399
|
+
Preferences.get({ key: "alertAccessEnabled" }).then(({ value }) => {
|
|
400
|
+
setAlarmEnabled(value === "true");
|
|
401
|
+
});
|
|
402
|
+
}, []);
|
|
403
|
+
|
|
404
|
+
async function handleAlarmToggle() {
|
|
405
|
+
if (!request) return;
|
|
406
|
+
setTogglingAlarm(true);
|
|
407
|
+
try {
|
|
408
|
+
if (alarmEnabled) {
|
|
409
|
+
await Preferences.set({ key: "alertAccessEnabled", value: "false" });
|
|
410
|
+
await request("device.capability.disable", { capability: "alert" });
|
|
411
|
+
setAlarmEnabled(false);
|
|
412
|
+
} else {
|
|
413
|
+
const { value: fcmToken } = await Preferences.get({ key: "fcmToken" });
|
|
414
|
+
if (!fcmToken) { console.warn("No FCM token available"); return; }
|
|
415
|
+
await Preferences.set({ key: "alertAccessEnabled", value: "true" });
|
|
416
|
+
await request("device.capability.enable", { capability: "alert", fcmToken });
|
|
417
|
+
setAlarmEnabled(true);
|
|
418
|
+
}
|
|
419
|
+
} catch (err) {
|
|
420
|
+
console.error("Failed to toggle alarm access:", err);
|
|
421
|
+
} finally {
|
|
422
|
+
setTogglingAlarm(false);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Sync battery toggle — no permission needed, just device registration
|
|
427
|
+
useEffect(() => {
|
|
428
|
+
if (!isNative) return;
|
|
429
|
+
Preferences.get({ key: "batteryAccessEnabled" }).then(({ value }) => {
|
|
430
|
+
setBatteryEnabled(value === "true");
|
|
431
|
+
});
|
|
432
|
+
}, []);
|
|
433
|
+
|
|
434
|
+
async function handleBatteryToggle() {
|
|
435
|
+
if (!request) return;
|
|
436
|
+
setTogglingBattery(true);
|
|
437
|
+
try {
|
|
438
|
+
if (batteryEnabled) {
|
|
439
|
+
await Preferences.set({ key: "batteryAccessEnabled", value: "false" });
|
|
440
|
+
await request("device.capability.disable", { capability: "battery" });
|
|
441
|
+
setBatteryEnabled(false);
|
|
442
|
+
} else {
|
|
443
|
+
const { value: fcmToken } = await Preferences.get({ key: "fcmToken" });
|
|
444
|
+
if (!fcmToken) { console.warn("No FCM token available"); return; }
|
|
445
|
+
await Preferences.set({ key: "batteryAccessEnabled", value: "true" });
|
|
446
|
+
await request("device.capability.enable", { capability: "battery", fcmToken });
|
|
447
|
+
setBatteryEnabled(true);
|
|
448
|
+
}
|
|
449
|
+
} catch (err) {
|
|
450
|
+
console.error("Failed to toggle battery access:", err);
|
|
451
|
+
} finally {
|
|
452
|
+
setTogglingBattery(false);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
75
456
|
async function handleLocationToggle() {
|
|
76
457
|
if (!request) return;
|
|
77
458
|
setTogglingLocation(true);
|
|
@@ -294,6 +675,90 @@ export default function HostMenu({ daemonVersion, locationClientToken, activeCli
|
|
|
294
675
|
<span className="toggle-switch-thumb" />
|
|
295
676
|
</button>
|
|
296
677
|
</label>
|
|
678
|
+
<label className="drawer-toggle">
|
|
679
|
+
<span className="drawer-toggle-label">Notification Access</span>
|
|
680
|
+
<button
|
|
681
|
+
className={`toggle-switch ${notificationListenerEnabled ? "toggle-switch-on" : ""}`}
|
|
682
|
+
onClick={handleNotificationListenerToggle}
|
|
683
|
+
disabled={togglingNotificationListener}
|
|
684
|
+
role="switch"
|
|
685
|
+
aria-checked={notificationListenerEnabled}
|
|
686
|
+
>
|
|
687
|
+
<span className="toggle-switch-thumb" />
|
|
688
|
+
</button>
|
|
689
|
+
</label>
|
|
690
|
+
<label className="drawer-toggle">
|
|
691
|
+
<span className="drawer-toggle-label">SMS Access</span>
|
|
692
|
+
<button
|
|
693
|
+
className={`toggle-switch ${smsEnabled ? "toggle-switch-on" : ""}`}
|
|
694
|
+
onClick={handleSmsToggle}
|
|
695
|
+
disabled={togglingSms}
|
|
696
|
+
role="switch"
|
|
697
|
+
aria-checked={smsEnabled}
|
|
698
|
+
>
|
|
699
|
+
<span className="toggle-switch-thumb" />
|
|
700
|
+
</button>
|
|
701
|
+
</label>
|
|
702
|
+
<label className="drawer-toggle">
|
|
703
|
+
<span className="drawer-toggle-label">Contacts Access</span>
|
|
704
|
+
<button
|
|
705
|
+
className={`toggle-switch ${contactsEnabled ? "toggle-switch-on" : ""}`}
|
|
706
|
+
onClick={handleContactsToggle}
|
|
707
|
+
disabled={togglingContacts}
|
|
708
|
+
role="switch"
|
|
709
|
+
aria-checked={contactsEnabled}
|
|
710
|
+
>
|
|
711
|
+
<span className="toggle-switch-thumb" />
|
|
712
|
+
</button>
|
|
713
|
+
</label>
|
|
714
|
+
<label className="drawer-toggle">
|
|
715
|
+
<span className="drawer-toggle-label">Calendar Access</span>
|
|
716
|
+
<button
|
|
717
|
+
className={`toggle-switch ${calendarEnabled ? "toggle-switch-on" : ""}`}
|
|
718
|
+
onClick={handleCalendarToggle}
|
|
719
|
+
disabled={togglingCalendar}
|
|
720
|
+
role="switch"
|
|
721
|
+
aria-checked={calendarEnabled}
|
|
722
|
+
>
|
|
723
|
+
<span className="toggle-switch-thumb" />
|
|
724
|
+
</button>
|
|
725
|
+
</label>
|
|
726
|
+
<label className="drawer-toggle">
|
|
727
|
+
<span className="drawer-toggle-label">Do Not Disturb Control</span>
|
|
728
|
+
<button
|
|
729
|
+
className={`toggle-switch ${dndEnabled ? "toggle-switch-on" : ""}`}
|
|
730
|
+
onClick={handleDndToggle}
|
|
731
|
+
disabled={togglingDnd}
|
|
732
|
+
role="switch"
|
|
733
|
+
aria-checked={dndEnabled}
|
|
734
|
+
>
|
|
735
|
+
<span className="toggle-switch-thumb" />
|
|
736
|
+
</button>
|
|
737
|
+
</label>
|
|
738
|
+
<label className="drawer-toggle">
|
|
739
|
+
<span className="drawer-toggle-label">Alert Access</span>
|
|
740
|
+
<button
|
|
741
|
+
className={`toggle-switch ${alarmEnabled ? "toggle-switch-on" : ""}`}
|
|
742
|
+
onClick={handleAlarmToggle}
|
|
743
|
+
disabled={togglingAlarm}
|
|
744
|
+
role="switch"
|
|
745
|
+
aria-checked={alarmEnabled}
|
|
746
|
+
>
|
|
747
|
+
<span className="toggle-switch-thumb" />
|
|
748
|
+
</button>
|
|
749
|
+
</label>
|
|
750
|
+
<label className="drawer-toggle">
|
|
751
|
+
<span className="drawer-toggle-label">Battery Access</span>
|
|
752
|
+
<button
|
|
753
|
+
className={`toggle-switch ${batteryEnabled ? "toggle-switch-on" : ""}`}
|
|
754
|
+
onClick={handleBatteryToggle}
|
|
755
|
+
disabled={togglingBattery}
|
|
756
|
+
role="switch"
|
|
757
|
+
aria-checked={batteryEnabled}
|
|
758
|
+
>
|
|
759
|
+
<span className="toggle-switch-thumb" />
|
|
760
|
+
</button>
|
|
761
|
+
</label>
|
|
297
762
|
</div>
|
|
298
763
|
</>
|
|
299
764
|
)}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Bump when a breaking host change is made. */
|
|
2
|
-
export const MIN_HOST_VERSION = "0.
|
|
2
|
+
export const MIN_HOST_VERSION = "0.7.3";
|