Merge branch 'learn-fb' into integration
This commit is contained in:
commit
918080ce46
9 changed files with 2471 additions and 368 deletions
|
|
@ -455,7 +455,11 @@ class _GrantSheetState() extends State<_GrantSheet> {
|
|||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
if (grantee == null) ...[
|
||||
Text(l10n.assistantPeopleTitle, style: heading),
|
||||
ExplainedTitle(
|
||||
l10n.assistantPeopleTitle,
|
||||
help: l10n.assistantGrantNote,
|
||||
style: heading,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (state.accounts.isNotEmpty) ...[
|
||||
Wrap(
|
||||
|
|
@ -505,7 +509,14 @@ class _GrantSheetState() extends State<_GrantSheet> {
|
|||
),
|
||||
],
|
||||
const SizedBox(height: 16),
|
||||
Text(l10n.assistantGrantBudget, style: heading),
|
||||
ExplainedTitle(
|
||||
l10n.assistantGrantBudget,
|
||||
help: [
|
||||
l10n.assistantGrantLimitHelper,
|
||||
if (priced) l10n.assistantGrantMoneyNote,
|
||||
].join(' '),
|
||||
style: heading,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (priced)
|
||||
_LimitRow(
|
||||
|
|
@ -520,13 +531,6 @@ class _GrantSheetState() extends State<_GrantSheet> {
|
|||
suffix: null,
|
||||
enabled: !state.busy,
|
||||
),
|
||||
Text(
|
||||
[
|
||||
l10n.assistantGrantLimitHelper,
|
||||
if (priced) l10n.assistantGrantMoneyNote,
|
||||
].join(' '),
|
||||
style: muted,
|
||||
),
|
||||
if (grantee == null && state.replaced.isNotEmpty) ...[
|
||||
const SizedBox(height: 16),
|
||||
_Replaced(state.replaced, currency: setUp.currency),
|
||||
|
|
@ -538,10 +542,6 @@ class _GrantSheetState() extends State<_GrantSheet> {
|
|||
style: TextStyle(color: theme.colorScheme.error),
|
||||
),
|
||||
],
|
||||
if (grantee == null) ...[
|
||||
const SizedBox(height: 16),
|
||||
Text(l10n.assistantGrantNote, style: muted),
|
||||
],
|
||||
],
|
||||
),
|
||||
footer: FilledButton(
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ class const _AssistantsView({
|
|||
return const SizedBox.shrink();
|
||||
}
|
||||
final offer = state.offer;
|
||||
final muted = theme.textTheme.bodyMedium?.copyWith(
|
||||
final subtitle = theme.textTheme.titleSmall?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
);
|
||||
return Padding(
|
||||
|
|
@ -141,21 +141,16 @@ class const _AssistantsView({
|
|||
if (ownKept && (offer.ownKeys || state.own.isNotEmpty)) ...[
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: Text(
|
||||
l10n.assistantOwnTitle,
|
||||
style: theme.textTheme.titleSmall?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
// Why own keys go unasked, where they are not allowed,
|
||||
// behind its question mark (decided 3 Nov 2026).
|
||||
child: offer.ownKeys
|
||||
? Text(l10n.assistantOwnTitle, style: subtitle)
|
||||
: ExplainedTitle(
|
||||
l10n.assistantOwnTitle,
|
||||
help: l10n.assistantOwnNotAllowed(instance),
|
||||
style: subtitle,
|
||||
),
|
||||
),
|
||||
if (!offer.ownKeys)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Text(
|
||||
l10n.assistantOwnNotAllowed(instance),
|
||||
style: muted,
|
||||
),
|
||||
),
|
||||
for (final own in state.own)
|
||||
ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
|
|
|
|||
|
|
@ -79,8 +79,10 @@ class const _AccountSecurity() extends StatelessWidget {
|
|||
ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
leading: const Icon(LucideIcons.keyRound),
|
||||
title: Text(l10n.passwordSectionSetUp),
|
||||
subtitle: Text(l10n.passwordSectionSetUpHint),
|
||||
title: ExplainedTitle(
|
||||
l10n.passwordSectionSetUp,
|
||||
help: l10n.passwordSectionSetUpHint,
|
||||
),
|
||||
onTap: () => _setUp(context, security),
|
||||
),
|
||||
] else if (!security.finished)
|
||||
|
|
|
|||
|
|
@ -731,9 +731,6 @@ class const _ProfilePage({required final String url}) extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
final l10n = HalkyonAppLocalizations.of(context);
|
||||
final theme = Theme.of(context);
|
||||
final muted = theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: Column(
|
||||
|
|
@ -751,6 +748,9 @@ class const _ProfilePage({required final String url}) extends StatelessWidget {
|
|||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
// What linking back does, behind its question mark (decided
|
||||
// 3 Nov 2026).
|
||||
HelpButton(l10n.publicProfileVerifyHint),
|
||||
IconButton(
|
||||
tooltip: l10n.publicProfilePageCopy,
|
||||
icon: const Icon(LucideIcons.copy, size: 18),
|
||||
|
|
@ -766,7 +766,6 @@ class const _ProfilePage({required final String url}) extends StatelessWidget {
|
|||
),
|
||||
],
|
||||
),
|
||||
Text(l10n.publicProfileVerifyHint, style: muted),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import 'package:halkyon_app/src/simplelogin/model/simplelogin_standing.dart';
|
|||
import 'package:halkyon_app/src/simplelogin/widget/keys_reset.dart';
|
||||
import 'package:halkyon_app/src/simplelogin/widget/simplelogin_sheet.dart';
|
||||
import 'package:halkyon_app/src/simplelogin/widget/simplelogin_standing_scope.dart';
|
||||
import 'package:halkyon_ui/halkyon_ui.dart';
|
||||
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
|
||||
|
|
@ -64,8 +65,10 @@ class const _SimpleLoginSection({required final double gapAbove})
|
|||
ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
leading: const Icon(LucideIcons.link),
|
||||
title: Text(l10n.simpleLoginAddTitle),
|
||||
subtitle: Text(l10n.simpleLoginAddHint),
|
||||
title: ExplainedTitle(
|
||||
l10n.simpleLoginAddTitle,
|
||||
help: l10n.simpleLoginAddHint,
|
||||
),
|
||||
onTap: () => addSimpleLogin(context),
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -144,6 +144,37 @@ void main() {
|
|||
await show(tester);
|
||||
await tap(tester, find.text('Grant assistants'));
|
||||
expect(tester.widget<FilledButton>(saveButton()).onPressed, isNull);
|
||||
// What a grant does and how its budget counts, behind the question
|
||||
// marks beside People and Budget (decided 3 Nov 2026).
|
||||
final note = find.textContaining('They agree once');
|
||||
final counts = find.textContaining('Empty for none.');
|
||||
expect(note, findsNothing);
|
||||
expect(counts, findsNothing);
|
||||
await tap(
|
||||
tester,
|
||||
find.descendant(
|
||||
of: find.widgetWithText(ExplainedTitle, 'People'),
|
||||
matching: find.byType(HelpButton),
|
||||
),
|
||||
);
|
||||
expect(note, findsOneWidget);
|
||||
await tap(tester, find.text('OK'));
|
||||
await tester.ensureVisible(
|
||||
find.widgetWithText(ExplainedTitle, 'Budget, for all of them together'),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
await tap(
|
||||
tester,
|
||||
find.descendant(
|
||||
of: find.widgetWithText(
|
||||
ExplainedTitle,
|
||||
'Budget, for all of them together',
|
||||
),
|
||||
matching: find.byType(HelpButton),
|
||||
),
|
||||
);
|
||||
expect(counts, findsOneWidget);
|
||||
await tap(tester, find.text('OK'));
|
||||
|
||||
await addPerson(tester, 'anna');
|
||||
await addPerson(tester, 'ben');
|
||||
|
|
|
|||
|
|
@ -406,14 +406,16 @@ void main() {
|
|||
),
|
||||
);
|
||||
await show(tester);
|
||||
|
||||
expect(
|
||||
find.text(
|
||||
const why =
|
||||
"Whoever runs test.example.org doesn't allow own keys here, so the "
|
||||
"app doesn't ask these.",
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
"app doesn't ask these.";
|
||||
|
||||
expect(find.text(why), findsNothing, reason: 'behind its question mark');
|
||||
await tester.tap(find.byType(HelpButton));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text(why), findsOneWidget);
|
||||
await tester.tap(find.text('OK'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Add an assistant with your own key'), findsNothing);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'dart:convert';
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:halkyon_app/halkyon_app.dart';
|
||||
import 'package:halkyon_app/testing.dart';
|
||||
import 'package:halkyon_ui/halkyon_ui.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
|
||||
/// A signed-in person's app as a test makes one: whose it is.
|
||||
|
|
@ -592,6 +593,20 @@ void main() {
|
|||
|
||||
expect(find.text('Set up a password'), findsOneWidget);
|
||||
expect(find.text('Change password'), findsNothing);
|
||||
|
||||
// What it is, behind its question mark (decided 3 Nov 2026).
|
||||
const what =
|
||||
'Sign in with your email address, a password and an '
|
||||
'authenticator app.';
|
||||
expect(find.text(what), findsNothing);
|
||||
await tester.tap(
|
||||
find.descendant(
|
||||
of: find.widgetWithText(ExplainedTitle, 'Set up a password'),
|
||||
matching: find.byType(HelpButton),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text(what), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('offers none to one without an address: a code mailed to '
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:halkyon_app/halkyon_app.dart';
|
||||
import 'package:halkyon_app/testing.dart';
|
||||
import 'package:halkyon_ui/halkyon_ui.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
|
||||
const password = 'correct horse battery';
|
||||
|
|
@ -46,10 +47,11 @@ void main() {
|
|||
|
||||
expect(find.text('SimpleLogin'), findsOneWidget);
|
||||
expect(find.widgetWithText(ListTile, 'Add SimpleLogin'), findsOneWidget);
|
||||
expect(
|
||||
find.text('Sign in with SimpleLogin, or with your Proton account.'),
|
||||
findsOneWidget,
|
||||
);
|
||||
const hint = 'Sign in with SimpleLogin, or with your Proton account.';
|
||||
expect(find.text(hint), findsNothing, reason: 'behind its question mark');
|
||||
await tester.tap(find.byType(HelpButton));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text(hint), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('nothing where the instance offers no SimpleLogin', (
|
||||
|
|
|
|||
Loading…
Reference in a new issue