M12e: contract pass (pair invite kind, admin name in invite links, sync failed flag, rejection actor, device version name, template id, update check result, untouched accounts, D-027 re-stamp, PushEvent in :core:model, snackbar hosts)

Server: E-3 invite_kind; E-12 link ?from=<admin name>, E-4 passes it into kvitt://invite;
X-Kvitt-App-Version-Name stored in devices.app_version_name (migration 0003, schema 3);
OpenAPI and examples follow. Phone: :core:model, :core:fakes, :data, features and :app
updated together; goldens re-recorded for S-20 (version name) and new S-1/S-22 states.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
This commit is contained in:
2026-10-18 15:00:50 +02:00
parent 6159e18198
commit 8c607153c0
96 changed files with 1090 additions and 298 deletions
@@ -74,6 +74,7 @@ data class AccountDetailActions(
* Account detail (S-5). The account id is the navigation argument [AccountDetailViewModel.ARG_ACCOUNT_ID].
* [onArchived] is called after archiving with the account's name: `:app` returns to Home and shows
* "Vardag arkiverades" with "Ångra" there (design-spec S-5). [onDeleted] after deleting, or when the account is gone.
* [snackbarHostState] is hosted in this screen's scaffold, so `:app` can show a result snackbar here (D-071).
*/
@Composable
fun AccountDetailScreen(
@@ -86,6 +87,7 @@ fun AccountDetailScreen(
onArchived: (AccountId, String) -> Unit,
onDeleted: () -> Unit,
modifier: Modifier = Modifier,
snackbarHostState: SnackbarHostState = remember { SnackbarHostState() },
viewModel: AccountDetailViewModel = hiltViewModel(),
) {
val ui by viewModel.ui.collectAsStateWithLifecycle()
@@ -118,7 +120,7 @@ fun AccountDetailScreen(
onRetry = viewModel::retry,
onMessageShown = viewModel::messageShown,
)
AccountDetailContent(ui, actions, modifier)
AccountDetailContent(ui, actions, modifier, snackbarHostState = snackbarHostState)
}
/** The stateless account screen, rendered by [AccountDetailScreen], previews and screenshot tests. */
@@ -128,9 +130,10 @@ fun AccountDetailContent(
actions: AccountDetailActions,
modifier: Modifier = Modifier,
listState: LazyListState = rememberLazyListState(),
snackbarHostState: SnackbarHostState = remember { SnackbarHostState() },
) {
val ready = ui as? AccountDetailUi.Ready
val snackbar = remember { SnackbarHostState() }
val snackbar = snackbarHostState
val scrolled by remember { derivedStateOf { listState.firstVisibleItemIndex > 0 || listState.firstVisibleItemScrollOffset > 0 } }
val fabExpanded by remember { derivedStateOf { !listState.lastScrolledForward } }
ready?.message?.let { message -> MessageEffect(message, snackbar, actions) }