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
2 changes: 2 additions & 0 deletions app-shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ dependencies {
testImplementation(testFixtures(project(":core")))
androidTestImplementation(libs.androidx.test.ext.junit)
androidTestImplementation(libs.androidx.test.runner)
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
debugImplementation(libs.androidx.compose.ui.test.manifest)
}

// The shared fixtures (the shared spec) are not part of this public repository, see :core.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package io.github.maxdac.marknotes.ui.browser

import androidx.activity.ComponentActivity
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.github.maxdac.marknotes.sync.FolderRemovalNotice
import io.github.maxdac.marknotes.sync.SyncHooks
import io.github.maxdac.marknotes.sync.SyncUiState
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

/** Inert hooks; [DrawerSection] is left at its default. */
private open class InertHooks : SyncHooks {
override val available = false
override val state: StateFlow<SyncUiState> = MutableStateFlow(SyncUiState())
override val providerNames = ""
override val aboutNotices = emptyList<String>()

override fun syncIfConnected() = Unit

override fun syncNow() = Unit

override fun acknowledgeFolderNotice(notice: FolderRemovalNotice) = Unit

override fun onActivityResumed() = Unit

override fun watchNetwork(activity: ComponentActivity) = Unit

override fun onForeground(foreground: Boolean) = Unit

override fun onActivityFinishing() = Unit

override fun updateReconnectPolicy(enabled: Boolean, unlocked: Boolean) = Unit

override suspend fun deleteAllLocalData() = Unit
}

@RunWith(AndroidJUnit4::class)
class FolderDrawerSectionTest {
@get:Rule
val rule = createAndroidComposeRule<ComponentActivity>()

private fun show(hooks: SyncHooks) = rule.setContent {
val navController = rememberNavController()
FolderDrawer(listOf("a"), current = "", inSemantics = true, drawerSection = {
with(hooks) { DrawerSection(navController) }
}, onSelect = {})
}

/** The default [SyncHooks.DrawerSection] (what the libre app uses) renders nothing in the drawer. */
@Test
fun defaultHookRendersNothing() {
show(InertHooks())
rule.onNodeWithText("All notes").assertIsDisplayed()
rule.onNodeWithText("Fake section").assertDoesNotExist()
}

@Test
fun hookSectionRendersInDrawer() {
show(object : InertHooks() {
@Composable
override fun ColumnScope.DrawerSection(navController: NavController) {
Text("Fake section")
}
})
rule.onNodeWithText("All notes").assertIsDisplayed()
rule.onNodeWithText("Fake section").assertIsDisplayed()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,8 @@ private fun MarkNotesNavHost(
onOpenNote = { navController.navigate(Routes.editor(it)) },
onOpenSettings = { navController.navigate(Routes.SETTINGS) },
onOpenConflicts = { navController.navigate(Routes.CONFLICTS) },
onBack = { navController.navigateUp() }
onBack = { navController.navigateUp() },
drawerSection = { with(container.syncHooks) { DrawerSection(navController) } }
)
}
composable(Routes.EDITOR, arguments = pathArg) { entry ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,14 @@ interface SyncHooks {
onLocalDataDeleted: () -> Unit
) {
}

/**
* An extra block of the left drawer, shown after the notes tree (inside the drawer's column). The libre app and
* [NoSyncHooks] show nothing; an app can add its own section, which may navigate with [navController].
*/
@Composable
fun ColumnScope.DrawerSection(navController: NavController) {
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
Expand Down Expand Up @@ -116,7 +117,9 @@ fun BrowserScreen(
onOpenNote: (String) -> Unit,
onOpenSettings: () -> Unit,
onOpenConflicts: () -> Unit,
onBack: () -> Unit
onBack: () -> Unit,
/** Extra block after the folder tree in the drawer; see [SyncHooks.DrawerSection]. */
drawerSection: @Composable ColumnScope.() -> Unit = {}
) {
val container = LocalContext.current.appContainer
val vm: BrowserViewModel = viewModel { BrowserViewModel(container, folder) }
Expand All @@ -136,7 +139,12 @@ fun BrowserScreen(
drawerContent = {
// A closed drawer stays composed; hide its items from semantics so they don't duplicate the list.
val showTree = drawerState.isOpen || drawerState.isAnimationRunning
FolderDrawer(sortedFolders, current = folder, inSemantics = showTree) { target ->
FolderDrawer(
sortedFolders,
current = folder,
inSemantics = showTree,
drawerSection = drawerSection
) { target ->
scope.launch { drawerState.close() }
if (target != folder) onJumpToFolder(target)
}
Expand Down Expand Up @@ -441,10 +449,11 @@ private fun BrowserDialogs(

/** Folder tree; folders are collapsed except the ancestors of [current]. */
@Composable
private fun FolderDrawer(
internal fun FolderDrawer(
folders: List<String>,
current: String,
inSemantics: Boolean,
drawerSection: @Composable ColumnScope.() -> Unit = {},
onSelect: (String) -> Unit
) {
var expanded by rememberSaveable(current) {
Expand All @@ -457,7 +466,8 @@ private fun FolderDrawer(
}
ModalDrawerSheet(modifier = if (inSemantics) Modifier else Modifier.clearAndSetSemantics {}) {
Text("Folders", style = MaterialTheme.typography.titleMedium, modifier = Modifier.padding(16.dp))
LazyColumn {
// fill = false: the tree keeps its natural height (unchanged without a section) and scrolls above the section.
LazyColumn(Modifier.weight(1f, fill = false)) {
item {
NavigationDrawerItem(
label = { Text("All notes") },
Expand Down Expand Up @@ -490,6 +500,7 @@ private fun FolderDrawer(
)
}
}
drawerSection()
}
}

Expand Down
Loading