Skip to content

Commit 528a09c

Browse files
ndbroadbentclaude
andcommitted
Add Contacts permission to onboarding UI
- Redesigned permission screen to show both FDA and Contacts as separate cards - Each card shows status (pending/granted/denied), description, and settings button - Full Disk Access marked as "Required", Contacts marked as "Recommended" - Added check_contacts_access Tauri command to verify Contacts permission - Added open_contacts_settings to open System Preferences > Contacts - Permission status updates dynamically when user clicks "Check Permissions" - Contacts is optional - app works without it (shows phone numbers instead of names) - Extracted screenshot mode code to separate module to reduce main.ts size 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 697c8e2 commit 528a09c

File tree

6 files changed

+386
-177
lines changed

6 files changed

+386
-177
lines changed

src-tauri/src/main.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,54 @@ fn open_full_disk_access_settings() -> Result<(), String> {
232232
Ok(())
233233
}
234234

235+
/// Check if Contacts access is granted (macOS)
236+
#[tauri::command]
237+
fn check_contacts_access() -> Result<bool, String> {
238+
eprintln!("[check_contacts_access] Checking...");
239+
240+
#[cfg(target_os = "macos")]
241+
{
242+
use chat_to_map_desktop::contacts::ContactsIndex;
243+
244+
// Try to build the contacts index - this will fail without Contacts permission
245+
match ContactsIndex::build(None) {
246+
Ok(index) => {
247+
let has_contacts = !index.is_empty();
248+
eprintln!(
249+
"[check_contacts_access] Contacts access granted, {} entries",
250+
index.len()
251+
);
252+
// If the index is empty, it might mean no permission OR no contacts
253+
// We return true if we could read the database (even if empty)
254+
Ok(has_contacts || index.is_empty())
255+
}
256+
Err(e) => {
257+
eprintln!("[check_contacts_access] Contacts access denied: {:?}", e);
258+
Ok(false)
259+
}
260+
}
261+
}
262+
263+
#[cfg(not(target_os = "macos"))]
264+
{
265+
// On non-macOS platforms, contacts aren't available
266+
Ok(false)
267+
}
268+
}
269+
270+
/// Open System Preferences to Contacts (macOS)
271+
#[tauri::command]
272+
fn open_contacts_settings() -> Result<(), String> {
273+
#[cfg(target_os = "macos")]
274+
{
275+
std::process::Command::new("open")
276+
.arg("x-apple.systempreferences:com.apple.preference.security?Privacy_Contacts")
277+
.spawn()
278+
.map_err(|e| e.to_string())?;
279+
}
280+
Ok(())
281+
}
282+
235283
/// Screenshot mode config returned to frontend
236284
#[derive(Debug, Clone, Serialize, Deserialize)]
237285
pub struct ScreenshotConfigResponse {
@@ -350,6 +398,8 @@ fn main() {
350398
export_and_upload,
351399
check_full_disk_access,
352400
open_full_disk_access_settings,
401+
check_contacts_access,
402+
open_contacts_settings,
353403
get_screenshot_config,
354404
take_screenshot,
355405
open_licenses,

src/index.html

Lines changed: 50 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,55 @@ <h1>ChatToMap</h1>
1919
<div id="permission-screen" class="screen hidden">
2020
<div class="permission-header">
2121
<div class="icon">🔒</div>
22-
<h2>Messages Data Permission</h2>
22+
<h2>App Permissions</h2>
2323
</div>
2424

2525
<div class="permission-explanation">
26-
<div class="permission-section">
27-
<h3>What we need access to:</h3>
28-
<div class="permission-item permission-yes">
29-
<span class="permission-icon"></span>
30-
<span><code>~/Library/Messages/chat.db</code> — your iMessage database</span>
26+
<!-- Permission 1: Full Disk Access (Required) -->
27+
<div class="permission-card" id="fda-permission-card">
28+
<div class="permission-card-header">
29+
<div class="permission-card-status" id="fda-status">
30+
<span class="status-icon status-pending"></span>
31+
</div>
32+
<div class="permission-card-title">
33+
<h3>Full Disk Access</h3>
34+
<span class="permission-badge required">Required</span>
35+
</div>
36+
</div>
37+
<div class="permission-card-body">
38+
<p>Access to <code>~/Library/Messages/chat.db</code> — your iMessage database</p>
39+
<div class="permission-note">
40+
Apple protects Messages. This is the only way to read it — even Beeper requires it.
41+
</div>
42+
</div>
43+
<div class="permission-card-action">
44+
<button id="open-fda-settings-btn" class="btn btn-secondary btn-small">
45+
Open Settings
46+
</button>
47+
</div>
48+
</div>
49+
50+
<!-- Permission 2: Contacts (Recommended) -->
51+
<div class="permission-card" id="contacts-permission-card">
52+
<div class="permission-card-header">
53+
<div class="permission-card-status" id="contacts-status">
54+
<span class="status-icon status-pending"></span>
55+
</div>
56+
<div class="permission-card-title">
57+
<h3>Contacts</h3>
58+
<span class="permission-badge recommended">Recommended</span>
59+
</div>
60+
</div>
61+
<div class="permission-card-body">
62+
<p>Display names instead of phone numbers</p>
63+
<div class="permission-note">
64+
Without this, chats will show as "+1 (555) 123-4567" instead of "John Smith".
65+
</div>
66+
</div>
67+
<div class="permission-card-action">
68+
<button id="open-contacts-settings-btn" class="btn btn-secondary btn-small">
69+
Open Settings
70+
</button>
3171
</div>
3272
</div>
3373

@@ -42,30 +82,18 @@ <h3>What we do NOT access:</h3>
4282
<span>Attachments or media from your chats</span>
4383
</div>
4484
</div>
45-
46-
<div class="permission-note">
47-
<strong>Why Full Disk Access?</strong> Apple protects your Messages
48-
folder. This is the only way for apps to read it — even Beeper and
49-
other iMessage tools require it.
50-
</div>
5185
</div>
5286

5387
<div class="permission-steps">
54-
<h3>How to enable:</h3>
88+
<h3>After enabling permissions:</h3>
5589
<ol>
56-
<li>Click "Open Privacy Settings" below</li>
57-
<li>Find <strong>ChatToMap</strong> in the list</li>
58-
<li>Toggle it <strong>ON</strong></li>
59-
<li><strong>Quit and reopen</strong> this app</li>
90+
<li><strong>Quit and reopen</strong> this app for changes to take effect</li>
6091
</ol>
6192
</div>
6293

6394
<div class="permission-actions">
64-
<button id="open-settings-btn" class="btn btn-primary btn-large">
65-
Open Privacy Settings
66-
</button>
67-
<button id="retry-permission-btn" class="btn btn-secondary">
68-
I've enabled it — Check Again
95+
<button id="retry-permission-btn" class="btn btn-primary btn-large">
96+
Check Permissions
6997
</button>
7098
<button id="select-db-btn" class="btn btn-link">
7199
Or select a copied chat.db file manually

0 commit comments

Comments
 (0)