Skip to content

Commit 0303f9a

Browse files
committed
WIP
1 parent 870c213 commit 0303f9a

36 files changed

+1159
-299
lines changed

android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/IconCell.kt

+9-7
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ fun PreviewIconCell() {
2424

2525
@Composable
2626
fun IconCell(
27-
iconId: Int,
27+
iconId: Int?,
2828
contentDescription: String? = null,
2929
title: String,
3030
titleStyle: TextStyle = MaterialTheme.typography.labelLarge,
@@ -36,12 +36,14 @@ fun IconCell(
3636
BaseCell(
3737
headlineContent = {
3838
Row(verticalAlignment = Alignment.CenterVertically) {
39-
Icon(
40-
painter = painterResource(id = iconId),
41-
contentDescription = contentDescription,
42-
tint = titleColor
43-
)
44-
Spacer(modifier = Modifier.width(Dimens.mediumPadding))
39+
iconId?.let {
40+
Icon(
41+
painter = painterResource(id = iconId),
42+
contentDescription = contentDescription,
43+
tint = titleColor
44+
)
45+
Spacer(modifier = Modifier.width(Dimens.mediumPadding))
46+
}
4547
BaseCellTitle(title = title, style = titleStyle, color = titleColor)
4648
}
4749
},

android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/RelayLocationCell.kt

+19-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package net.mullvad.mullvadvpn.compose.cell
22

33
import androidx.compose.animation.animateContentSize
4+
import androidx.compose.foundation.ExperimentalFoundationApi
45
import androidx.compose.foundation.Image
56
import androidx.compose.foundation.background
67
import androidx.compose.foundation.clickable
8+
import androidx.compose.foundation.combinedClickable
79
import androidx.compose.foundation.layout.Box
810
import androidx.compose.foundation.layout.BoxScope
911
import androidx.compose.foundation.layout.Column
@@ -258,7 +260,8 @@ fun NormalRelayLocationCell(
258260
inactiveColor: Color = MaterialTheme.colorScheme.error,
259261
disabledColor: Color = MaterialTheme.colorScheme.onSecondary,
260262
selectedItem: RelayItem? = null,
261-
onSelectRelay: (item: RelayItem) -> Unit = {}
263+
onSelectRelay: (item: RelayItem) -> Unit = {},
264+
onLongClick: (item: RelayItem) -> Unit = {}
262265
) {
263266
RelayLocationCell(
264267
relay = relay,
@@ -304,6 +307,7 @@ fun NormalRelayLocationCell(
304307
}
305308
},
306309
onClick = onSelectRelay,
310+
onLongClick = onLongClick,
307311
depth = 0
308312
)
309313
}
@@ -326,17 +330,20 @@ fun CheckableRelayLocationCell(
326330
},
327331
modifier = modifier,
328332
onClick = { onRelayCheckedChange(it, !selectedRelays.contains(it)) },
333+
onLongClick = {},
329334
depth = 0
330335
)
331336
}
332337

338+
@OptIn(ExperimentalFoundationApi::class)
333339
@Composable
334340
private fun RelayLocationCell(
335341
relay: RelayItem,
336342
leadingContent: @Composable BoxScope.(relay: RelayItem) -> Unit,
337343
modifier: Modifier = Modifier,
338344
specialBackgroundColor: @Composable (relayItem: RelayItem) -> Color? = { null },
339345
onClick: (item: RelayItem) -> Unit,
346+
onLongClick: (item: RelayItem) -> Unit,
340347
depth: Int
341348
) {
342349
val startPadding =
@@ -376,7 +383,13 @@ private fun RelayLocationCell(
376383
)
377384
) {
378385
Row(
379-
modifier = Modifier.weight(1f).clickable(enabled = relay.active) { onClick(relay) }
386+
modifier =
387+
Modifier.weight(1f)
388+
.combinedClickable(
389+
enabled = relay.active,
390+
onClick = { onClick(relay) },
391+
onLongClick = { onLongClick(relay) },
392+
)
380393
) {
381394
Box(
382395
modifier =
@@ -428,7 +441,8 @@ private fun RelayLocationCell(
428441
modifier = Modifier.animateContentSize(),
429442
leadingContent = leadingContent,
430443
specialBackgroundColor = specialBackgroundColor,
431-
depth = depth + 1
444+
onLongClick = onLongClick,
445+
depth = depth + 1,
432446
)
433447
}
434448
}
@@ -440,6 +454,7 @@ private fun RelayLocationCell(
440454
modifier = Modifier.animateContentSize(),
441455
leadingContent = leadingContent,
442456
specialBackgroundColor = specialBackgroundColor,
457+
onLongClick = onLongClick,
443458
depth = depth + 1
444459
)
445460
}
@@ -452,6 +467,7 @@ private fun RelayLocationCell(
452467
modifier = Modifier.animateContentSize(),
453468
leadingContent = leadingContent,
454469
specialBackgroundColor = specialBackgroundColor,
470+
onLongClick = onLongClick,
455471
depth = depth + 1
456472
)
457473
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package net.mullvad.mullvadvpn.compose.communication
2+
3+
import net.mullvad.mullvadvpn.relaylist.Location
4+
5+
sealed interface CustomListAction {
6+
data class Rename(val customListId: String, val name: String) : CustomListAction {
7+
fun not(oldName: String): CustomListAction = this.copy(name = oldName)
8+
}
9+
10+
data class Delete(val customListId: String) : CustomListAction {
11+
fun not(name: String, locations: List<Location>): CustomListAction = Create(name, locations)
12+
}
13+
14+
data class Create(val name: String, val locations: List<Location> = emptyList()) :
15+
CustomListAction {
16+
fun not(customListId: String) = Delete(customListId)
17+
}
18+
19+
data class UpdateLocations(val customListId: String, val newList: Boolean) : CustomListAction {
20+
fun not(locations: List<Location>): CustomListAction =
21+
RemoveLocations(customListId, locations)
22+
}
23+
24+
data class RemoveLocations(val customListId: String, val locations: List<Location>) :
25+
CustomListAction
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package net.mullvad.mullvadvpn.compose.communication
2+
3+
sealed interface CustomListRequest {
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package net.mullvad.mullvadvpn.compose.communication
2+
3+
@JvmInline value class Request<T : CustomListAction>(val action: T)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package net.mullvad.mullvadvpn.compose.communication
2+
3+
data class Result<T : CustomListAction>(val reverseAction: T, val messageParams: List<String>)

android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scaffolding.kt

+31
Original file line numberDiff line numberDiff line change
@@ -255,3 +255,34 @@ fun ScaffoldWithLargeTopBarAndButton(
255255
}
256256
)
257257
}
258+
259+
@OptIn(ExperimentalMaterial3Api::class)
260+
@Composable
261+
fun ScaffoldWithSmallTopBar(
262+
appBarTitle: String,
263+
modifier: Modifier = Modifier,
264+
lazyListState: LazyListState = rememberLazyListState(),
265+
navigationIcon: @Composable () -> Unit = {},
266+
actions: @Composable RowScope.() -> Unit = {},
267+
scrollbarColor: Color = MaterialTheme.colorScheme.onBackground.copy(alpha = AlphaScrollbar),
268+
content: @Composable (modifier: Modifier, lazyListState: LazyListState) -> Unit
269+
) {
270+
Scaffold(
271+
modifier = modifier.fillMaxSize(),
272+
topBar = {
273+
MullvadSmallTopBar(
274+
title = appBarTitle,
275+
navigationIcon = navigationIcon,
276+
actions = actions
277+
)
278+
},
279+
content = {
280+
content(
281+
Modifier.fillMaxSize()
282+
.padding(it)
283+
.drawVerticalScrollbar(state = lazyListState, color = scrollbarColor),
284+
lazyListState
285+
)
286+
}
287+
)
288+
}

android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/TopBar.kt

+19
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,25 @@ fun MullvadTopBar(
189189
)
190190
}
191191

192+
@Composable
193+
fun MullvadSmallTopBar(
194+
title: String,
195+
navigationIcon: @Composable () -> Unit = {},
196+
actions: @Composable RowScope.() -> Unit = {}
197+
) {
198+
TopAppBar(
199+
title = { Text(text = title, maxLines = 1, overflow = TextOverflow.Ellipsis) },
200+
navigationIcon = navigationIcon,
201+
colors =
202+
TopAppBarDefaults.topAppBarColors(
203+
containerColor = MaterialTheme.colorScheme.background,
204+
scrolledContainerColor = MaterialTheme.colorScheme.background,
205+
actionIconContentColor = MaterialTheme.colorScheme.onPrimary.copy(AlphaTopBar),
206+
),
207+
actions = actions
208+
)
209+
}
210+
192211
@Preview
193212
@Composable
194213
private fun PreviewMediumTopBar() {

android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/CreateCustomListDialog.kt

+32-4
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,23 @@ import androidx.compose.ui.res.stringResource
1818
import androidx.compose.ui.text.input.KeyboardType
1919
import androidx.compose.ui.tooling.preview.Preview
2020
import com.ramcosta.composedestinations.annotation.Destination
21+
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
2122
import com.ramcosta.composedestinations.result.ResultBackNavigator
2223
import com.ramcosta.composedestinations.spec.DestinationStyle
2324
import net.mullvad.mullvadvpn.R
2425
import net.mullvad.mullvadvpn.compose.button.PrimaryButton
26+
import net.mullvad.mullvadvpn.compose.communication.CustomListAction
27+
import net.mullvad.mullvadvpn.compose.communication.Request
28+
import net.mullvad.mullvadvpn.compose.communication.Result
29+
import net.mullvad.mullvadvpn.compose.destinations.CustomListLocationsDestination
2530
import net.mullvad.mullvadvpn.compose.state.CreateCustomListUiState
2631
import net.mullvad.mullvadvpn.compose.textfield.CustomTextField
2732
import net.mullvad.mullvadvpn.lib.theme.AppTheme
2833
import net.mullvad.mullvadvpn.model.CustomListsError
2934
import net.mullvad.mullvadvpn.viewmodel.CreateCustomListDialogSideEffect
3035
import net.mullvad.mullvadvpn.viewmodel.CreateCustomListDialogViewModel
3136
import org.koin.androidx.compose.koinViewModel
37+
import org.koin.core.parameter.parametersOf
3238

3339
@Preview
3440
@Composable
@@ -48,13 +54,35 @@ fun PreviewCreateCustomListDialogError() {
4854

4955
@Composable
5056
@Destination(style = DestinationStyle.Dialog::class)
51-
fun CreateCustomList(backNavigator: ResultBackNavigator<String>) {
52-
val vm: CreateCustomListDialogViewModel = koinViewModel()
57+
fun CreateCustomList(
58+
navigator: DestinationsNavigator,
59+
backNavigator: ResultBackNavigator<Result<CustomListAction.Delete>>,
60+
request: Request<CustomListAction.Create>
61+
) {
62+
val vm: CreateCustomListDialogViewModel =
63+
koinViewModel(parameters = { parametersOf(request.action.locations) })
5364
LaunchedEffect(key1 = Unit) {
5465
vm.uiSideEffect.collect { sideEffect ->
5566
when (sideEffect) {
56-
is CreateCustomListDialogSideEffect.NavigateToCustomListScreen -> {
57-
backNavigator.navigateBack(sideEffect.customListId)
67+
is CreateCustomListDialogSideEffect.NavigateToCustomListLocationsScreen -> {
68+
navigator.popBackStack()
69+
navigator.navigate(
70+
CustomListLocationsDestination(
71+
request =
72+
Request(
73+
action =
74+
CustomListAction.UpdateLocations(
75+
customListId = sideEffect.customListId,
76+
newList = true
77+
)
78+
)
79+
)
80+
) {
81+
launchSingleTop = true
82+
}
83+
}
84+
is CreateCustomListDialogSideEffect.ReturnWithResult -> {
85+
backNavigator.navigateBack(result = sideEffect.result)
5886
}
5987
}
6088
}

android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeleteCustomListConfirmationDialog.kt

+7-19
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,13 @@ import androidx.compose.ui.focus.focusRequester
1414
import androidx.compose.ui.graphics.Color
1515
import androidx.compose.ui.res.painterResource
1616
import androidx.compose.ui.res.stringResource
17-
import androidx.compose.ui.text.font.FontWeight
1817
import androidx.compose.ui.tooling.preview.Preview
19-
import androidx.core.text.HtmlCompat
2018
import com.ramcosta.composedestinations.annotation.Destination
2119
import com.ramcosta.composedestinations.result.ResultBackNavigator
2220
import com.ramcosta.composedestinations.spec.DestinationStyle
2321
import net.mullvad.mullvadvpn.R
2422
import net.mullvad.mullvadvpn.compose.button.NegativeButton
2523
import net.mullvad.mullvadvpn.compose.button.PrimaryButton
26-
import net.mullvad.mullvadvpn.compose.component.textResource
27-
import net.mullvad.mullvadvpn.compose.extensions.toAnnotatedString
2824
import net.mullvad.mullvadvpn.lib.theme.AppTheme
2925
import net.mullvad.mullvadvpn.lib.theme.Dimens
3026
import net.mullvad.mullvadvpn.viewmodel.DeleteCustomListConfirmationSideEffect
@@ -40,23 +36,23 @@ private fun PreviewRemoveDeviceConfirmationDialog() {
4036

4137
@Composable
4238
@Destination(style = DestinationStyle.Dialog::class)
43-
fun DeleteCustomList(navigator: ResultBackNavigator<Boolean>, id: String, name: String) {
39+
fun DeleteCustomList(navigator: ResultBackNavigator<String>, id: String, name: String) {
4440
val viewModel: DeleteCustomListConfirmationViewModel =
4541
koinViewModel(parameters = { parametersOf(id) })
4642

4743
LaunchedEffect(Unit) {
4844
viewModel.uiSideEffect.collect {
4945
when (it) {
5046
is DeleteCustomListConfirmationSideEffect.CloseDialog ->
51-
navigator.navigateBack(result = true)
47+
navigator.navigateBack(result = name)
5248
}
5349
}
5450
}
5551

5652
DeleteCustomListConfirmationDialog(
5753
name = name,
5854
onDelete = viewModel::deleteCustomList,
59-
onBack = { navigator.navigateBack(result = false) }
55+
onBack = { navigator.navigateBack() }
6056
)
6157
}
6258

@@ -76,18 +72,10 @@ fun DeleteCustomListConfirmationDialog(
7672
tint = Color.Unspecified
7773
)
7874
},
79-
text = {
80-
val htmlFormattedDialogText =
81-
HtmlCompat.fromHtml(
82-
textResource(id = R.string.delete_custom_list_confirmation_description, name),
83-
HtmlCompat.FROM_HTML_MODE_COMPACT
84-
)
85-
val annotatedText = htmlFormattedDialogText.toAnnotatedString(FontWeight.Bold)
86-
75+
title = {
8776
Text(
88-
text = annotatedText,
89-
color = MaterialTheme.colorScheme.onBackground,
90-
style = MaterialTheme.typography.bodySmall,
77+
text =
78+
stringResource(id = R.string.delete_custom_list_confirmation_description, name)
9179
)
9280
},
9381
dismissButton = {
@@ -98,7 +86,7 @@ fun DeleteCustomListConfirmationDialog(
9886
)
9987
},
10088
confirmButton = {
101-
NegativeButton(onClick = onDelete, text = stringResource(id = R.string.delete_list))
89+
NegativeButton(onClick = onDelete, text = stringResource(id = R.string.delete))
10290
},
10391
containerColor = MaterialTheme.colorScheme.background
10492
)

0 commit comments

Comments
 (0)