Add email notification preference for task completion
- Add emailTaskCompleted field to NotificationPreference model - Add email preference toggle to notification settings UI (iOS & Android) - Add localized strings for email notifications section - Update ViewModel to support email preference updates 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -390,6 +390,11 @@
|
||||
<string name="notifications_warranty_expiring">Warranty Expiring</string>
|
||||
<string name="notifications_warranty_expiring_desc">Reminders for expiring warranties</string>
|
||||
|
||||
<!-- Email Notifications -->
|
||||
<string name="notifications_email_section">Email Notifications</string>
|
||||
<string name="notifications_email_task_completed">Task Completed Email</string>
|
||||
<string name="notifications_email_task_completed_desc">Receive email when a task is completed</string>
|
||||
|
||||
<!-- Common -->
|
||||
<string name="common_save">Save</string>
|
||||
<string name="common_cancel">Cancel</string>
|
||||
|
||||
@@ -40,7 +40,10 @@ data class NotificationPreference(
|
||||
@SerialName("residence_shared")
|
||||
val residenceShared: Boolean = true,
|
||||
@SerialName("warranty_expiring")
|
||||
val warrantyExpiring: Boolean = true
|
||||
val warrantyExpiring: Boolean = true,
|
||||
// Email preferences
|
||||
@SerialName("email_task_completed")
|
||||
val emailTaskCompleted: Boolean = true
|
||||
)
|
||||
|
||||
@Serializable
|
||||
@@ -56,7 +59,10 @@ data class UpdateNotificationPreferencesRequest(
|
||||
@SerialName("residence_shared")
|
||||
val residenceShared: Boolean? = null,
|
||||
@SerialName("warranty_expiring")
|
||||
val warrantyExpiring: Boolean? = null
|
||||
val warrantyExpiring: Boolean? = null,
|
||||
// Email preferences
|
||||
@SerialName("email_task_completed")
|
||||
val emailTaskCompleted: Boolean? = null
|
||||
)
|
||||
|
||||
@Serializable
|
||||
|
||||
@@ -35,6 +35,7 @@ fun NotificationPreferencesScreen(
|
||||
var taskAssigned by remember { mutableStateOf(true) }
|
||||
var residenceShared by remember { mutableStateOf(true) }
|
||||
var warrantyExpiring by remember { mutableStateOf(true) }
|
||||
var emailTaskCompleted by remember { mutableStateOf(true) }
|
||||
|
||||
// Load preferences on first render
|
||||
LaunchedEffect(Unit) {
|
||||
@@ -51,6 +52,7 @@ fun NotificationPreferencesScreen(
|
||||
taskAssigned = prefs.taskAssigned
|
||||
residenceShared = prefs.residenceShared
|
||||
warrantyExpiring = prefs.warrantyExpiring
|
||||
emailTaskCompleted = prefs.emailTaskCompleted
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,6 +296,36 @@ fun NotificationPreferencesScreen(
|
||||
}
|
||||
}
|
||||
|
||||
// Email Notifications Section
|
||||
Text(
|
||||
stringResource(Res.string.notifications_email_section),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
modifier = Modifier.padding(top = AppSpacing.md)
|
||||
)
|
||||
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(AppRadius.md),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant
|
||||
)
|
||||
) {
|
||||
Column {
|
||||
NotificationToggle(
|
||||
title = stringResource(Res.string.notifications_email_task_completed),
|
||||
description = stringResource(Res.string.notifications_email_task_completed_desc),
|
||||
icon = Icons.Default.Email,
|
||||
iconTint = MaterialTheme.colorScheme.primary,
|
||||
checked = emailTaskCompleted,
|
||||
onCheckedChange = {
|
||||
emailTaskCompleted = it
|
||||
viewModel.updatePreference(emailTaskCompleted = it)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(AppSpacing.xl))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,8 @@ class NotificationPreferencesViewModel : ViewModel() {
|
||||
taskCompleted: Boolean? = null,
|
||||
taskAssigned: Boolean? = null,
|
||||
residenceShared: Boolean? = null,
|
||||
warrantyExpiring: Boolean? = null
|
||||
warrantyExpiring: Boolean? = null,
|
||||
emailTaskCompleted: Boolean? = null
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
_updateState.value = ApiResult.Loading
|
||||
@@ -46,7 +47,8 @@ class NotificationPreferencesViewModel : ViewModel() {
|
||||
taskCompleted = taskCompleted,
|
||||
taskAssigned = taskAssigned,
|
||||
residenceShared = residenceShared,
|
||||
warrantyExpiring = warrantyExpiring
|
||||
warrantyExpiring = warrantyExpiring,
|
||||
emailTaskCompleted = emailTaskCompleted
|
||||
)
|
||||
val result = APILayer.updateNotificationPreferences(request)
|
||||
_updateState.value = when (result) {
|
||||
|
||||
@@ -540,6 +540,11 @@ enum L10n {
|
||||
static var warrantyExpiring: String { String(localized: "profile_warranty_expiring") }
|
||||
static var warrantyExpiringDescription: String { String(localized: "profile_warranty_expiring_description") }
|
||||
static var otherNotifications: String { String(localized: "profile_other_notifications") }
|
||||
|
||||
// Email Notifications
|
||||
static var emailNotifications: String { String(localized: "profile_email_notifications") }
|
||||
static var emailTaskCompleted: String { String(localized: "profile_email_task_completed") }
|
||||
static var emailTaskCompletedDescription: String { String(localized: "profile_email_task_completed_description") }
|
||||
}
|
||||
|
||||
// MARK: - Settings
|
||||
|
||||
@@ -17844,6 +17844,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"profile_email_notifications" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Email Notifications"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"profile_email_required_unique" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
@@ -17909,6 +17920,28 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"profile_email_task_completed" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Task Completed Email"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"profile_email_task_completed_description" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Receive email when a task is completed"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"profile_first_name" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
|
||||
@@ -191,6 +191,31 @@ struct NotificationPreferencesView: View {
|
||||
Text(L10n.Profile.otherNotifications)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
// Email Notifications
|
||||
Section {
|
||||
Toggle(isOn: $viewModel.emailTaskCompleted) {
|
||||
Label {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(L10n.Profile.emailTaskCompleted)
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
Text(L10n.Profile.emailTaskCompletedDescription)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
} icon: {
|
||||
Image(systemName: "envelope.fill")
|
||||
.foregroundColor(Color.appPrimary)
|
||||
}
|
||||
}
|
||||
.tint(Color.appPrimary)
|
||||
.onChange(of: viewModel.emailTaskCompleted) { _, newValue in
|
||||
viewModel.updatePreference(emailTaskCompleted: newValue)
|
||||
}
|
||||
} header: {
|
||||
Text(L10n.Profile.emailNotifications)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
}
|
||||
}
|
||||
.listStyle(.plain)
|
||||
@@ -223,6 +248,7 @@ class NotificationPreferencesViewModelWrapper: ObservableObject {
|
||||
@Published var taskAssigned: Bool = true
|
||||
@Published var residenceShared: Bool = true
|
||||
@Published var warrantyExpiring: Bool = true
|
||||
@Published var emailTaskCompleted: Bool = true
|
||||
|
||||
@Published var isLoading: Bool = false
|
||||
@Published var errorMessage: String?
|
||||
@@ -243,6 +269,7 @@ class NotificationPreferencesViewModelWrapper: ObservableObject {
|
||||
self.taskAssigned = prefs.taskAssigned
|
||||
self.residenceShared = prefs.residenceShared
|
||||
self.warrantyExpiring = prefs.warrantyExpiring
|
||||
self.emailTaskCompleted = prefs.emailTaskCompleted
|
||||
self.isLoading = false
|
||||
self.errorMessage = nil
|
||||
} else if let error = result as? ApiResultError {
|
||||
@@ -262,7 +289,8 @@ class NotificationPreferencesViewModelWrapper: ObservableObject {
|
||||
taskCompleted: Bool? = nil,
|
||||
taskAssigned: Bool? = nil,
|
||||
residenceShared: Bool? = nil,
|
||||
warrantyExpiring: Bool? = nil
|
||||
warrantyExpiring: Bool? = nil,
|
||||
emailTaskCompleted: Bool? = nil
|
||||
) {
|
||||
isSaving = true
|
||||
|
||||
@@ -274,7 +302,8 @@ class NotificationPreferencesViewModelWrapper: ObservableObject {
|
||||
taskCompleted: taskCompleted.map { KotlinBoolean(bool: $0) },
|
||||
taskAssigned: taskAssigned.map { KotlinBoolean(bool: $0) },
|
||||
residenceShared: residenceShared.map { KotlinBoolean(bool: $0) },
|
||||
warrantyExpiring: warrantyExpiring.map { KotlinBoolean(bool: $0) }
|
||||
warrantyExpiring: warrantyExpiring.map { KotlinBoolean(bool: $0) },
|
||||
emailTaskCompleted: emailTaskCompleted.map { KotlinBoolean(bool: $0) }
|
||||
)
|
||||
let result = try await APILayer.shared.updateNotificationPreferences(request: request)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user