|
1 | 1 | package com.flipcash.services.models |
2 | 2 |
|
| 3 | +import com.codeinc.flipcash.gen.common.v1.Common |
| 4 | +import com.codeinc.flipcash.gen.common.v1.validate |
| 5 | +import dev.bmcreations.protovalidate.ValidationResult |
| 6 | + |
3 | 7 | /** |
4 | 8 | * The `@` a handle is shown with. |
5 | 9 | * |
6 | | - * Usernames are stored, sent and matched bare — the server's `^[a-z0-9_]{2,15}$` charset has no room |
| 10 | + * Usernames are stored, sent and matched bare — the contract's `^[a-z0-9_]{2,30}$` charset has no room |
7 | 11 | * for an `@`, and [ProfileIdentifier.Username] / [ResolveIdentifier.Username] carry them that way. |
8 | 12 | * The prefix is presentation, so it is added here rather than at each surface that shows one. |
9 | 13 | */ |
10 | 14 | const val HandlePrefix = "@" |
11 | 15 |
|
12 | | -/** Mirrors the server's `^[a-z0-9_]{2,15}$` validation on `common.v1.Username`. */ |
| 16 | +/** |
| 17 | + * The bounds `common.v1.Username` allows (`^[a-z0-9_]{2,30}$`). What a handle may be on the wire, so |
| 18 | + * what the app must be able to read, deeplink and mention; [isUsernameShaped] asks the generated |
| 19 | + * validator rather than restating them. |
| 20 | + */ |
13 | 21 | const val MinUsernameLength = 2 |
14 | | -const val MaxUsernameLength = 15 |
| 22 | +const val MaxUsernameLength = 30 |
15 | 23 |
|
16 | 24 | /** |
17 | | - * The server's own charset and bounds. Kept here rather than at the one screen that types a |
18 | | - * username, because two other places have to recognise one without being able to ask the server: |
19 | | - * the `flipcash.com/<username>` deeplink, which must not mistake `/download` for a handle, and the |
20 | | - * tip card's link row, which decides whether a URL's last segment is readable or an opaque id. |
| 25 | + * The longest username the claim screen lets someone pick. A product limit tighter than the |
| 26 | + * contract's [MaxUsernameLength]: handles the server assigns can run longer than this. |
21 | 27 | */ |
22 | | -private val UsernamePattern = Regex("^[a-z0-9_]{$MinUsernameLength,$MaxUsernameLength}$") |
| 28 | +const val MaxClaimableUsernameLength = 15 |
23 | 29 |
|
24 | 30 | /** |
25 | | - * Whether this could be a username — the charset and length the server accepts, with or without a |
26 | | - * leading `@`. A yes means only "shaped like one"; whether it is actually claimed is the server's |
27 | | - * answer, not ours. |
| 31 | + * Whether this could be a username — what `common.v1.Username` accepts, with or without a leading |
| 32 | + * `@`. A yes means only "shaped like one"; whether it is actually claimed is the server's answer. |
| 33 | + * |
| 34 | + * Delegates to the contract's generated validator so the protos stay the single source of truth. |
| 35 | + * Two places rely on it without being able to ask the server: the `flipcash.com/<username>` |
| 36 | + * deeplink, which must not mistake `/download` for a handle, and the tip card's link row, which |
| 37 | + * decides whether a URL's last segment is readable or an opaque id. |
28 | 38 | */ |
29 | | -fun String.isUsernameShaped(): Boolean = UsernamePattern.matches(removePrefix(HandlePrefix)) |
| 39 | +fun String.isUsernameShaped(): Boolean = |
| 40 | + Common.Username.newBuilder().setValue(removePrefix(HandlePrefix)).build().validate() is ValidationResult.Valid |
30 | 41 |
|
31 | 42 | /** |
32 | 43 | * This username as a handle — `sally_streamer` becomes `@sally_streamer`. |
|
0 commit comments