dart_pkpass/lib/pkpass/models/pass_structure_dictionary.dart
The one with the braid a8e912327b chore: apply code style
Signed-off-by: The one with the braid <info@braid.business>
2024-06-15 17:52:40 +02:00

341 lines
10 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:intl/intl.dart';
import 'package:intl/locale.dart';
import 'package:pkpass/pkpass.dart';
import 'package:pkpass/pkpass/utils/maybe_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<DictionaryField> headerFields;
/// Fields to be displayed prominently on the front of the pass.
final List<DictionaryField> primaryFields;
/// Fields to be displayed on the front of the pass.
final List<DictionaryField> secondaryFields;
/// Fields to be on the back of the pass.
final List<DictionaryField> backFields;
/// Additional fields to be displayed on the front of the pass.
final List<DictionaryField> 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<String, Object?> 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;
/// Attributed value of the field.
///
/// The value may contain HTML markup for links. Only the <a> 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": "<a href='http://example.com/customers/123'>Edit my profile</a>"
///
/// This keys value overrides the text specified by the value key.
final DictionaryValue? attributedValue;
/// 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 fields new value. For example, “Gate changed to %@.”
///
/// If you dont specify a change message, the user isnt notified when the field changes.
final String? changeMessage;
/// Alignment for the fields contents.
final PassTextAlign? textAlignment;
/// Number Style for a numeric value.
final PassTextNumberStyle? numberStyle;
/// ISO 4217 Currency Code for a numeric value.
final String? currencyCode;
/// Date and Time Style for a Date/Time value.
final PassTextDateStyle? dateStyle;
final PassTextDateStyle? timeStyle;
/// Wether to display the Date/Time value in the device timezone or in the timezone of the value.
final bool ignoresTimeZone;
const DictionaryField({
required this.key,
required this.value,
this.attributedValue,
this.label,
this.changeMessage,
this.textAlignment,
this.numberStyle,
this.currencyCode,
this.dateStyle,
this.timeStyle,
this.ignoresTimeZone = false,
});
factory DictionaryField.fromJson(Map<String, Object?> json) =>
DictionaryField(
key: json['key'] as String,
value: DictionaryValue.parse(json['value'] as String),
attributedValue: json['attributedValue'] == null
? null
: DictionaryValue.parse(json['attributedValue'] as String),
label: json['label'] as String?,
changeMessage: json['changeMessage'] as String?,
textAlignment:
MaybeDecode.maybeTextAlign(json['textAlignment'] as String?),
numberStyle:
MaybeDecode.maybeNumberStyle(json['numberStyle'] as String?),
currencyCode: json['currencyCode'] as String?,
dateStyle: MaybeDecode.maybeDateStyle(json['dateStyle'] as String?),
timeStyle: MaybeDecode.maybeDateStyle(json['timeStyle'] as String?),
ignoresTimeZone: json['ignoresTimeZone'] == true ? true : false,
);
/// Localized version of [label] based on given [locale] and [pass].
String? getLocalizedLabel(PassFile pass, Locale? locale) {
final localizations = pass.getLocalizations(locale);
return localizations?[label] ?? label;
}
/// Localized version of [value] based on given [locale] and [pass].
String? getLocalizedValue(PassFile pass, Locale? locale) {
final language = locale?.toLanguageTag();
if (value is StringDictionaryValue) {
String string = (value as StringDictionaryValue).string;
final localizations = pass.getLocalizations(locale);
return localizations?[string] ?? string;
} else if (value is DateTimeDictionaryValue) {
DateTime dateTime = (value as DateTimeDictionaryValue).dateTime;
DateFormat format = DateFormat(null, language);
switch (dateStyle) {
case PassTextDateStyle.none:
break;
case PassTextDateStyle.short:
format = DateFormat.yMd(language);
break;
case PassTextDateStyle.medium:
format = DateFormat.yMMMd(language);
break;
case PassTextDateStyle.long:
format = DateFormat.yMMMMd(language);
break;
case PassTextDateStyle.full:
default:
format = DateFormat.yMMMMEEEEd(language);
break;
}
switch (timeStyle) {
case PassTextDateStyle.none:
break;
case PassTextDateStyle.short:
format.add_jm();
break;
case PassTextDateStyle.medium:
case PassTextDateStyle.long:
case PassTextDateStyle.full:
default:
format.add_jms();
break;
}
if (ignoresTimeZone) {
/// violating standard: return in UTC instead of original timezone
return format.format(dateTime);
} else {
return format.format(dateTime.toLocal());
}
} else if (value is NumberDictionaryValue) {
num number = (value as NumberDictionaryValue).number;
if (currencyCode != null) {
return NumberFormat.simpleCurrency(locale: language, name: currencyCode)
.format(number);
} else if (numberStyle == PassTextNumberStyle.percent) {
return NumberFormat.percentPattern(language).format(number);
} else if (numberStyle == PassTextNumberStyle.scientific) {
return NumberFormat.scientificPattern(language).format(number);
} else {
/// PassTextNumberStyle.SpellOut not implemented
/// default to decimal format
return NumberFormat.decimalPattern(language).format(number);
}
}
return null;
}
/// 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 = num.tryParse(value);
if (number != null) return NumberDictionaryValue(number);
final dateTime = DateTime.tryParse(value);
if (dateTime != null) return DateTimeDictionaryValue(dateTime);
return StringDictionaryValue(value);
}
}
/// [String] content of a [DictionaryField].
class StringDictionaryValue extends DictionaryValue {
final String string;
const StringDictionaryValue(this.string);
}
/// [DateTime] content of a [DictionaryField].
class DateTimeDictionaryValue extends DictionaryValue {
final DateTime dateTime;
const DateTimeDictionaryValue(this.dateTime);
}
/// [num] content of a [DictionaryField].
class NumberDictionaryValue extends DictionaryValue {
final num number;
const NumberDictionaryValue(this.number);
}
/// 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,
}
/// Possible types of [DictionaryField.dateStyle] and [DictionaryField.timeStyle].
enum PassTextDateStyle {
/// PKDateStyleNone
none,
/// PKDateStyleShort
short,
/// PKDateStyleMedium
medium,
/// PKDateStyleLong
long,
/// PKDateStyleFull
full,
}
/// Possible types of [DictionaryField.numberStyle].
enum PassTextNumberStyle {
/// PKNumberStyleDecimal
decimal,
/// PKNumberStylePercent
percent,
/// PKNumberStyleScientific
scientific,
/// PKNumberStyleSpellOut
spellOut,
}
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;
}
}
}