Skip to content

Commit e500111

Browse files
authored
android: put new search behind flag (#587)
Until the WIP feature is ready Updates tailscale/corp#18973 Signed-off-by: kari-ts <[email protected]> Signed-off-by: kari-ts <[email protected]>
1 parent ebbc1b2 commit e500111

File tree

3 files changed

+84
-3
lines changed

3 files changed

+84
-3
lines changed

android/src/main/java/com/tailscale/ipn/App.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import com.tailscale.ipn.ui.notifier.Notifier
3535
import com.tailscale.ipn.ui.viewModel.VpnViewModel
3636
import com.tailscale.ipn.ui.viewModel.VpnViewModelFactory
3737
import com.tailscale.ipn.util.TSLog
38+
import com.tailscale.ipn.util.FeatureFlags
3839
import kotlinx.coroutines.CoroutineScope
3940
import kotlinx.coroutines.Dispatchers
4041
import kotlinx.coroutines.SupervisorJob
@@ -191,6 +192,7 @@ class App : UninitializedApp(), libtailscale.AppContext, ViewModelStoreOwner {
191192
val hideDisconnectAction = MDMSettings.forceEnabled.flow.first()
192193
}
193194
TSLog.init(this)
195+
FeatureFlags.initialize(mapOf("enable_new_search" to false))
194196
}
195197

196198
private fun initViewModels() {

android/src/main/java/com/tailscale/ipn/ui/view/MainView.kt

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ import androidx.compose.foundation.shape.RoundedCornerShape
2727
import androidx.compose.material.icons.Icons
2828
import androidx.compose.material.icons.filled.Search
2929
import androidx.compose.material.icons.outlined.ArrowDropDown
30+
import androidx.compose.material.icons.outlined.Clear
31+
import androidx.compose.material.icons.outlined.Close
3032
import androidx.compose.material.icons.outlined.Lock
33+
import androidx.compose.material.icons.outlined.Search
3134
import androidx.compose.material.icons.outlined.Settings
3235
import androidx.compose.material3.Button
3336
import androidx.compose.material3.DropdownMenu
@@ -39,6 +42,7 @@ import androidx.compose.material3.ListItem
3942
import androidx.compose.material3.ListItemDefaults
4043
import androidx.compose.material3.MaterialTheme
4144
import androidx.compose.material3.ModalBottomSheet
45+
import androidx.compose.material3.OutlinedTextField
4246
import androidx.compose.material3.Scaffold
4347
import androidx.compose.material3.Text
4448
import androidx.compose.runtime.Composable
@@ -56,6 +60,7 @@ import androidx.compose.ui.draw.clip
5660
import androidx.compose.ui.focus.onFocusChanged
5761
import androidx.compose.ui.graphics.Color
5862
import androidx.compose.ui.platform.LocalClipboardManager
63+
import androidx.compose.ui.platform.LocalFocusManager
5964
import androidx.compose.ui.res.painterResource
6065
import androidx.compose.ui.res.stringResource
6166
import androidx.compose.ui.text.SpanStyle
@@ -83,6 +88,7 @@ import com.tailscale.ipn.ui.theme.exitNodeToggleButton
8388
import com.tailscale.ipn.ui.theme.listItem
8489
import com.tailscale.ipn.ui.theme.minTextSize
8590
import com.tailscale.ipn.ui.theme.primaryListItem
91+
import com.tailscale.ipn.ui.theme.searchBarColors
8692
import com.tailscale.ipn.ui.theme.secondaryButton
8793
import com.tailscale.ipn.ui.theme.short
8894
import com.tailscale.ipn.ui.theme.surfaceContainerListItem
@@ -98,6 +104,7 @@ import com.tailscale.ipn.ui.util.set
98104
import com.tailscale.ipn.ui.viewModel.IpnViewModel.NodeState
99105
import com.tailscale.ipn.ui.viewModel.MainViewModel
100106
import com.tailscale.ipn.ui.viewModel.VpnViewModel
107+
import com.tailscale.ipn.util.FeatureFlags
101108

102109
// Navigation actions for the MainView
103110
data class MainViewNavigation(
@@ -215,7 +222,8 @@ fun MainView(
215222
PeerList(
216223
viewModel = viewModel,
217224
onNavigateToPeerDetails = navigation.onNavigateToPeerDetails,
218-
onSearchBarClick = navigation.onNavigateToSearch)
225+
onSearchBarClick = navigation.onNavigateToSearch,
226+
onSearch = { viewModel.searchPeers(it) })
219227
}
220228
Ipn.State.NoState,
221229
Ipn.State.Starting -> StartingView()
@@ -518,24 +526,69 @@ fun ConnectView(
518526
fun PeerList(
519527
viewModel: MainViewModel,
520528
onNavigateToPeerDetails: (Tailcfg.Node) -> Unit,
521-
onSearchBarClick: () -> Unit
529+
onSearchBarClick: () -> Unit,
530+
onSearch: (String) -> Unit
522531
) {
523532
val peerList by viewModel.peers.collectAsState(initial = emptyList<PeerSet>())
524533
val searchTermStr by viewModel.searchTerm.collectAsState(initial = "")
525534
val showNoResults =
526535
remember { derivedStateOf { searchTermStr.isNotEmpty() && peerList.isEmpty() } }.value
527536

528537
val netmap = viewModel.netmap.collectAsState()
538+
val focusManager = LocalFocusManager.current
539+
var isSearchFocussed by remember { mutableStateOf(false) }
529540
var isListFocussed by remember { mutableStateOf(false) }
530541
val expandedPeer = viewModel.expandedMenuPeer.collectAsState()
531542
val localClipboardManager = LocalClipboardManager.current
532543
val enableSearch = !isAndroidTV()
533544

534545
Column(modifier = Modifier.fillMaxSize()) {
535-
if (enableSearch) {
546+
if (enableSearch && FeatureFlags.isEnabled("enable_new_search")) {
536547
Search(onSearchBarClick)
537548

538549
Spacer(modifier = Modifier.height(if (showNoResults) 0.dp else 8.dp))
550+
} else {
551+
if (enableSearch) {
552+
Box(
553+
modifier =
554+
Modifier.fillMaxWidth().background(color = MaterialTheme.colorScheme.surface)) {
555+
OutlinedTextField(
556+
modifier =
557+
Modifier.fillMaxWidth()
558+
.padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 0.dp)
559+
.onFocusChanged { isSearchFocussed = it.isFocused },
560+
singleLine = true,
561+
shape = MaterialTheme.shapes.extraLarge,
562+
colors = MaterialTheme.colorScheme.searchBarColors,
563+
leadingIcon = {
564+
Icon(imageVector = Icons.Outlined.Search, contentDescription = "search")
565+
},
566+
trailingIcon = {
567+
if (isSearchFocussed) {
568+
IconButton(
569+
onClick = {
570+
focusManager.clearFocus()
571+
onSearch("")
572+
}) {
573+
Icon(
574+
imageVector =
575+
if (searchTermStr.isEmpty()) Icons.Outlined.Close
576+
else Icons.Outlined.Clear,
577+
contentDescription = "clear search",
578+
tint = MaterialTheme.colorScheme.onSurfaceVariant)
579+
}
580+
}
581+
},
582+
placeholder = {
583+
Text(
584+
text = stringResource(id = R.string.search),
585+
style = MaterialTheme.typography.bodyLarge,
586+
maxLines = 1)
587+
},
588+
value = searchTermStr,
589+
onValueChange = { onSearch(it) })
590+
}
591+
}
539592
}
540593

541594
// Peers display
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright (c) Tailscale Inc & AUTHORS
2+
// SPDX-License-Identifier: BSD-3-Clause
3+
package com.tailscale.ipn.util
4+
5+
object FeatureFlags {
6+
7+
// Map to hold the feature flags
8+
private val flags: MutableMap<String, Boolean> = mutableMapOf()
9+
10+
fun initialize(defaults: Map<String, Boolean>) {
11+
flags.clear()
12+
flags.putAll(defaults)
13+
}
14+
15+
fun enable(feature: String) {
16+
flags[feature] = true
17+
}
18+
19+
fun disable(feature: String) {
20+
flags[feature] = false
21+
}
22+
23+
fun isEnabled(feature: String): Boolean {
24+
return flags[feature] ?: false
25+
}
26+
}

0 commit comments

Comments
 (0)