Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
874908398a
commit
5e2ff8b990
@ -500,9 +500,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"Share your card and track recipients, or scan someone else's QR code to save their card." : {
|
|
||||||
|
|
||||||
},
|
},
|
||||||
"Shared With" : {
|
"Shared With" : {
|
||||||
|
|
||||||
@ -512,6 +509,9 @@
|
|||||||
},
|
},
|
||||||
"Support & Funding" : {
|
"Support & Funding" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Tap + to add a contact, scan a QR code, or track who you share your card with." : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Tap a field below to add it" : {
|
"Tap a field below to add it" : {
|
||||||
|
|
||||||
|
|||||||
@ -75,6 +75,33 @@ final class ContactsStore: ContactTracking {
|
|||||||
fetchContacts()
|
fetchContacts()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new contact manually
|
||||||
|
func createContact(
|
||||||
|
name: String,
|
||||||
|
role: String = "",
|
||||||
|
company: String = "",
|
||||||
|
email: String = "",
|
||||||
|
phone: String = "",
|
||||||
|
notes: String = "",
|
||||||
|
tags: String = "",
|
||||||
|
metAt: String = ""
|
||||||
|
) {
|
||||||
|
let contact = Contact(
|
||||||
|
name: name,
|
||||||
|
role: role,
|
||||||
|
company: company,
|
||||||
|
cardLabel: "Manual",
|
||||||
|
notes: notes,
|
||||||
|
tags: tags,
|
||||||
|
email: email,
|
||||||
|
phone: phone,
|
||||||
|
metAt: metAt
|
||||||
|
)
|
||||||
|
modelContext.insert(contact)
|
||||||
|
saveContext()
|
||||||
|
fetchContacts()
|
||||||
|
}
|
||||||
|
|
||||||
/// Updates a contact's notes
|
/// Updates a contact's notes
|
||||||
func updateNotes(for contact: Contact, notes: String) {
|
func updateNotes(for contact: Contact, notes: String) {
|
||||||
contact.notes = notes
|
contact.notes = notes
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import SwiftData
|
|||||||
struct ContactsView: View {
|
struct ContactsView: View {
|
||||||
@Environment(AppState.self) private var appState
|
@Environment(AppState.self) private var appState
|
||||||
@State private var showingScanner = false
|
@State private var showingScanner = false
|
||||||
|
@State private var showingAddContact = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@Bindable var contactsStore = appState.contactsStore
|
@Bindable var contactsStore = appState.contactsStore
|
||||||
@ -20,12 +21,19 @@ struct ContactsView: View {
|
|||||||
.navigationTitle(String.localized("Contacts"))
|
.navigationTitle(String.localized("Contacts"))
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .primaryAction) {
|
ToolbarItem(placement: .primaryAction) {
|
||||||
|
HStack(spacing: Design.Spacing.small) {
|
||||||
|
Button(String.localized("Add Contact"), systemImage: "plus") {
|
||||||
|
showingAddContact = true
|
||||||
|
}
|
||||||
|
.accessibilityHint(String.localized("Manually add a new contact"))
|
||||||
|
|
||||||
Button(String.localized("Scan Card"), systemImage: "qrcode.viewfinder") {
|
Button(String.localized("Scan Card"), systemImage: "qrcode.viewfinder") {
|
||||||
showingScanner = true
|
showingScanner = true
|
||||||
}
|
}
|
||||||
.accessibilityHint(String.localized("Scan someone else's QR code to save their card"))
|
.accessibilityHint(String.localized("Scan someone else's QR code to save their card"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.sheet(isPresented: $showingScanner) {
|
.sheet(isPresented: $showingScanner) {
|
||||||
QRScannerView { scannedData in
|
QRScannerView { scannedData in
|
||||||
if !scannedData.isEmpty {
|
if !scannedData.isEmpty {
|
||||||
@ -34,6 +42,9 @@ struct ContactsView: View {
|
|||||||
showingScanner = false
|
showingScanner = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.sheet(isPresented: $showingAddContact) {
|
||||||
|
AddContactSheet()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -49,7 +60,7 @@ private struct EmptyContactsView: View {
|
|||||||
.font(.headline)
|
.font(.headline)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.foregroundStyle(Color.Text.primary)
|
||||||
|
|
||||||
Text("Share your card and track recipients, or scan someone else's QR code to save their card.")
|
Text("Tap + to add a contact, scan a QR code, or track who you share your card with.")
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.foregroundStyle(Color.Text.secondary)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
|
|||||||
85
BusinessCard/Views/Sheets/AddContactSheet.swift
Normal file
85
BusinessCard/Views/Sheets/AddContactSheet.swift
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import SwiftData
|
||||||
|
import Bedrock
|
||||||
|
|
||||||
|
struct AddContactSheet: View {
|
||||||
|
@Environment(AppState.self) private var appState
|
||||||
|
@Environment(\.dismiss) private var dismiss
|
||||||
|
|
||||||
|
@State private var name = ""
|
||||||
|
@State private var role = ""
|
||||||
|
@State private var company = ""
|
||||||
|
@State private var email = ""
|
||||||
|
@State private var phone = ""
|
||||||
|
@State private var metAt = ""
|
||||||
|
|
||||||
|
private var canSave: Bool {
|
||||||
|
!name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
NavigationStack {
|
||||||
|
Form {
|
||||||
|
Section(String.localized("Name")) {
|
||||||
|
TextField(String.localized("Full name"), text: $name)
|
||||||
|
.textContentType(.name)
|
||||||
|
.accessibilityLabel(String.localized("Contact name"))
|
||||||
|
}
|
||||||
|
|
||||||
|
Section(String.localized("Role")) {
|
||||||
|
TextField(String.localized("Job title"), text: $role)
|
||||||
|
.textContentType(.jobTitle)
|
||||||
|
TextField(String.localized("Company"), text: $company)
|
||||||
|
.textContentType(.organizationName)
|
||||||
|
}
|
||||||
|
|
||||||
|
Section(String.localized("Contact")) {
|
||||||
|
TextField(String.localized("Email"), text: $email)
|
||||||
|
.keyboardType(.emailAddress)
|
||||||
|
.textContentType(.emailAddress)
|
||||||
|
.textInputAutocapitalization(.never)
|
||||||
|
TextField(String.localized("Phone"), text: $phone)
|
||||||
|
.keyboardType(.phonePad)
|
||||||
|
.textContentType(.telephoneNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
Section(String.localized("Where You Met")) {
|
||||||
|
TextField(String.localized("Event, location, or how you connected..."), text: $metAt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle(String.localized("Add Contact"))
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItem(placement: .cancellationAction) {
|
||||||
|
Button(String.localized("Cancel")) {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToolbarItem(placement: .confirmationAction) {
|
||||||
|
Button(String.localized("Save")) {
|
||||||
|
saveContact()
|
||||||
|
}
|
||||||
|
.bold()
|
||||||
|
.disabled(!canSave)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func saveContact() {
|
||||||
|
appState.contactsStore.createContact(
|
||||||
|
name: name.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||||
|
role: role.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||||
|
company: company.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||||
|
email: email.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||||
|
phone: phone.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||||
|
metAt: metAt.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
AddContactSheet()
|
||||||
|
.environment(AppState(modelContext: try! ModelContainer(for: BusinessCard.self, Contact.self).mainContext))
|
||||||
|
}
|
||||||
@ -67,6 +67,7 @@ Each field has:
|
|||||||
|
|
||||||
### Contacts
|
### Contacts
|
||||||
|
|
||||||
|
- **Add contacts manually**: Tap + to create contacts with name, role, company, email, phone
|
||||||
- Track who you've shared your card with
|
- Track who you've shared your card with
|
||||||
- **Scan QR codes** to save someone else's business card
|
- **Scan QR codes** to save someone else's business card
|
||||||
- **Notes & annotations**: Add notes about each contact
|
- **Notes & annotations**: Add notes about each contact
|
||||||
|
|||||||
@ -129,6 +129,7 @@ Reusable components (in `Views/Components/`):
|
|||||||
Sheets (in `Views/Sheets/`):
|
Sheets (in `Views/Sheets/`):
|
||||||
- `RecordContactSheet.swift` — track share recipient
|
- `RecordContactSheet.swift` — track share recipient
|
||||||
- `ContactFieldEditorSheet.swift` — add/edit contact field with type-specific UI
|
- `ContactFieldEditorSheet.swift` — add/edit contact field with type-specific UI
|
||||||
|
- `AddContactSheet.swift` — manually add a new contact
|
||||||
|
|
||||||
Small utilities:
|
Small utilities:
|
||||||
- `Views/EmptyStateView.swift` — empty state placeholder
|
- `Views/EmptyStateView.swift` — empty state placeholder
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user