Android overhaul: the §9.6 text and date limits in :core:design's Limits.kt
DESCRIPTION_MAX (S-7 and S-19 each had 100), NOTES_MAX (S-7's 1000 and NotesField's private copy), FIRST_DATE and DAYS_AHEAD_MAX (the S-7 and S-9 date pickers each spelled 2000-01-01 and 365). Values unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
This commit is contained in:
@@ -22,9 +22,9 @@ import androidx.compose.foundation.selection.selectable
|
||||
import androidx.compose.foundation.selection.selectableGroup
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -52,10 +52,10 @@ import androidx.compose.ui.semantics.testTag
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.rememberTextMeasurer
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -65,6 +65,7 @@ import se.hagstedt.kvitt.design.format.AmountKey
|
||||
import se.hagstedt.kvitt.design.format.DateFormatter
|
||||
import se.hagstedt.kvitt.design.format.KvittLanguage
|
||||
import se.hagstedt.kvitt.design.format.MoneyFormatter
|
||||
import se.hagstedt.kvitt.design.format.NOTES_MAX
|
||||
import se.hagstedt.kvitt.design.icon.CheckBadgeIcon
|
||||
import se.hagstedt.kvitt.design.icon.KvittIcon
|
||||
import se.hagstedt.kvitt.design.theme.KvittDimens
|
||||
@@ -255,7 +256,7 @@ fun CategoryQuickRow(categories: List<Category>, selected: CategoryId?, onPick:
|
||||
|
||||
/**
|
||||
* One category item of the quick row or the grid sheet. The check badge of a selected item overhangs the tile's top-right
|
||||
* corner; it is drawn in a layer above the clipped, clickable item so the item's ripple clip never cuts it (M8b).
|
||||
* corner; it is drawn in a layer above the clipped, clickable item so the item's ripple clip never cuts it.
|
||||
*/
|
||||
@Composable
|
||||
fun CategoryItem(category: Category, selected: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
@@ -636,7 +637,6 @@ fun NotesField(value: String, onValueChange: (String) -> Unit, expanded: Boolean
|
||||
)
|
||||
}
|
||||
|
||||
private const val NOTES_MAX = 1000
|
||||
|
||||
/**
|
||||
* The compact row pinned under the app bar when the amount has scrolled away (design-spec §3.7): tile 36,
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
package se.hagstedt.kvitt.design.format
|
||||
|
||||
import kotlinx.datetime.LocalDate
|
||||
|
||||
/** Basis points of a whole: a ratio's per-user shares sum to 10000 bp, 100 % (SPEC §1, §9.3). */
|
||||
const val BP_TOTAL = 10_000
|
||||
|
||||
/** A user's display name is 1 to 40 code points after trimming (SPEC §9.6). */
|
||||
const val DISPLAY_NAME_MAX = 40
|
||||
|
||||
/** An expense's or recurring template's description is 1 to 100 code points (SPEC §9.6). */
|
||||
const val DESCRIPTION_MAX = 100
|
||||
|
||||
/** An expense's notes are 0 to 1000 code points (SPEC §9.6). */
|
||||
const val NOTES_MAX = 1000
|
||||
|
||||
/** The earliest expense or settlement date (SPEC §9.6). */
|
||||
val FIRST_DATE: LocalDate = LocalDate(2000, 1, 1)
|
||||
|
||||
/** The latest expense or settlement date is today plus this many days (SPEC §9.6). */
|
||||
const val DAYS_AHEAD_MAX = 365
|
||||
|
||||
+9
-9
@@ -55,7 +55,6 @@ import kotlinx.datetime.DateTimeUnit
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.plus
|
||||
import se.hagstedt.kvitt.design.KvittTheme
|
||||
import se.hagstedt.kvitt.core.design.R as DesignR
|
||||
import se.hagstedt.kvitt.design.component.AccountPicker
|
||||
import se.hagstedt.kvitt.design.component.AmountDisplay
|
||||
import se.hagstedt.kvitt.design.component.AmountKeypad
|
||||
@@ -90,6 +89,10 @@ import se.hagstedt.kvitt.design.component.splitOption
|
||||
import se.hagstedt.kvitt.design.component.viewerPercentOf
|
||||
import se.hagstedt.kvitt.design.format.AmountInput
|
||||
import se.hagstedt.kvitt.design.format.AmountKey
|
||||
import se.hagstedt.kvitt.design.format.DAYS_AHEAD_MAX
|
||||
import se.hagstedt.kvitt.design.format.DESCRIPTION_MAX
|
||||
import se.hagstedt.kvitt.design.format.FIRST_DATE
|
||||
import se.hagstedt.kvitt.design.format.NOTES_MAX
|
||||
import se.hagstedt.kvitt.design.icon.KvittIcon
|
||||
import se.hagstedt.kvitt.model.AccountId
|
||||
import se.hagstedt.kvitt.model.CategoryId
|
||||
@@ -97,6 +100,7 @@ import se.hagstedt.kvitt.model.ExpenseId
|
||||
import se.hagstedt.kvitt.model.FieldError
|
||||
import se.hagstedt.kvitt.model.SplitMode
|
||||
import se.hagstedt.kvitt.model.UserId
|
||||
import se.hagstedt.kvitt.core.design.R as DesignR
|
||||
|
||||
/** What S-7 can ask its state holder to do; [ExpenseEditorViewModel] implements it. */
|
||||
interface ExpenseEditorActions {
|
||||
@@ -400,8 +404,8 @@ private fun EditorDatePicker(date: String, today: String, actions: ExpenseEditor
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun LocalizedDatePicker(date: String, today: String, actions: ExpenseEditorActions, inline: Boolean) {
|
||||
val first = remember { LocalDate(FIRST_YEAR, 1, 1).toEpochDays() * DAY_MILLIS }
|
||||
val last = remember(today) { LocalDate.parse(today).plus(MAX_DAYS_AHEAD, DateTimeUnit.DAY).toEpochDays() * DAY_MILLIS }
|
||||
val first = remember { FIRST_DATE.toEpochDays() * DAY_MILLIS }
|
||||
val last = remember(today) { LocalDate.parse(today).plus(DAYS_AHEAD_MAX, DateTimeUnit.DAY).toEpochDays() * DAY_MILLIS }
|
||||
val picker = rememberDatePickerState(
|
||||
initialSelectedDateMillis = LocalDate.parse(date).toEpochDays() * DAY_MILLIS,
|
||||
selectableDates = object : SelectableDates {
|
||||
@@ -479,9 +483,9 @@ internal fun errorText(errors: List<FieldError>, field: String): String? {
|
||||
}
|
||||
ExpenseEditorViewModel.FIELD_DESCRIPTION -> when (error.error) {
|
||||
ExpenseEditorViewModel.REQUIRED -> stringResource(DesignR.string.error_description_required)
|
||||
else -> pluralStringResource(DesignR.plurals.error_too_long, MAX_DESCRIPTION, MAX_DESCRIPTION)
|
||||
else -> pluralStringResource(DesignR.plurals.error_too_long, DESCRIPTION_MAX, DESCRIPTION_MAX)
|
||||
}
|
||||
ExpenseEditorViewModel.FIELD_NOTES -> pluralStringResource(DesignR.plurals.error_too_long, MAX_NOTES, MAX_NOTES)
|
||||
ExpenseEditorViewModel.FIELD_NOTES -> pluralStringResource(DesignR.plurals.error_too_long, NOTES_MAX, NOTES_MAX)
|
||||
ExpenseEditorViewModel.FIELD_DATE -> stringResource(DesignR.string.error_date_out_of_range)
|
||||
ExpenseEditorViewModel.FIELD_SPLIT -> when (error.error) {
|
||||
"exact_sum_mismatch", "exact_negative" -> stringResource(DesignR.string.error_exact_share_too_large)
|
||||
@@ -495,8 +499,4 @@ internal fun errorText(errors: List<FieldError>, field: String): String? {
|
||||
private const val PERCENT_HUNDREDTHS = 100L
|
||||
private const val HALF = 50
|
||||
private const val MIN_AMOUNT_ORE = 1L
|
||||
private const val MAX_DESCRIPTION = 100
|
||||
private const val MAX_NOTES = 1000
|
||||
private const val FIRST_YEAR = 2000
|
||||
private const val MAX_DAYS_AHEAD = 365
|
||||
private const val DAY_MILLIS = 86_400_000L
|
||||
|
||||
+1
@@ -77,6 +77,7 @@ import se.hagstedt.kvitt.design.component.splitOption
|
||||
import se.hagstedt.kvitt.design.format.AmountInput
|
||||
import se.hagstedt.kvitt.design.format.AmountKey
|
||||
import se.hagstedt.kvitt.design.format.BP_TOTAL
|
||||
import se.hagstedt.kvitt.design.format.DESCRIPTION_MAX
|
||||
import se.hagstedt.kvitt.design.format.DateFormatter
|
||||
import se.hagstedt.kvitt.design.format.KvittLanguage
|
||||
import se.hagstedt.kvitt.design.icon.KvittIcon
|
||||
|
||||
+1
-3
@@ -4,6 +4,7 @@ import se.hagstedt.kvitt.design.component.RatioField
|
||||
import se.hagstedt.kvitt.design.format.AmountInput
|
||||
import se.hagstedt.kvitt.design.format.AmountKey
|
||||
import se.hagstedt.kvitt.design.format.BP_TOTAL
|
||||
import se.hagstedt.kvitt.design.format.DESCRIPTION_MAX
|
||||
import se.hagstedt.kvitt.model.AccountId
|
||||
import se.hagstedt.kvitt.model.CategoryId
|
||||
import se.hagstedt.kvitt.model.ExpenseDraft
|
||||
@@ -16,9 +17,6 @@ import se.hagstedt.kvitt.model.UserId
|
||||
/** Which keypad field has focus (design-spec §3.7: the keypad drives the amount, the exact share and the percents). */
|
||||
enum class EditorFocus { None, Amount, RatioViewer, RatioPartner, Exact }
|
||||
|
||||
/** §9.6: description 1 to 100, notes 0 to 1000 code points. */
|
||||
const val DESCRIPTION_MAX = 100
|
||||
|
||||
/**
|
||||
* The S-19 editor's input, independent of the repositories: the expense fields of S-7 without the date (D-019) plus
|
||||
* the schedule of FR-9. Pure, so the editor's rules are unit-tested without Android.
|
||||
|
||||
+5
-5
@@ -40,7 +40,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import kotlinx.datetime.DateTimeUnit
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.plus
|
||||
import se.hagstedt.kvitt.core.design.R as DesignR
|
||||
import se.hagstedt.kvitt.design.KvittTheme
|
||||
import se.hagstedt.kvitt.design.component.AccountTile
|
||||
import se.hagstedt.kvitt.design.component.AllocationRow
|
||||
@@ -69,20 +68,21 @@ import se.hagstedt.kvitt.design.component.TopBarNavigation
|
||||
import se.hagstedt.kvitt.design.component.detailDate
|
||||
import se.hagstedt.kvitt.design.component.kvittLanguage
|
||||
import se.hagstedt.kvitt.design.component.money
|
||||
import se.hagstedt.kvitt.design.format.DAYS_AHEAD_MAX
|
||||
import se.hagstedt.kvitt.design.format.DateFormatter
|
||||
import se.hagstedt.kvitt.design.format.FIRST_DATE
|
||||
import se.hagstedt.kvitt.design.format.MoneyFormatter
|
||||
import se.hagstedt.kvitt.design.icon.KvittIcon
|
||||
import se.hagstedt.kvitt.design.theme.KvittDimens
|
||||
import se.hagstedt.kvitt.model.Account
|
||||
import se.hagstedt.kvitt.model.FieldError
|
||||
import se.hagstedt.kvitt.model.User
|
||||
import se.hagstedt.kvitt.core.design.R as DesignR
|
||||
|
||||
/** §9.6: the longest settlement note, in code points. */
|
||||
private const val NOTE_MAX = 200
|
||||
|
||||
/** §9.6: dates from 2000-01-01 to today + 365 days. */
|
||||
private val FIRST_DATE = LocalDate(2000, 1, 1)
|
||||
private const val DAYS_AHEAD = 365
|
||||
/** The date picker counts in UTC milliseconds; dates run from [FIRST_DATE] to today + [DAYS_AHEAD_MAX] days (§9.6). */
|
||||
private const val MILLIS_PER_DAY = 86_400_000L
|
||||
|
||||
/**
|
||||
@@ -440,7 +440,7 @@ private fun AddAccountRow(account: Account, colorIndex: Int, onClick: () -> Unit
|
||||
@Composable
|
||||
private fun SettleDatePicker(isoDate: String, actions: SettleActions) {
|
||||
val today = LocalKvittClock.current.today
|
||||
val last = today.plus(DAYS_AHEAD, DateTimeUnit.DAY)
|
||||
val last = today.plus(DAYS_AHEAD_MAX, DateTimeUnit.DAY)
|
||||
val selectable = remember(today) {
|
||||
object : SelectableDates {
|
||||
override fun isSelectableDate(utcTimeMillis: Long): Boolean {
|
||||
|
||||
Reference in New Issue
Block a user