Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
Expand Down Expand Up @@ -68,6 +69,8 @@ fun AppSheet(
Column(
modifier = Modifier
.fillMaxWidth()
// Only the drag handle may dismiss the sheet; swipes in the content just scroll it.
.nestedScroll(ConsumeVerticalOverscroll)
.verticalScroll(rememberScrollState())
.navigationBarsPadding()
.imePadding()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
Expand All @@ -57,6 +58,7 @@ import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner
import dev.tricked.solidverdant.R
import dev.tricked.solidverdant.ui.auth.OAuthConfigState
import dev.tricked.solidverdant.ui.components.ConsumeVerticalOverscroll
import dev.tricked.solidverdant.ui.components.EntrySheetHeader
import dev.tricked.solidverdant.ui.components.GroupedDivider
import dev.tricked.solidverdant.ui.components.GroupedRow
Expand Down Expand Up @@ -128,6 +130,8 @@ fun ConfigScreen(
Column(
modifier = Modifier
.fillMaxWidth()
// Only the drag handle may dismiss the sheet; swipes in the content just scroll it.
.nestedScroll(ConsumeVerticalOverscroll)
.verticalScroll(rememberScrollState())
.navigationBarsPadding()
.imePadding()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.style.TextOverflow
import dev.tricked.solidverdant.R
import dev.tricked.solidverdant.ui.components.ConsumeVerticalOverscroll
import dev.tricked.solidverdant.ui.components.EmptyState
import dev.tricked.solidverdant.ui.components.ErrorState
import dev.tricked.solidverdant.ui.components.GroupedPosition
Expand Down Expand Up @@ -85,7 +87,12 @@ fun StatDrillDownSheet(state: DrillDownUiState, onDismiss: () -> Unit, onRetry:
containerColor = MaterialTheme.colorScheme.background,
) {
LazyColumn(
modifier = Modifier.fillMaxWidth().navigationBarsPadding().testTag(StatDrillDownTestTags.LIST),
modifier = Modifier
.fillMaxWidth()
// Only the drag handle may dismiss the sheet; swipes in the content just scroll it.
.nestedScroll(ConsumeVerticalOverscroll)
.navigationBarsPadding()
.testTag(StatDrillDownTestTags.LIST),
contentPadding = PaddingValues(bottom = Dimens.Space24),
) {
item(key = "title") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
Expand All @@ -43,6 +44,7 @@ import dev.tricked.solidverdant.data.model.Tag
import dev.tricked.solidverdant.data.model.Task
import dev.tricked.solidverdant.data.repository.EntryTemplate
import dev.tricked.solidverdant.ui.components.ConfirmDialog
import dev.tricked.solidverdant.ui.components.ConsumeVerticalOverscroll
import dev.tricked.solidverdant.ui.components.DestructiveActionRow
import dev.tricked.solidverdant.ui.components.EntrySheetHeader
import dev.tricked.solidverdant.ui.components.GroupedDivider
Expand Down Expand Up @@ -142,6 +144,8 @@ fun TemplateEditorSheet(
Column(
modifier = Modifier
.fillMaxWidth()
// Only the drag handle may dismiss the sheet; swipes in the content just scroll it.
.nestedScroll(ConsumeVerticalOverscroll)
.verticalScroll(rememberScrollState())
.navigationBarsPadding()
.imePadding()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ import dev.tricked.solidverdant.service.TimeTrackingNotificationService
import dev.tricked.solidverdant.ui.components.AppSheet
import dev.tricked.solidverdant.ui.components.AppTimePickerDialog
import dev.tricked.solidverdant.ui.components.ConfirmDialog
import dev.tricked.solidverdant.ui.components.ConsumeVerticalOverscroll
import dev.tricked.solidverdant.ui.components.DateRangePickerDialog
import dev.tricked.solidverdant.ui.components.DestructiveActionRow
import dev.tricked.solidverdant.ui.components.EditTimeEntryTestTags
Expand Down Expand Up @@ -1953,6 +1954,8 @@ internal fun TimeEntryFormSheet(
Column(
modifier = Modifier
.fillMaxWidth()
// Only the drag handle may dismiss the sheet; swipes in the form just scroll it.
.nestedScroll(ConsumeVerticalOverscroll)
.verticalScroll(rememberScrollState())
.navigationBarsPadding()
.imePadding()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
Expand Down Expand Up @@ -99,4 +100,22 @@ class SheetSwipeDismissTest {
private companion object {
const val FORM = "sheet_form"
}

@Test
fun swiping_down_inside_an_app_sheet_does_not_dismiss_it() {
var dismissed = 0
composeRule.setContent {
MaterialTheme {
AppSheet(title = "Options", onDismiss = { dismissed++ }) {
Text("Body", Modifier.fillMaxWidth().height(200.dp).testTag("body"))
}
}
}

composeRule.onNodeWithTag("body").performTouchInput { swipeDown(durationMillis = 100) }
composeRule.waitForIdle()

composeRule.onNodeWithTag("body").assertExists()
assertEquals(0, dismissed)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollTo
import androidx.compose.ui.test.performTextReplacement
import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.test.swipeDown
import androidx.lifecycle.Lifecycle
import dev.tricked.solidverdant.ui.auth.OAuthConfigState
import org.junit.Assert.assertEquals
Expand All @@ -36,6 +38,7 @@ class ConfigScreenPasteTest {
val composeRule = createAndroidComposeRule<ComponentActivity>()

private var saved: Pair<String, String>? = null
private var dismissed = 0

private fun showSheetWithClipboard(text: String) {
val clipboard = composeRule.activity.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
Expand All @@ -47,7 +50,7 @@ class ConfigScreenPasteTest {
onSave = { endpoint, clientId -> saved = endpoint to clientId },
onReset = {},
onTestConnection = { _, _ -> },
onDismiss = {},
onDismiss = { dismissed++ },
)
}
}
Expand Down Expand Up @@ -91,6 +94,17 @@ class ConfigScreenPasteTest {
composeRule.onNodeWithTag(ConfigTestTags.CLIENT_ID_FIELD).assert(hasText("saved-client"))
}

@Test
fun swiping_down_inside_the_form_does_not_dismiss_the_sheet() {
showSheetWithClipboard("https://clip.example:$CLIP_CLIENT")

composeRule.onNodeWithTag(ConfigTestTags.ENDPOINT_FIELD).performTouchInput { swipeDown(durationMillis = 100) }
composeRule.waitForIdle()

composeRule.onNodeWithTag(ConfigTestTags.ENDPOINT_FIELD).assertExists()
assertEquals(0, dismissed)
}

private companion object {
const val CLIP_CLIENT = "9f3c2a71-5d1e-4c9b-a0f2-1b7e6d4c8a90"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollToNode
import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.test.swipeDown
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
Expand Down Expand Up @@ -42,9 +44,11 @@ class StatDrillDownSheetTest {
billable = index % 2 == 0,
)

private var dismissed = 0

private fun show(state: DrillDownUiState) {
composeRule.setContent {
MaterialTheme { StatDrillDownSheet(state = state, onDismiss = {}) }
MaterialTheme { StatDrillDownSheet(state = state, onDismiss = { dismissed++ }) }
}
}

Expand Down Expand Up @@ -113,4 +117,15 @@ class StatDrillDownSheetTest {
assertEquals(1, retries)
composeRule.onNodeWithTag(StatDrillDownTestTags.row("e0")).assertExists()
}

@Test
fun swipingDownInsideTheListDoesNotDismissTheSheet() {
show(DrillDownUiState(target = target, isLoading = false, rows = listOf(row(0)), totalSeconds = 600))

composeRule.onNodeWithTag(StatDrillDownTestTags.LIST).performTouchInput { swipeDown(durationMillis = 100) }
composeRule.waitForIdle()

composeRule.onNodeWithTag(StatDrillDownTestTags.SHEET).assertExists()
assertEquals(0, dismissed)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollTo
import androidx.compose.ui.test.performTextInput
import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.test.swipeDown
import dev.tricked.solidverdant.data.model.Tag
import dev.tricked.solidverdant.data.repository.EntryTemplate
import dev.tricked.solidverdant.ui.components.EditTimeEntryTestTags
Expand All @@ -38,6 +40,7 @@ class TemplateEditorSheetTest {
private var saved: TemplateDraft? = null
private var updated: EntryTemplate? = null
private var deleted = 0
private var dismissed = 0

private fun show(template: EntryTemplate?, deletable: Boolean = true) {
composeRule.setContent {
Expand All @@ -47,7 +50,7 @@ class TemplateEditorSheetTest {
projects = emptyList(),
tasks = emptyList(),
tags = tags,
onDismiss = {},
onDismiss = { dismissed++ },
onSave = { saved = it },
onUpdate = { updated = it },
onDelete = if (deletable) ({ deleted++ }) else null,
Expand Down Expand Up @@ -112,4 +115,15 @@ class TemplateEditorSheetTest {

composeRule.onNodeWithTag(TemplateEditorTestTags.DELETE).assertDoesNotExist()
}

@Test
fun swipingDownInsideTheFormDoesNotDismissTheSheet() {
show(template = existing)

composeRule.onNodeWithTag(TemplateEditorTestTags.NAME).performTouchInput { swipeDown(durationMillis = 100) }
composeRule.waitForIdle()

composeRule.onNodeWithTag(TemplateEditorTestTags.SHEET).assertExists()
assertEquals(0, dismissed)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import androidx.compose.ui.test.junit4.v2.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollTo
import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.test.swipeDown
import dev.tricked.solidverdant.data.model.TimeEntry
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
Expand Down Expand Up @@ -115,4 +117,37 @@ class TimeEntryFormSheetTest {
composeRule.onNodeWithTag(TrackingTestTags.SHEET_SAVE_BUTTON).performClick()
assertEquals("Running work", saved)
}

@Test
fun swiping_down_inside_the_form_does_not_dismiss_the_sheet() {
var dismissCount = 0
composeRule.setContent {
MaterialTheme {
TimeEntryFormSheet(
entry = TimeEntry(
id = "completed",
userId = "user",
organizationId = "org",
start = "2026-08-21T08:00:00Z",
end = "2026-08-21T09:00:00Z",
description = "Milling",
),
zone = ZoneId.of("UTC"),
suggestedStart = null,
projects = emptyList(),
tasks = emptyList(),
tags = emptyList(),
onDismiss = { dismissCount++ },
onSave = { _, _, _, _, _, _, _ -> },
)
}
}

// The form is already scrolled to the top, so the whole swipe is leftover the sheet could take.
composeRule.onNodeWithTag(TrackingTestTags.SHEET_DESCRIPTION_FIELD).performTouchInput { swipeDown(durationMillis = 100) }
composeRule.waitForIdle()

composeRule.onNodeWithTag(TrackingTestTags.SHEET).assertExists()
assertEquals(0, dismissCount)
}
}
Loading