The app gets its look and navigation before more features land on them. - Theme (core/theme/): hand-written light and dark Material 3 colour schemes around cobalt and copper; ThemeExtras for the colours Material has no role for (states, the four FSRS ratings); the 20 colours a person can give content. Tests hold text to WCAG AA contrast on each palette colour and on the dark theme's primary colour and surface. - Layout and shared widgets: Material 3's window-size classes (core/layout/); SectionCard, AppLoadingIndicator, confirmDestructiveAction and FoundById (core/widget/). Lucide icons. - Navigation with go_router: four tabs (Home, Library, Explore, Profile), each keeping its own stack of screens, as a navigation rail with the app's icon from 600 dp and as a navigation bar below; choosing the open tab again returns to its first screen. A studyset's screen sits in the library; studying, cards and settings cover the tabs. - Routes carry ids: FoundById finds the item in the repository's live list, so a studyset deleted on another device, or an old link, says it no longer exists. A screen that deletes its own item keeps showing it while it closes, rather than flashing that message. - Every person gets an app of their own (PersonApp, lib/src/app/): a MaterialApp.router with its own GoRouter and PersonScope around the navigator, built by SessionGate inside the person's keyed subtree. Screens read the person's repositories wherever they are built, so the static route() helpers and the listener that closed every pushed screen on a change of person are gone: a change of person now disposes the whole router and starts again at home. - The account dialog becomes the Profile tab, with Settings (sign out, delete the account, About) behind a gear. A waiting GitHub sign-in now keeps waiting while another tab shows; a change of person still cancels it. - The new screens' strings come from Flutter's gen-l10n (English for now). The generated files are committed, left out of analysis and formatting, and written anew by `flutter pub get`, so CI's check of generated code also catches stale ones. Widget tests now start the whole app (test/support/pump_app.dart) at the screen's path and move through it as a person would, by tabs and taps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
350 lines
11 KiB
Dart
350 lines
11 KiB
Dart
import 'dart:async';
|
||
|
||
import 'package:flutter/foundation.dart';
|
||
import 'package:flutter/widgets.dart';
|
||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||
import 'package:intl/intl.dart' as intl;
|
||
|
||
import 'app_localizations_en.dart';
|
||
|
||
// ignore_for_file: type=lint
|
||
|
||
/// Callers can lookup localized strings with an instance of AppLocalizations
|
||
/// returned by `AppLocalizations.of(context)`.
|
||
///
|
||
/// Applications need to include `AppLocalizations.delegate()` in their app's
|
||
/// `localizationDelegates` list, and the locales they support in the app's
|
||
/// `supportedLocales` list. For example:
|
||
///
|
||
/// ```dart
|
||
/// import 'l10n/app_localizations.dart';
|
||
///
|
||
/// return MaterialApp(
|
||
/// localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||
/// supportedLocales: AppLocalizations.supportedLocales,
|
||
/// home: MyApplicationHome(),
|
||
/// );
|
||
/// ```
|
||
///
|
||
/// ## Update pubspec.yaml
|
||
///
|
||
/// Please make sure to update your pubspec.yaml to include the following
|
||
/// packages:
|
||
///
|
||
/// ```yaml
|
||
/// dependencies:
|
||
/// # Internationalization support.
|
||
/// flutter_localizations:
|
||
/// sdk: flutter
|
||
/// intl: any # Use the pinned version from flutter_localizations
|
||
///
|
||
/// # Rest of dependencies
|
||
/// ```
|
||
///
|
||
/// ## iOS Applications
|
||
///
|
||
/// iOS applications define key application metadata, including supported
|
||
/// locales, in an Info.plist file that is built into the application bundle.
|
||
/// To configure the locales supported by your app, you’ll need to edit this
|
||
/// file.
|
||
///
|
||
/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file.
|
||
/// Then, in the Project Navigator, open the Info.plist file under the Runner
|
||
/// project’s Runner folder.
|
||
///
|
||
/// Next, select the Information Property List item, select Add Item from the
|
||
/// Editor menu, then select Localizations from the pop-up menu.
|
||
///
|
||
/// Select and expand the newly-created Localizations item then, for each
|
||
/// locale your application supports, add a new item and select the locale
|
||
/// you wish to add from the pop-up menu in the Value field. This list should
|
||
/// be consistent with the languages listed in the AppLocalizations.supportedLocales
|
||
/// property.
|
||
abstract class AppLocalizations {
|
||
AppLocalizations(String locale)
|
||
: localeName = intl.Intl.canonicalizedLocale(locale.toString());
|
||
|
||
final String localeName;
|
||
|
||
static AppLocalizations of(BuildContext context) {
|
||
return Localizations.of<AppLocalizations>(context, AppLocalizations)!;
|
||
}
|
||
|
||
static const LocalizationsDelegate<AppLocalizations> delegate =
|
||
_AppLocalizationsDelegate();
|
||
|
||
/// A list of this localizations delegate along with the default localizations
|
||
/// delegates.
|
||
///
|
||
/// Returns a list of localizations delegates containing this delegate along with
|
||
/// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
|
||
/// and GlobalWidgetsLocalizations.delegate.
|
||
///
|
||
/// Additional delegates can be added by appending to this list in
|
||
/// MaterialApp. This list does not have to be used at all if a custom list
|
||
/// of delegates is preferred or required.
|
||
static const List<LocalizationsDelegate<dynamic>> localizationsDelegates =
|
||
<LocalizationsDelegate<dynamic>>[
|
||
delegate,
|
||
GlobalMaterialLocalizations.delegate,
|
||
GlobalCupertinoLocalizations.delegate,
|
||
GlobalWidgetsLocalizations.delegate,
|
||
];
|
||
|
||
/// A list of this localizations delegate's supported locales.
|
||
static const List<Locale> supportedLocales = <Locale>[Locale('en')];
|
||
|
||
/// The app's name, e.g. in the window title and the rail's logo.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Halkyon Learn'**
|
||
String get appTitle;
|
||
|
||
/// No description provided for @shellTabHome.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Home'**
|
||
String get shellTabHome;
|
||
|
||
/// No description provided for @shellTabLibrary.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Library'**
|
||
String get shellTabLibrary;
|
||
|
||
/// No description provided for @shellTabExplore.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Explore'**
|
||
String get shellTabExplore;
|
||
|
||
/// No description provided for @shellTabProfile.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Profile'**
|
||
String get shellTabProfile;
|
||
|
||
/// No description provided for @homeTitle.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Home'**
|
||
String get homeTitle;
|
||
|
||
/// No description provided for @homeLibraryIntro.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Your studysets and cards are in your library.'**
|
||
String get homeLibraryIntro;
|
||
|
||
/// No description provided for @homeOpenLibrary.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Open library'**
|
||
String get homeOpenLibrary;
|
||
|
||
/// No description provided for @libraryTitle.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Library'**
|
||
String get libraryTitle;
|
||
|
||
/// No description provided for @exploreTitle.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Explore'**
|
||
String get exploreTitle;
|
||
|
||
/// No description provided for @exploreEmpty.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Studysets that others share will appear here.'**
|
||
String get exploreEmpty;
|
||
|
||
/// No description provided for @profileTitle.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Profile'**
|
||
String get profileTitle;
|
||
|
||
/// No description provided for @profileSettingsTooltip.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Settings'**
|
||
String get profileSettingsTooltip;
|
||
|
||
/// No description provided for @profileIdentified.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Your progress is kept with your account.'**
|
||
String get profileIdentified;
|
||
|
||
/// No description provided for @profileAnonymous.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Progress is saved on this device. Sign in to keep it.'**
|
||
String get profileAnonymous;
|
||
|
||
/// No description provided for @profileSignInWithGitHub.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Sign in with GitHub'**
|
||
String get profileSignInWithGitHub;
|
||
|
||
/// No description provided for @profileSignInFailed.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Couldn\'t sign in with GitHub.'**
|
||
String get profileSignInFailed;
|
||
|
||
/// No description provided for @profileContinueInBrowser.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Continue in your browser.'**
|
||
String get profileContinueInBrowser;
|
||
|
||
/// No description provided for @profileSwitchTitle.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Switch to your GitHub account?'**
|
||
String get profileSwitchTitle;
|
||
|
||
/// No description provided for @profileSwitchMessage.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'This GitHub account already has progress from another device. The studysets on this device can\'t be combined with it yet: if you switch, they are deleted.'**
|
||
String get profileSwitchMessage;
|
||
|
||
/// No description provided for @profileSwitchStay.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Stay anonymous'**
|
||
String get profileSwitchStay;
|
||
|
||
/// No description provided for @profileSwitchConfirm.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Switch'**
|
||
String get profileSwitchConfirm;
|
||
|
||
/// No description provided for @settingsTitle.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Settings'**
|
||
String get settingsTitle;
|
||
|
||
/// No description provided for @settingsAccount.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Account'**
|
||
String get settingsAccount;
|
||
|
||
/// No description provided for @settingsSignOut.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Sign out'**
|
||
String get settingsSignOut;
|
||
|
||
/// No description provided for @settingsSignOutTitle.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Sign out?'**
|
||
String get settingsSignOutTitle;
|
||
|
||
/// No description provided for @settingsSignOutAnonymousMessage.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'This account can\'t be signed into again, so signing out deletes it with its progress.'**
|
||
String get settingsSignOutAnonymousMessage;
|
||
|
||
/// No description provided for @settingsDeleteAccount.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Delete account'**
|
||
String get settingsDeleteAccount;
|
||
|
||
/// No description provided for @settingsDeleteTitle.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Delete your account?'**
|
||
String get settingsDeleteTitle;
|
||
|
||
/// No description provided for @settingsDeleteMessage.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Everything you have, your studysets, cards and reviews, is deleted on the server and on all your devices. This can\'t be undone.'**
|
||
String get settingsDeleteMessage;
|
||
|
||
/// No description provided for @settingsAbout.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'About Halkyon Learn'**
|
||
String get settingsAbout;
|
||
|
||
/// No description provided for @commonCancel.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Cancel'**
|
||
String get commonCancel;
|
||
|
||
/// No description provided for @commonClose.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Close'**
|
||
String get commonClose;
|
||
|
||
/// No description provided for @commonUndo.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Undo'**
|
||
String get commonUndo;
|
||
|
||
/// No description provided for @commonTryAgain.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Try again'**
|
||
String get commonTryAgain;
|
||
|
||
/// No description provided for @sessionUnreachable.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'Couldn\'t reach the server.'**
|
||
String get sessionUnreachable;
|
||
|
||
/// No description provided for @notFoundGone.
|
||
///
|
||
/// In en, this message translates to:
|
||
/// **'This no longer exists. It may have been deleted on another device.'**
|
||
String get notFoundGone;
|
||
}
|
||
|
||
class _AppLocalizationsDelegate
|
||
extends LocalizationsDelegate<AppLocalizations> {
|
||
const _AppLocalizationsDelegate();
|
||
|
||
@override
|
||
Future<AppLocalizations> load(Locale locale) {
|
||
return SynchronousFuture<AppLocalizations>(lookupAppLocalizations(locale));
|
||
}
|
||
|
||
@override
|
||
bool isSupported(Locale locale) =>
|
||
<String>['en'].contains(locale.languageCode);
|
||
|
||
@override
|
||
bool shouldReload(_AppLocalizationsDelegate old) => false;
|
||
}
|
||
|
||
AppLocalizations lookupAppLocalizations(Locale locale) {
|
||
// Lookup logic when only language code is specified.
|
||
switch (locale.languageCode) {
|
||
case 'en':
|
||
return AppLocalizationsEn();
|
||
}
|
||
|
||
throw FlutterError(
|
||
'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely '
|
||
'an issue with the localizations generation tool. Please file an issue '
|
||
'on GitHub with a reproducible sample app and the gen-l10n configuration '
|
||
'that was used.',
|
||
);
|
||
}
|