feat(publish): the Publish sheet lists the tag names a release sends (ADR 0019)
Before Publish, the sheet shows the names of the tags that go with the release, which the instance learns, sorted, or that none go; none where they can't be had, as something in the tree is private (the short reading of 22 Oct, finding 5). In English and German. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
5a61f2ea07
commit
9841d2d864
8 changed files with 137 additions and 8 deletions
|
|
@ -2073,6 +2073,9 @@
|
|||
"studysetReadableBody": "Alles darin wird entschlüsselt und neu synchronisiert, sodass die Instanz es lesen kann: Du kannst es veröffentlichen, und Claude kann es über den Connector lesen.",
|
||||
"studysetPrivateNoKeys": "Melde dich auf diesem Gerät mit deinem Passwort an, um Lernsets privat zu machen.",
|
||||
"studysetPrivateSomeLocked": "Manches darin ließ sich auf diesem Gerät nicht öffnen und blieb, wie es war.",
|
||||
"publishTagsSent": "Die Namen der Tags, die mitgehen und die die Instanz erfährt: {names}",
|
||||
"@publishTagsSent": {"placeholders": {"names": {"type": "String"}}},
|
||||
"publishTagsSentNone": "Es gehen keine Tags mit.",
|
||||
"studysetLockedTitle": "Gesperrt",
|
||||
"studysetLockedBody": "Dieses Lernset ist privat, und sein Schlüssel öffnet sich auf diesem Gerät nicht: Es wurde mit Schlüsseln verschlüsselt, die dein Konto nicht mehr hat. Es lässt sich nur noch löschen.",
|
||||
"studysetVisibilityPartly": "Privat, teils lesbar",
|
||||
|
|
|
|||
|
|
@ -2439,6 +2439,9 @@
|
|||
"studysetReadableBody": "Everything in it is decrypted and synced again, so the instance can read it: you can publish it, and Claude can read it through the connector.",
|
||||
"studysetPrivateNoKeys": "Sign in with your password on this device to make studysets private.",
|
||||
"studysetPrivateSomeLocked": "Some of it couldn't be opened on this device and stayed as it was.",
|
||||
"publishTagsSent": "The names of the tags that go with it, which the instance learns: {names}",
|
||||
"@publishTagsSent": {"placeholders": {"names": {"type": "String"}}},
|
||||
"publishTagsSentNone": "No tags go with it.",
|
||||
"studysetLockedTitle": "Locked",
|
||||
"studysetLockedBody": "This studyset is private, and its key doesn't open on this device: it was encrypted with keys your account no longer has. It can only be deleted.",
|
||||
"studysetVisibilityPartly": "Private, partly readable",
|
||||
|
|
|
|||
|
|
@ -12225,6 +12225,18 @@ abstract class AppLocalizations {
|
|||
/// **'Some of it couldn\'t be opened on this device and stayed as it was.'**
|
||||
String get studysetPrivateSomeLocked;
|
||||
|
||||
/// No description provided for @publishTagsSent.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'The names of the tags that go with it, which the instance learns: {names}'**
|
||||
String publishTagsSent(String names);
|
||||
|
||||
/// No description provided for @publishTagsSentNone.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No tags go with it.'**
|
||||
String get publishTagsSentNone;
|
||||
|
||||
/// No description provided for @studysetLockedTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
|
|||
|
|
@ -7751,6 +7751,14 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
String get studysetPrivateSomeLocked =>
|
||||
'Manches darin ließ sich auf diesem Gerät nicht öffnen und blieb, wie es war.';
|
||||
|
||||
@override
|
||||
String publishTagsSent(String names) {
|
||||
return 'Die Namen der Tags, die mitgehen und die die Instanz erfährt: $names';
|
||||
}
|
||||
|
||||
@override
|
||||
String get publishTagsSentNone => 'Es gehen keine Tags mit.';
|
||||
|
||||
@override
|
||||
String get studysetLockedTitle => 'Gesperrt';
|
||||
|
||||
|
|
|
|||
|
|
@ -7678,6 +7678,14 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
String get studysetPrivateSomeLocked =>
|
||||
'Some of it couldn\'t be opened on this device and stayed as it was.';
|
||||
|
||||
@override
|
||||
String publishTagsSent(String names) {
|
||||
return 'The names of the tags that go with it, which the instance learns: $names';
|
||||
}
|
||||
|
||||
@override
|
||||
String get publishTagsSentNone => 'No tags go with it.';
|
||||
|
||||
@override
|
||||
String get studysetLockedTitle => 'Locked';
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,13 @@ class PublishRepository(
|
|||
return publication;
|
||||
}
|
||||
|
||||
/// The names of the tags a release of [studysetId] would send, by their
|
||||
/// ids, for the person to see before it goes (the short reading of 22
|
||||
/// Oct); none without names to give. Throws as publishing would where
|
||||
/// something in it is private.
|
||||
Future<Map<String, String>> tagNamesOf(String studysetId) async =>
|
||||
await _tagNames?.call(studysetId) ?? const {};
|
||||
|
||||
/// Publishes the studyset [studysetId] as its next release, [notes]
|
||||
/// saying what changed; at the first, under the name [slug]. Explore
|
||||
/// shows [description] as its description, when given. Throws
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||
import 'package:go_router/go_router.dart';
|
||||
import 'package:halkyon_learn_flutter/l10n/app_localizations.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/dependencies/person_dependencies.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/editor/rich_text_screen.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/publish/bloc/publish_bloc.dart';
|
||||
|
|
@ -169,14 +170,25 @@ class _PublishSheetState() extends State<_PublishSheet> {
|
|||
),
|
||||
],
|
||||
),
|
||||
final PublishStateReady ready => _Form(
|
||||
studyset: widget.studyset,
|
||||
all: widget.all,
|
||||
state: ready,
|
||||
slug: _slug,
|
||||
notes: _notes,
|
||||
about: _aboutIn(ready),
|
||||
onEditAbout: _editAbout,
|
||||
final PublishStateReady ready => Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_Form(
|
||||
studyset: widget.studyset,
|
||||
all: widget.all,
|
||||
state: ready,
|
||||
slug: _slug,
|
||||
notes: _notes,
|
||||
about: _aboutIn(ready),
|
||||
onEditAbout: _editAbout,
|
||||
),
|
||||
if (ready.profile != null)
|
||||
PublishTagNames(
|
||||
names: PersonDependencies.of(
|
||||
context,
|
||||
).publishing.tagNamesOf(widget.studyset.id),
|
||||
),
|
||||
],
|
||||
),
|
||||
},
|
||||
footer: switch (state) {
|
||||
|
|
@ -611,3 +623,37 @@ String _problemText(
|
|||
PublishProblem.private => l10n.publishPrivateRefused,
|
||||
PublishProblem.tagNamesMismatch => l10n.publishTagNames,
|
||||
};
|
||||
|
||||
/// The names of the tags a release sends, which the instance learns, shown
|
||||
/// before Publish (the short reading of 22 Oct): the person sees each name
|
||||
/// that goes out; none while they can't be had.
|
||||
class const PublishTagNames({
|
||||
required final Future<Map<String, String>> names,
|
||||
super.key,
|
||||
}) extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
final theme = Theme.of(context);
|
||||
return FutureBuilder<Map<String, String>>(
|
||||
future: names.catchError((Object _) => const <String, String>{}),
|
||||
builder: (context, snapshot) {
|
||||
final sent = [...?snapshot.data?.values]..sort();
|
||||
if (!snapshot.hasData) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 12),
|
||||
child: Text(
|
||||
sent.isEmpty
|
||||
? l10n.publishTagsSentNone
|
||||
: l10n.publishTagsSent(sent.join(', ')),
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/l10n/app_languages.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/publish/widget/publish_sheet.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
|
||||
/// The Publish sheet shows the names of the tags a release sends before
|
||||
/// Publish, as the instance learns each (ADR 0019, the short reading of 22
|
||||
/// Oct).
|
||||
void main() {
|
||||
Future<void> pump(
|
||||
WidgetTester tester,
|
||||
Future<Map<String, String>> names,
|
||||
) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
localizationsDelegates: appLocalizationsDelegates,
|
||||
home: Scaffold(body: PublishTagNames(names: names)),
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
}
|
||||
|
||||
testWidgets('lists every name sent, sorted', (tester) async {
|
||||
await pump(tester, Future.value({'t2': 'Verbs', 't1': 'JLPT-N3'}));
|
||||
|
||||
expect(find.textContaining('JLPT-N3, Verbs'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('says when none go with it', (tester) async {
|
||||
await pump(tester, Future.value(const {}));
|
||||
|
||||
expect(find.text('No tags go with it.'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('shows no name where they can not be had (something private)', (
|
||||
tester,
|
||||
) async {
|
||||
await pump(tester, Future(() => throw StateError('private')));
|
||||
|
||||
expect(find.textContaining('JLPT'), findsNothing);
|
||||
});
|
||||
}
|
||||
Loading…
Reference in a new issue