Skip to content

Commit ca273b7

Browse files
committed
feat: added local search functionality in browse screen
1 parent 755089c commit ca273b7

1 file changed

Lines changed: 69 additions & 8 deletions

File tree

mobile/app/src/main/java/com/xrontrix/lansync/ui/screens/BrowseScreen.kt

Lines changed: 69 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import androidx.compose.foundation.shape.RoundedCornerShape
1313
import androidx.compose.material.icons.Icons
1414
import androidx.compose.material.icons.filled.*
1515
import androidx.compose.material.icons.outlined.Folder
16+
import androidx.compose.material.icons.rounded.Search
17+
import androidx.compose.material.icons.rounded.Close
1618
import androidx.compose.material3.*
1719
import androidx.compose.material3.HorizontalDivider
1820
import androidx.compose.runtime.*
@@ -77,12 +79,26 @@ fun BrowseScreen(
7779
var showCreateFolderDialog by remember { mutableStateOf(false) }
7880
var newFolderName by remember { mutableStateOf("") }
7981
var folderError by remember { mutableStateOf("") }
82+
var searchQuery by remember { mutableStateOf("") }
83+
var isSearchActive by remember { mutableStateOf(false) }
8084

8185
BackHandler(enabled = currentPath != "/" && currentPath.isNotEmpty()) {
8286
onNavigate(parentPath.ifEmpty { "/" })
8387
}
8488

85-
LaunchedEffect(currentPath) { selectedFiles = emptySet() }
89+
LaunchedEffect(currentPath) {
90+
selectedFiles = emptySet()
91+
searchQuery = ""
92+
isSearchActive = false
93+
}
94+
95+
val displayFiles = remember(files, searchQuery) {
96+
if (searchQuery.isBlank()) {
97+
files
98+
} else {
99+
files.filter { it.name.contains(searchQuery, ignoreCase = true) }
100+
}
101+
}
86102

87103
val filePickerLauncher = rememberLauncherForActivityResult(contract = ActivityResultContracts.GetMultipleContents()) { uris: List<Uri> ->
88104
if (uris.isNotEmpty()) onUploadFiles(uris)
@@ -211,10 +227,48 @@ fun BrowseScreen(
211227
Column(modifier = Modifier.fillMaxSize()) {
212228

213229
Surface(color = BgBase, modifier = Modifier.fillMaxWidth()) {
214-
Row(modifier = Modifier.padding(horizontal = 16.dp, vertical = 16.dp), verticalAlignment = Alignment.CenterVertically) {
215-
DeviceIcon(activeDeviceOS, GreenAccent, Modifier.size(20.dp))
216-
Spacer(modifier = Modifier.width(12.dp))
217-
Text(activeDeviceName, color = TextPrimary, fontWeight = FontWeight.Black, fontSize = 18.sp, modifier = Modifier.weight(1f))
230+
// ── DYNAMIC HEADER: Device Name OR Search Bar ──
231+
if (isSearchActive) {
232+
OutlinedTextField(
233+
value = searchQuery,
234+
onValueChange = { searchQuery = it },
235+
placeholder = { Text("Search files...", color = TextMuted) },
236+
singleLine = true,
237+
textStyle = androidx.compose.ui.text.TextStyle(color = TextPrimary, fontSize = 14.sp),
238+
colors = OutlinedTextFieldDefaults.colors(
239+
focusedBorderColor = GreenAccent,
240+
unfocusedBorderColor = Surface,
241+
focusedContainerColor = Surface, // Darker inset look
242+
unfocusedContainerColor = Surface,
243+
focusedTextColor = TextPrimary
244+
),
245+
modifier = Modifier.weight(1f).height(50.dp),
246+
shape = RoundedCornerShape(12.dp),
247+
trailingIcon = {
248+
IconButton(
249+
onClick = {
250+
if (searchQuery.isNotEmpty()) {
251+
searchQuery = "" // Clear input
252+
} else {
253+
isSearchActive = false // Close search
254+
}
255+
}
256+
) {
257+
Icon(Icons.Rounded.Close, contentDescription = "Close", tint = TextMuted)
258+
}
259+
}
260+
)
261+
} else {
262+
Row(modifier = Modifier.padding(horizontal = 16.dp, vertical = 16.dp), verticalAlignment = Alignment.CenterVertically) {
263+
DeviceIcon(activeDeviceOS, GreenAccent, Modifier.size(20.dp))
264+
Spacer(modifier = Modifier.width(8.dp))
265+
Text(activeDeviceName, color = TextPrimary, fontWeight = FontWeight.Black, fontSize = 18.sp, modifier = Modifier.weight(1f))
266+
}
267+
268+
// ── Search Trigger Button ──
269+
IconButton(onClick = { isSearchActive = true }) {
270+
Icon(Icons.Rounded.Search, contentDescription = "Search", tint = TextPrimary)
271+
}
218272
}
219273
}
220274

@@ -279,7 +333,7 @@ fun BrowseScreen(
279333
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { CircularProgressIndicator(color = Accent) }
280334
} else {
281335
LazyColumn(contentPadding = PaddingValues(bottom = 80.dp)) {
282-
items(files) { file ->
336+
items(displayFiles) { file ->
283337
val isSelected = selectedFiles.contains(file)
284338
FileRowItem(
285339
file = file, isSelected = isSelected,
@@ -297,8 +351,15 @@ fun BrowseScreen(
297351
Column(modifier = Modifier.align(Alignment.BottomEnd).padding(16.dp), horizontalAlignment = Alignment.End) {
298352
SmallFloatingActionButton(
299353
onClick = onShareClipboardClick,
300-
containerColor = Color(0xFFa78bfa).copy(alpha = 0.15f), contentColor = Color(0xFFa78bfa),
301-
modifier = Modifier.padding(bottom = 12.dp), shape = RoundedCornerShape(12.dp)
354+
containerColor = Color(0xFFa78bfa).copy(alpha = 0.15f), // High transparency
355+
contentColor = Color(0xFFa78bfa),
356+
elevation = FloatingActionButtonDefaults.elevation(0.dp, 0.dp, 0.dp, 0.dp), // Remove shadow for true glass look
357+
modifier = Modifier
358+
.padding(bottom = 12.dp)
359+
// Optional: If running strictly on Android 12+, you can add actual blur here
360+
// .graphicsLayer { renderEffect = android.graphics.RenderEffect.createBlurEffect(20f, 20f, android.graphics.Shader.TileMode.CLAMP).asComposeRenderEffect() }
361+
,
362+
shape = RoundedCornerShape(12.dp)
302363
) {
303364
Icon(Icons.Filled.ContentPaste, contentDescription = "Share Clipboard")
304365
}

0 commit comments

Comments
 (0)