Skip to content

Commit a9d180b

Browse files
committed
Merge branch 'release/1.2.0' into main
2 parents 5ee8df4 + fbbdf15 commit a9d180b

File tree

66 files changed

+1655
-145
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+1655
-145
lines changed

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ android {
2323

2424
defaultConfig {
2525
applicationId = "com.twofasapp.pass"
26-
versionName = "1.1.6"
27-
versionCode = 26
26+
versionName = "1.2.0"
27+
versionCode = 27
2828
}
2929

3030
applicationVariants.all {

core/android/src/main/java/com/twofasapp/core/android/navigation/Screen.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ sealed class Screen(
109109
@Serializable
110110
data object Customization : Screen()
111111

112+
@Serializable
113+
data object ManageTags : Screen()
114+
112115
@Serializable
113116
data object KnownBrowsers : Screen()
114117

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.twofasapp.core.common.domain
2+
3+
data class Tag(
4+
val id: String,
5+
val vaultId: String,
6+
val name: String,
7+
val color: String?,
8+
val position: Int,
9+
val updatedAt: Long,
10+
val assignedItemsCount: Int,
11+
) {
12+
companion object {
13+
val Empty = Tag(
14+
id = "",
15+
vaultId = "",
16+
name = "",
17+
color = null,
18+
position = 0,
19+
updatedAt = 0,
20+
assignedItemsCount = 0,
21+
)
22+
}
23+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package com.twofasapp.core.common.ktx
2+
3+
fun <T> List<T>.toggle(item: T): List<T> =
4+
if (any { it == item }) filterNot { it == item } else this + item
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package com.twofasapp.core.common.ktx
2+
3+
const val StringListSeparator = "«§»"

core/design/src/main/java/com/twofasapp/core/design/AppTheme.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ fun AppTheme(
7979
background = colorScheme.background,
8080
onBackground = colorScheme.onBackground,
8181
success = if (isInDarkTheme) successDark else successLight,
82+
notice = colorScheme.error,
8283
surface = colorScheme.surface,
8384
onSurface = colorScheme.onSurface,
8485
surfaceVariant = colorScheme.surfaceVariant,

core/design/src/main/java/com/twofasapp/core/design/MdtIcons.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ object MdtIcons {
4545
val ChevronUp @Composable get() = painterResource(R.drawable.ic_chevron_up)
4646
val ChevronDown @Composable get() = painterResource(R.drawable.ic_chevron_down)
4747
val Check @Composable get() = painterResource(R.drawable.ic_check)
48+
val CircleFilled @Composable get() = painterResource(R.drawable.ic_circle_filled)
4849
val CircleCheck @Composable get() = painterResource(R.drawable.ic_circle_check)
4950
val CircleCheckFilled @Composable get() = painterResource(R.drawable.ic_circle_check_filled)
5051
val CircleCheckThinFilled @Composable get() = painterResource(R.drawable.ic_circle_check_thin_filled)
@@ -73,6 +74,7 @@ object MdtIcons {
7374
val Document @Composable get() = painterResource(R.drawable.ic_document)
7475
val PasswordGenerator @Composable get() = painterResource(R.drawable.ic_password_generator)
7576
val Filter @Composable get() = painterResource(R.drawable.ic_filter)
77+
val FilterAlt @Composable get() = painterResource(R.drawable.ic_filter_alt)
7678
val Sort @Composable get() = painterResource(R.drawable.ic_sort)
7779
val LockTime @Composable get() = painterResource(R.drawable.ic_lock_time)
7880
val LockAttempts @Composable get() = painterResource(R.drawable.ic_lock_attempts)
@@ -106,6 +108,7 @@ object MdtIcons {
106108
val AddCircle @Composable get() = painterResource(R.drawable.ic_add_circle)
107109
val RotateLeft @Composable get() = painterResource(R.drawable.ic_rotate_left)
108110
val Tag @Composable get() = painterResource(R.drawable.ic_tag)
111+
val AddTag @Composable get() = painterResource(R.drawable.ic_add_tag)
109112
val Hashtag @Composable get() = painterResource(R.drawable.ic_hashtag)
110113
val Rocket @Composable get() = painterResource(R.drawable.ic_rocket)
111114
val RocketLaunch @Composable get() = painterResource(R.drawable.ic_rocket_launch)

core/design/src/main/java/com/twofasapp/core/design/feature/password/PasswordGeneratorForm.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ fun PasswordGeneratorForm(
8888
.padding(horizontal = 16.dp)
8989
.fillMaxWidth()
9090
.heightIn(min = 100.dp)
91-
.background(MdtTheme.color.surfaceContainer, RoundedShape12)
91+
.background(MdtTheme.color.surfaceContainerHigh, RoundedShape12)
9292
.padding(horizontal = 16.dp)
9393
.animateContentSize(),
9494
horizontalAlignment = Alignment.CenterHorizontally,
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package com.twofasapp.core.design.feature.tags
2+
3+
import androidx.compose.foundation.text.KeyboardOptions
4+
import androidx.compose.runtime.Composable
5+
import androidx.compose.ui.text.input.KeyboardCapitalization
6+
import androidx.compose.ui.tooling.preview.Preview
7+
import com.twofasapp.core.common.domain.Tag
8+
import com.twofasapp.core.design.MdtIcons
9+
import com.twofasapp.core.design.foundation.dialog.InputDialog
10+
import com.twofasapp.core.design.foundation.dialog.InputValidation
11+
import com.twofasapp.core.design.foundation.preview.PreviewTheme
12+
import com.twofasapp.core.locale.MdtLocale
13+
14+
@Composable
15+
fun TagDialog(
16+
onDismissRequest: () -> Unit,
17+
tag: Tag,
18+
onSaveClick: (Tag) -> Unit = {},
19+
) {
20+
InputDialog(
21+
onDismissRequest = onDismissRequest,
22+
title = if (tag.id.isEmpty()) {
23+
MdtLocale.strings.tagEditorNewTitle
24+
} else {
25+
MdtLocale.strings.tagEditorEditTitle
26+
},
27+
body = MdtLocale.strings.tagEditorDescription,
28+
icon = if (tag.id.isEmpty()) {
29+
MdtIcons.AddTag
30+
} else {
31+
MdtIcons.Tag
32+
},
33+
prefill = tag.name,
34+
label = MdtLocale.strings.tagEditorPlaceholder,
35+
keyboardOptions = KeyboardOptions(
36+
capitalization = KeyboardCapitalization.Sentences,
37+
),
38+
onPositive = { text ->
39+
onSaveClick(
40+
tag.copy(
41+
name = text.trim(),
42+
),
43+
)
44+
},
45+
validate = { text ->
46+
if (text.isBlank()) {
47+
InputValidation.Invalid("Name can not be empty")
48+
} else if (text.length > 1000) {
49+
InputValidation.Invalid("Max length is 1000 characters")
50+
} else {
51+
InputValidation.Valid
52+
}
53+
},
54+
)
55+
}
56+
57+
@Preview
58+
@Composable
59+
private fun Preview() {
60+
PreviewTheme {
61+
TagDialog(
62+
onDismissRequest = {},
63+
tag = Tag.Empty,
64+
)
65+
}
66+
}

core/design/src/main/java/com/twofasapp/core/design/foundation/dialog/InputDialog.kt

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ import androidx.compose.ui.graphics.painter.Painter
2828
import androidx.compose.ui.input.key.onKeyEvent
2929
import androidx.compose.ui.text.AnnotatedString
3030
import androidx.compose.ui.text.TextRange
31-
import androidx.compose.ui.text.input.ImeAction
32-
import androidx.compose.ui.text.input.KeyboardType
3331
import androidx.compose.ui.text.input.TextFieldValue
3432
import androidx.compose.ui.tooling.preview.Preview
3533
import androidx.compose.ui.window.DialogProperties
@@ -67,6 +65,7 @@ fun InputDialog(
6765
neutralColor: Color = Color.Unspecified,
6866
actionsAlignment: ActionsAlignment = ActionsAlignment.Horizontal,
6967
properties: DialogProperties = DialogProperties(),
68+
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
7069
) {
7170
val focusRequester = remember { FocusRequester() }
7271
var textFieldValue by remember {
@@ -77,8 +76,16 @@ fun InputDialog(
7776
),
7877
)
7978
}
80-
val inputValidation by remember { derivedStateOf { validate(textFieldValue.text) } }
8179
var startedTyping by remember { mutableStateOf(false) }
80+
val inputValidation by remember {
81+
derivedStateOf {
82+
if (startedTyping.not() && textFieldValue.text.isEmpty()) {
83+
InputValidation.Invalid(null)
84+
} else {
85+
validate(textFieldValue.text)
86+
}
87+
}
88+
}
8289

8390
LaunchedEffect(Unit) {
8491
awaitFrame()
@@ -123,7 +130,7 @@ fun InputDialog(
123130
maxLines = 1,
124131
supportingText = (inputValidation as? InputValidation.Invalid)?.error.orEmpty(),
125132
isError = startedTyping && inputValidation is InputValidation.Invalid,
126-
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, imeAction = ImeAction.Done),
133+
keyboardOptions = keyboardOptions,
127134
keyboardActions = if (inputValidation is InputValidation.Valid) {
128135
KeyboardActions(
129136
onDone = { onPositive(textFieldValue.text) },

0 commit comments

Comments
 (0)