import 'package:intl/locale.dart'; import 'package:pkpass/pkpass.dart'; import 'package:pkpass/src/utils/mabe_decode.dart'; /// Keys that define the structure of the pass. /// /// These keys are used for all pass styles and partition the fields into the various parts of the pass. class PassStructureDictionary { /// Fields to be displayed in the header on the front of the pass. /// /// Use header fields sparingly; unlike all other fields, they remain visible when a stack of passes are displayed. final List headerFields; /// Fields to be displayed prominently on the front of the pass. final List primaryFields; /// Fields to be displayed on the front of the pass. final List secondaryFields; /// Fields to be on the back of the pass. final List backFields; /// Additional fields to be displayed on the front of the pass. final List auxiliaryFields; /// Required for boarding passes; otherwise not allowed. Type of transit. final TransitType? transitType; const PassStructureDictionary({ this.headerFields = const [], this.primaryFields = const [], this.secondaryFields = const [], this.backFields = const [], this.auxiliaryFields = const [], this.transitType, }); factory PassStructureDictionary.fromJson(Map json) => PassStructureDictionary( headerFields: (json['headerFields'] as List?) ?.map((i) => DictionaryField.fromJson(i)) .toList() ?? [], primaryFields: (json['primaryFields'] as List?) ?.map((i) => DictionaryField.fromJson(i)) .toList() ?? [], secondaryFields: (json['secondaryFields'] as List?) ?.map((i) => DictionaryField.fromJson(i)) .toList() ?? [], backFields: (json['backFields'] as List?) ?.map((i) => DictionaryField.fromJson(i)) .toList() ?? [], auxiliaryFields: (json['auxiliaryFields'] as List?) ?.map((i) => DictionaryField.fromJson(i)) .toList() ?? [], transitType: _TarnsitType.parse(json['transitType'] as String?), ); } /// Information about a field. class DictionaryField { /// The key must be unique within the scope of the entire pass. For example, “departure-gate.” final String key; /// Value of the field, for example, 42. final DictionaryValue value; /// Label text for the field. final String? label; /// Format string for the alert text that is displayed when the pass is updated. The format string must contain the escape %@, which is replaced with the field’s new value. For example, “Gate changed to %@.” /// /// If you don’t specify a change message, the user isn’t notified when the field changes. final String? changeMessage; /// Alignment for the field’s contents. final PassTextAlign? textAlignment; /// Attributed value of the field. /// /// The value may contain HTML markup for links. Only the tag and its href attribute are supported. For example, the following is key-value pair specifies a link with the text “Edit my profile”: /// /// "attributedValue": "Edit my profile" /// /// This key’s value overrides the text specified by the value key. final DictionaryValue? attributedValue; const DictionaryField({ required this.key, required this.value, this.label, this.changeMessage, this.textAlignment, this.attributedValue, }); factory DictionaryField.fromJson(Map json) => DictionaryField( key: json['key'] as String, value: DictionaryValue.parse(json['value'] as String), label: json['label'] as String?, changeMessage: json['changeMessage'] as String?, textAlignment: MaybeDecode.maybeTextAlign(json['textAlignment'] as String?), attributedValue: json['attributedValue'] == null ? null : DictionaryValue.parse(json['attributedValue'] as String), ); /// Localized version of [label] based on given [locale] and [pass]. String? getLocalizedLabel(PassFile pass, Locale? locale) { final localizations = pass.getLocalizations(locale); print(localizations); return localizations?[label] ?? label; } /// Localized version of [changeMessage] based on given [locale] and [pass]. String? getLocalizedChangeMessage(PassFile pass, Locale? locale) { final localizations = pass.getLocalizations(locale); return localizations?[changeMessage] ?? changeMessage; } } /// represents the possible values of a [DictionaryField]. abstract class DictionaryValue { const DictionaryValue(); /// parses the correct [DictionaryValue] implementor based on a given [value]. factory DictionaryValue.parse(String value) { final number = int.tryParse(value); if (number != null) return NumberDictionaryValue(number); final dateTime = DateTime.tryParse(value); if (dateTime != null) return DateTimeDictionaryValue(dateTime); return StringDictionaryValue(value); } /// Localized value based on given [locale] and [pass]. DictionaryValue getLocalizedValue(PassFile pass, Locale? locale); } /// [String] content of a [DictionaryField]. class StringDictionaryValue extends DictionaryValue { final String string; const StringDictionaryValue(this.string); @override /// Localized value based on given [locale] and [pass]. DictionaryValue getLocalizedValue(PassFile pass, Locale? locale) { final localizations = pass.getLocalizations(locale); return StringDictionaryValue(localizations?[string] ?? string); } } /// [DateTime] content of a [DictionaryField]. class DateTimeDictionaryValue extends DictionaryValue { final DateTime dateTime; const DateTimeDictionaryValue(this.dateTime); @override /// Localized value based on given [locale] and [pass]. Same as [dateTime]. DictionaryValue getLocalizedValue(PassFile pass, Locale? locale) => this; } /// [int] content of a [DictionaryField]. class NumberDictionaryValue extends DictionaryValue { final int number; const NumberDictionaryValue(this.number); @override /// Localized value based on given [locale] and [pass]. Same as [number]. DictionaryValue getLocalizedValue(PassFile pass, Locale? locale) => this; } /// Possible types of [PassStructureDictionary.transitType]. enum TransitType { /// PKTransitTypeAir air, /// PKTransitTypeBoat boat, /// PKTransitTypeBus bus, /// PKTransitTypeGeneric generic, /// PKTransitTypeTrain train, } /// Possible types of [DictionaryField.textAlignment]. enum PassTextAlign { /// PKTextAlignmentLeft, corresponds `dart:ui` [TextAlign.left] left, /// PKTextAlignmentCenter, corresponds `dart:ui` [TextAlign.center] center, /// PKTextAlignmentRight, corresponds `dart:ui` [TextAlign.left] right, /// PKTextAlignmentNatural, corresponds `dart:ui` [TextAlign.start] natural, } extension _TarnsitType on TransitType { static TransitType? parse(String? type) { if (type == null) return null; switch (type) { case 'PKTransitTypeAir': return TransitType.air; case 'PKTransitTypeBoat': return TransitType.boat; case 'PKTransitTypeBus': return TransitType.bus; case 'PKTransitTypeTrain': return TransitType.train; default: return TransitType.generic; } } }