From 047c536facfaddfdb077015cc5172060a5a0c9a6 Mon Sep 17 00:00:00 2001 From: Joseph Hale Date: Thu, 22 Feb 2024 00:36:31 -0700 Subject: [PATCH] refactor: Hoist all rpc calls to `ActivitiesScreen` Components are more reusable when they have minimal state and/or dependency on outside resources. This commit raises the rpc calls to `ActivitiesScreen` which has the responsibility for managing the data flow and interactions between components. This update also fixes a few bugs: - Tapping an activity chip will start the activity if it doesn't have any tags. - The switches now update automatically after an activity is started or stopped. closes thehale/SimpleTimeTracker-WearOS#12 closes thehale/SimpleTimeTracker-WearOS#13 --- .../presentation/components/ActivitiesList.kt | 47 +++-------------- .../presentation/components/ActivityChip.kt | 17 +++---- .../presentation/screens/ActivitiesScreen.kt | 51 ++++++++++++++++--- 3 files changed, 60 insertions(+), 55 deletions(-) diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/ActivitiesList.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/ActivitiesList.kt index 365598d3..36b7b3b8 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/ActivitiesList.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/ActivitiesList.kt @@ -1,4 +1,3 @@ - /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -6,7 +5,6 @@ */ package com.example.util.simpletimetracker.presentation.components -import android.widget.Toast import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.platform.LocalContext @@ -16,57 +14,27 @@ import com.example.util.simpletimetracker.presentation.layout.ScaffoldedScrollin import com.example.util.simpletimetracker.presentation.remember.rememberRPCClient import com.example.util.simpletimetracker.wearrpc.Activity import com.example.util.simpletimetracker.wearrpc.CurrentActivity -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch @Composable fun ActivitiesList( activities: Array, currentActivities: Array, onSelectActivity: (activity: Activity) -> Unit, - onDeselectActivity: (activity: Activity) -> Unit, + onEnableActivity: (activity: Activity) -> Unit, + onDisableActivity: (activity: Activity) -> Unit, onRefresh: () -> Unit, ) { - val coroutineScope = rememberCoroutineScope() - val rpcClient = rememberRPCClient() - val context = LocalContext.current - ScaffoldedScrollingColumn { for (activity in activities) { val currentActivity = currentActivities.filter { it.id == activity.id }.getOrNull(0) - item { + item(key = activity.id) { ActivityChip( activity, startedAt = currentActivity?.startedAt, tags = currentActivity?.tags ?: arrayOf(), - onSelectActivity = { - coroutineScope.launch(Dispatchers.Default) { - val activityTags = rpcClient.queryTagsForActivity(activity.id) - coroutineScope.launch(Dispatchers.Main) { - if (activityTags.isNotEmpty()) { - onSelectActivity(activity) - } else { - Toast.makeText(context, "Activity has no tags", Toast.LENGTH_SHORT).show() - } - } - } - }, - onSelectActivitySkipTagSelection = { - coroutineScope.launch(Dispatchers.Default) { - rpcClient.setCurrentActivities( - currentActivities.plus( - CurrentActivity( - activity.id, - System.currentTimeMillis(), - arrayOf(), - ), - ), - ) - } - }, - onDeselectActivity = { - onDeselectActivity(activity) - }, + onPress = { onSelectActivity(activity) }, + onToggleOn = { onEnableActivity(activity) }, + onToggleOff = { onDisableActivity(activity) }, ) } } @@ -93,7 +61,8 @@ private fun Preview() { activities, currentActivities = currents, onSelectActivity = { /* `it` is the selected activity */ }, - onDeselectActivity = { /* `it` is the deselected activity */ }, + onEnableActivity = { /* `it` is the enabled activity */ }, + onDisableActivity = { /* `it` is the disabled activity */ }, onRefresh = { /* What to do when requesting a refresh */ }, ) } \ No newline at end of file diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/ActivityChip.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/ActivityChip.kt index e7b62b4e..459bdfbd 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/ActivityChip.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/ActivityChip.kt @@ -37,9 +37,9 @@ fun ActivityChip( activity: Activity, startedAt: Long? = null, tags: Array = arrayOf(), - onSelectActivity: () -> Unit = {}, - onSelectActivitySkipTagSelection: () -> Unit = {}, - onDeselectActivity: () -> Unit = {}, + onPress: () -> Unit = {}, + onToggleOn: () -> Unit = {}, + onToggleOff: () -> Unit = {}, ) { val briefIcon = if (activity.icon.startsWith("ic_")) { "?" @@ -60,7 +60,7 @@ fun ActivityChip( var modifier = Modifier .fillMaxWidth(0.9f) .padding(top = 10.dp) - var switchChecked by remember { mutableStateOf(startedAt != null) } + var switchChecked = startedAt != null SplitToggleChip( modifier = modifier, label = { @@ -87,16 +87,13 @@ fun ActivityChip( ), onCheckedChange = { if (it) { - onSelectActivitySkipTagSelection() + onToggleOn() } else { - onDeselectActivity() + onToggleOff() } - switchChecked = it }, checked = switchChecked, - onClick = { - onSelectActivity() - }, + onClick = onPress, toggleControl = { Switch( checked = switchChecked, diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/ActivitiesScreen.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/ActivitiesScreen.kt index ba3bfc54..8bd8fda7 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/ActivitiesScreen.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/ActivitiesScreen.kt @@ -6,30 +6,69 @@ package com.example.util.simpletimetracker.presentation.screens import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope import com.example.util.simpletimetracker.presentation.components.ActivitiesList import com.example.util.simpletimetracker.presentation.remember.rememberActivities import com.example.util.simpletimetracker.presentation.remember.rememberCurrentActivities +import com.example.util.simpletimetracker.presentation.remember.rememberRPCClient import com.example.util.simpletimetracker.wearrpc.Activity +import com.example.util.simpletimetracker.wearrpc.CurrentActivity +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch @Composable fun ActivitiesScreen(onSelectActivity: (activityId: Long) -> Unit) { + val coroutineScope = rememberCoroutineScope() + val rpc = rememberRPCClient() val (activities, refreshActivities) = rememberActivities() val (currentActivities, setCurrentActivities, refreshCurrentActivities) = rememberCurrentActivities() + val refresh = { + refreshActivities() + refreshCurrentActivities() + } ActivitiesList( activities, currentActivities, onSelectActivity = { - onSelectActivity(it.id) + coroutineScope.launch(Dispatchers.Default) { + val activityTags = rpc.queryTagsForActivity(it.id) + if (activityTags.isNotEmpty()) { + coroutineScope.launch(Dispatchers.Main) { onSelectActivity(it.id) } + } else { + rpc.setCurrentActivities( + arrayOf( + CurrentActivity( + it.id, + System.currentTimeMillis(), + arrayOf(), + ), + ), + ) + refresh() + } + } }, - onDeselectActivity = { deselectedActivity: Activity -> + onEnableActivity = { + coroutineScope.launch(Dispatchers.Default) { + rpc.setCurrentActivities( + arrayOf( + CurrentActivity( + it.id, + System.currentTimeMillis(), + arrayOf(), + ), + ), + ) + refresh() + } + }, + onDisableActivity = { deselectedActivity: Activity -> val remainingActivities = currentActivities.filter { it.id != deselectedActivity.id }.toTypedArray() setCurrentActivities(remainingActivities) + refresh() }, - onRefresh = { - refreshActivities() - refreshCurrentActivities() - }, + onRefresh = refresh, ) } -- 2.54.0