Skip to content

Commit fb8fd6c

Browse files
committed
Enable logging events for multiple addictions
- Update `EventLogDialog` to allow selecting and removing multiple addictions when logging an event. - Modify `SoberViewModel.logEvent` to accept a list of addiction IDs and process logs/penalties for each. - Update `MainScreen`, `TimelineScreen`, `LogsScreen`, and `CheckInActivity` to support the new multi-selection logging flow. - Add an addiction picker dialog within the event logging flow.
1 parent 97a2e57 commit fb8fd6c

6 files changed

Lines changed: 104 additions & 31 deletions

File tree

.idea/planningMode.xml

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/src/main/java/me/shortman/sobercheck/CheckInActivity.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,11 @@ fun CheckInScreen(
7878
addiction = addiction,
7979
isAnswered = answeredAddictions.containsKey(addiction.id),
8080
onStayedSober = {
81-
viewModel.logEvent(addiction.id, LogType.EVENT, "Daily check-in: Stayed sober")
81+
viewModel.logEvent(listOf(addiction.id), LogType.EVENT, "Daily check-in: Stayed sober")
8282
answeredAddictions[addiction.id] = true
8383
},
8484
onRelapsed = {
85-
viewModel.logEvent(addiction.id, LogType.RELAPSE, "Daily check-in: Relapsed")
85+
viewModel.logEvent(listOf(addiction.id), LogType.RELAPSE, "Daily check-in: Relapsed")
8686
answeredAddictions[addiction.id] = false
8787
}
8888
)

app/src/main/java/me/shortman/sobercheck/ui/LogsScreen.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,10 @@ fun LogsScreen(
4141
if (editingLog != null) {
4242
EventLogDialog(
4343
type = editingLog!!.type,
44+
initialAddictionId = editingLog!!.addictionId,
4445
existingLog = editingLog,
4546
onDismiss = { editingLog = null },
46-
onConfirm = { note, strength, timestamp, type ->
47+
onConfirm = { note, strength, timestamp, type, _ ->
4748
viewModel.updateLog(editingLog!!.copy(
4849
note = note,
4950
cravingStrength = strength,

app/src/main/java/me/shortman/sobercheck/ui/MainScreen.kt

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,15 @@ fun MainScreen(
158158
if (eventLogTargetAddiction != null && showEventLogType != null) {
159159
EventLogDialog(
160160
type = showEventLogType!!,
161+
allAddictions = addictions,
162+
initialAddictionId = eventLogTargetAddiction?.id,
161163
onDismiss = {
162164
showEventLogType = null
163165
eventLogTargetAddiction = null
164166
},
165-
onConfirm = { note, strength, timestamp, finalType ->
167+
onConfirm = { note, strength, timestamp, finalType, ids ->
166168
viewModel.logEvent(
167-
eventLogTargetAddiction!!.id,
169+
ids,
168170
finalType,
169171
note,
170172
strength,
@@ -710,16 +712,23 @@ fun AddictionActionsSheet(
710712
@Composable
711713
fun EventLogDialog(
712714
type: LogType,
715+
allAddictions: List<Addiction> = emptyList(),
716+
initialAddictionId: Long? = null,
713717
existingLog: LogEntry? = null,
714718
onDismiss: () -> Unit,
715-
onConfirm: (String, Int?, Long, LogType) -> Unit
719+
onConfirm: (String, Int?, Long, LogType, List<Long>) -> Unit
716720
) {
717721
var selectedType by remember { mutableStateOf(existingLog?.type ?: if (type == LogType.EVENT) LogType.EVENT_GROUP else type) }
718722
var note by remember { mutableStateOf(existingLog?.note ?: "") }
719723
var strength by remember { mutableFloatStateOf(existingLog?.cravingStrength?.toFloat() ?: 5f) }
720724
var showDatePicker by remember { mutableStateOf(false) }
721725
var showTimePicker by remember { mutableStateOf(false) }
722726

727+
var selectedAddictionIds by remember(initialAddictionId) {
728+
mutableStateOf(initialAddictionId?.let { listOf(it) } ?: emptyList())
729+
}
730+
var showAddictionPicker by remember { mutableStateOf(false) }
731+
723732
val initialDate = Calendar.getInstance().apply {
724733
if (existingLog != null) {
725734
timeInMillis = existingLog.timestamp
@@ -865,6 +874,40 @@ fun EventLogDialog(
865874
Text("%02d:%02d".format(timePickerState.hour, timePickerState.minute))
866875
}
867876
}
877+
878+
if (!isEdit && allAddictions.isNotEmpty()) {
879+
Spacer(modifier = Modifier.height(8.dp))
880+
Text("Add to other addiction", style = MaterialTheme.typography.labelLarge)
881+
OutlinedButton(
882+
onClick = { showAddictionPicker = true },
883+
modifier = Modifier.fillMaxWidth()
884+
) {
885+
Icon(Icons.Default.Add, contentDescription = null)
886+
Spacer(Modifier.width(8.dp))
887+
Text("Add to other addiction")
888+
}
889+
890+
val otherAddictions = allAddictions.filter { it.id in selectedAddictionIds && it.id != initialAddictionId }
891+
if (otherAddictions.isNotEmpty()) {
892+
Column(modifier = Modifier.padding(top = 4.dp)) {
893+
otherAddictions.forEach { addiction ->
894+
Row(
895+
modifier = Modifier.fillMaxWidth(),
896+
horizontalArrangement = Arrangement.SpaceBetween,
897+
verticalAlignment = Alignment.CenterVertically
898+
) {
899+
Text(addiction.name, style = MaterialTheme.typography.bodySmall)
900+
IconButton(
901+
onClick = { selectedAddictionIds = selectedAddictionIds - addiction.id },
902+
modifier = Modifier.size(24.dp)
903+
) {
904+
Icon(Icons.Default.Delete, contentDescription = "Remove", modifier = Modifier.size(16.dp))
905+
}
906+
}
907+
}
908+
}
909+
}
910+
}
868911

869912
if (isFuture) {
870913
Text(
@@ -894,15 +937,39 @@ fun EventLogDialog(
894937
confirmButton = {
895938
Button(
896939
onClick = {
897-
onConfirm(note, if (type == LogType.CRAVING) strength.toInt() else null, selectedTimestamp, selectedType)
940+
onConfirm(note, if (type == LogType.CRAVING) strength.toInt() else null, selectedTimestamp, selectedType, selectedAddictionIds)
898941
},
899-
enabled = !isFuture
942+
enabled = !isFuture && selectedAddictionIds.isNotEmpty()
900943
) { Text(if (existingLog != null) "Update" else "Log") }
901944
},
902945
dismissButton = {
903946
TextButton(onClick = onDismiss) { Text("Cancel") }
904947
}
905948
)
949+
950+
if (showAddictionPicker) {
951+
val availableAddictions = allAddictions.filter { it.id !in selectedAddictionIds }
952+
AlertDialog(
953+
onDismissRequest = { showAddictionPicker = false },
954+
title = { Text("Select Addiction") },
955+
text = {
956+
LazyColumn(modifier = Modifier.fillMaxWidth()) {
957+
items(availableAddictions) { addiction ->
958+
ListItem(
959+
headlineContent = { Text(addiction.name) },
960+
modifier = Modifier.clickable {
961+
selectedAddictionIds = selectedAddictionIds + addiction.id
962+
showAddictionPicker = false
963+
}
964+
)
965+
}
966+
}
967+
},
968+
confirmButton = {
969+
TextButton(onClick = { showAddictionPicker = false }) { Text("Cancel") }
970+
}
971+
)
972+
}
906973
}
907974

908975
@Composable

app/src/main/java/me/shortman/sobercheck/ui/SoberViewModel.kt

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -106,31 +106,33 @@ class SoberViewModel @Inject constructor(
106106
))
107107
}
108108

109-
fun logEvent(addictionId: Long, type: LogType, note: String = "", strength: Int? = null, timestamp: Long = System.currentTimeMillis()) {
109+
fun logEvent(addictionIds: List<Long>, type: LogType, note: String = "", strength: Int? = null, timestamp: Long = System.currentTimeMillis()) {
110110
viewModelScope.launch {
111-
repository.addLog(LogEntry(
112-
addictionId = addictionId,
113-
type = type,
114-
timestamp = timestamp,
115-
note = note,
116-
cravingStrength = strength
117-
))
111+
addictionIds.forEach { addictionId ->
112+
repository.addLog(LogEntry(
113+
addictionId = addictionId,
114+
type = type,
115+
timestamp = timestamp,
116+
note = note,
117+
cravingStrength = strength
118+
))
119+
120+
if (type == LogType.RELAPSE) {
121+
val addictions = repository.getAllAddictionsList()
122+
val addiction = addictions.find { it.id == addictionId }
123+
if (addiction != null) {
124+
val penalty = GamificationEngine.getRelapsePenalty(addiction.totalScore)
125+
val newScore = (addiction.totalScore - penalty).coerceAtLeast(0.0)
126+
127+
repository.updateAddiction(addiction.copy(
128+
totalScore = newScore,
129+
lastCheckpointTimestamp = timestamp
130+
))
131+
}
118132

119-
if (type == LogType.RELAPSE) {
120-
val addictions = repository.getAllAddictionsList()
121-
val addiction = addictions.find { it.id == addictionId }
122-
if (addiction != null) {
123-
val penalty = GamificationEngine.getRelapsePenalty(addiction.totalScore)
124-
val newScore = (addiction.totalScore - penalty).coerceAtLeast(0.0)
125-
126-
repository.updateAddiction(addiction.copy(
127-
totalScore = newScore,
128-
lastCheckpointTimestamp = timestamp
129-
))
133+
val relapseCount = repository.getRelapseCount(addictionId)
134+
createAttempt(addictionId, relapseCount + 1, timestamp)
130135
}
131-
132-
val relapseCount = repository.getRelapseCount(addictionId)
133-
createAttempt(addictionId, relapseCount + 1, timestamp)
134136
}
135137
}
136138
}

app/src/main/java/me/shortman/sobercheck/ui/TimelineScreen.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,10 @@ fun TimelineScreen(
3737
if (editingLog != null) {
3838
EventLogDialog(
3939
type = editingLog!!.type,
40+
initialAddictionId = editingLog!!.addictionId,
4041
existingLog = editingLog,
4142
onDismiss = { editingLog = null },
42-
onConfirm = { note, strength, timestamp, type ->
43+
onConfirm = { note, strength, timestamp, type, _ ->
4344
viewModel.updateLog(editingLog!!.copy(
4445
note = note,
4546
cravingStrength = strength,

0 commit comments

Comments
 (0)