Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ When upgrading a toolkit, move all three platforms together where API surface ov
- **Bridge serialization**: Readium-owned objects (`Locator`, `Decoration`, …) → JSON strings via `json.encode`; plugin-owned flat structures (preferences, action configs) → Maps. Rationale + Web-TS `.serialize()` rules: `docs/architecture.md#bridge-serialization`.
- **Models**: hand-written `toJson`/`fromJson`. No `json_serializable`/`freezed`/build_runner codegen — don't reintroduce.
- **PDF locator**: position = 1-based page in `Locator.locations.position` (matches upstream); don't invent plugin-side parallels to upstream models. Detail: `docs/api-reference/locator.md#pdf-locators`.
- **copyWith sentinel**: use the shared `const unset` from `utils/constants.dart` for parameters that default to "not set" — never declare a new `_unset = Object()`. Merge additional-properties via `AdditionalProperties.copyAdditionalProperties()` instead of inline merge logic.

### Android

Expand Down
2 changes: 1 addition & 1 deletion flutter_readium/example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,6 @@ SPEC CHECKSUMS:
SwiftSoup: 959c9ac70d7053fbf476341bfac90ab228523f2a
wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556

PODFILE CHECKSUM: 2f4fb5dcab74326e2b88472463efb20c7535dda1
PODFILE CHECKSUM: c4906ba94e31390c41a115760de24db9cfc2379e

COCOAPODS: 1.16.2
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:collection/collection.dart';
import 'package:meta/meta.dart';

import '../shared/index.dart';
import '../shared/publication.dart';
Expand All @@ -15,7 +16,7 @@ extension LocationExtension on Locations {
if (fragment != null) fragment.fragment,
];

return copyWith(fragments: newFragments.isEmpty ? null : newFragments);
return copyWith(fragments: newFragments);
}

Locations copyWithPhysicalPageNumber(final String? index) {
Expand All @@ -24,7 +25,7 @@ extension LocationExtension on Locations {
if (index != null) 'physicalPage=$index',
];

return copyWith(fragments: newFragments.isEmpty ? null : newFragments);
return copyWith(fragments: newFragments);
}

Locations copyWithPage(final int? index) {
Expand All @@ -33,7 +34,7 @@ extension LocationExtension on Locations {
if (index != null) 'page=$index',
];

return copyWith(fragments: newFragments.isEmpty ? null : newFragments);
return copyWith(fragments: newFragments);
}

/// Duration must be in seconds.
Expand All @@ -44,7 +45,7 @@ extension LocationExtension on Locations {
if (duration != null) 'duration=$duration',
];

return copyWith(fragments: newFragments.isEmpty ? null : newFragments);
return copyWith(fragments: newFragments);
}

String? get physicalPage => fragments.firstWhereOrNull((final f) => f.startsWith('physicalPage='))?.split('=').last;
Expand All @@ -64,6 +65,7 @@ extension LocationExtension on Locations {
);
}

@immutable
class TimeFragment {
const TimeFragment({this.begin = Duration.zero, this.end});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
import '../utils/constants.dart';

/// Configures the automatic audio-stream error recovery loop (retry attempts,
/// backoff, and stall detection) shared by the iOS/Android/web audio
Expand Down Expand Up @@ -59,15 +60,19 @@ class AudioRecoveryPolicy with Equatable {
};

AudioRecoveryPolicy copyWith({
int? maxAttempts,
double? backoffBaseSeconds,
double? stallTimeoutSeconds,
double? connectionTimeoutSeconds,
Object? maxAttempts = unset,
Object? backoffBaseSeconds = unset,
Object? stallTimeoutSeconds = unset,
Object? connectionTimeoutSeconds = unset,
}) => AudioRecoveryPolicy(
maxAttempts: maxAttempts ?? this.maxAttempts,
backoffBaseSeconds: backoffBaseSeconds ?? this.backoffBaseSeconds,
stallTimeoutSeconds: stallTimeoutSeconds ?? this.stallTimeoutSeconds,
connectionTimeoutSeconds: connectionTimeoutSeconds ?? this.connectionTimeoutSeconds,
maxAttempts: identical(maxAttempts, unset) ? this.maxAttempts : (maxAttempts as int),
backoffBaseSeconds: identical(backoffBaseSeconds, unset) ? this.backoffBaseSeconds : (backoffBaseSeconds as double),
stallTimeoutSeconds: identical(stallTimeoutSeconds, unset)
? this.stallTimeoutSeconds
: (stallTimeoutSeconds as double),
connectionTimeoutSeconds: identical(connectionTimeoutSeconds, unset)
? this.connectionTimeoutSeconds
: (connectionTimeoutSeconds as double),
);

@override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,25 +132,33 @@ class AudioPreferences with Equatable implements JSONable {
];

AudioPreferences copyWith({
double? volume,
double? speed,
double? pitch,
double? seekInterval,
bool? continuousSeeking,
bool? allowExternalSeeking,
double? updateIntervalSecs,
ControlPanelInfoType? controlPanelInfoType,
ControlPanelTimebase? controlPanelTimebase,
Object? volume = unset,
Object? speed = unset,
Object? pitch = unset,
Object? seekInterval = unset,
Object? continuousSeeking = unset,
Object? allowExternalSeeking = unset,
Object? updateIntervalSecs = unset,
Object? controlPanelInfoType = unset,
Object? controlPanelTimebase = unset,
}) => AudioPreferences(
volume: volume ?? this.volume,
speed: speed ?? this.speed,
pitch: pitch ?? this.pitch,
seekInterval: seekInterval ?? this.seekInterval,
continuousSeeking: continuousSeeking ?? this.continuousSeeking,
allowExternalSeeking: allowExternalSeeking ?? this.allowExternalSeeking,
updateIntervalSecs: updateIntervalSecs ?? this.updateIntervalSecs,
controlPanelInfoType: controlPanelInfoType ?? this.controlPanelInfoType,
controlPanelTimebase: controlPanelTimebase ?? this.controlPanelTimebase,
volume: identical(volume, unset) ? this.volume : (volume as double?)!,
speed: identical(speed, unset) ? this.speed : (speed as double?)!,
pitch: identical(pitch, unset) ? this.pitch : (pitch as double?)!,
seekInterval: identical(seekInterval, unset) ? this.seekInterval : (seekInterval as double?)!,
continuousSeeking: identical(continuousSeeking, unset) ? this.continuousSeeking : (continuousSeeking as bool?),
allowExternalSeeking: identical(allowExternalSeeking, unset)
? this.allowExternalSeeking
: (allowExternalSeeking as bool?),
updateIntervalSecs: identical(updateIntervalSecs, unset)
? this.updateIntervalSecs
: (updateIntervalSecs as double?)!,
controlPanelInfoType: identical(controlPanelInfoType, unset)
? this.controlPanelInfoType
: (controlPanelInfoType as ControlPanelInfoType?)!,
controlPanelTimebase: identical(controlPanelTimebase, unset)
? this.controlPanelTimebase
: (controlPanelTimebase as ControlPanelTimebase?)!,
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,11 @@ class ReaderDecoration implements JSONable {
@override
Map<String, dynamic> toJson() => {'id': id, 'locator': locator.toJson(), 'style': style.toJson()};

ReaderDecoration copyWith({String? id, Locator? locator, ReaderDecorationStyle? style}) =>
ReaderDecoration(id: id ?? this.id, locator: locator ?? this.locator, style: style ?? this.style);
ReaderDecoration copyWith({Object id = unset, Object locator = unset, Object style = unset}) => ReaderDecoration(
id: identical(id, unset) ? this.id : (id as String),
locator: identical(locator, unset) ? this.locator : (locator as Locator),
style: identical(style, unset) ? this.style : (style as ReaderDecorationStyle),
);
}

class ReaderDecorationStyle implements JSONable {
Expand Down Expand Up @@ -86,9 +89,10 @@ class ReaderDecorationStyle implements JSONable {
isActive: map['isActive'] as bool? ?? false,
);

ReaderDecorationStyle copyWith({DecorationStyle? style, Color? tint, bool? isActive}) => ReaderDecorationStyle(
style: style ?? this.style,
tint: tint ?? this.tint,
isActive: isActive ?? this.isActive,
);
ReaderDecorationStyle copyWith({Object style = unset, Object? tint = unset, Object isActive = unset}) =>
ReaderDecorationStyle(
style: identical(style, unset) ? this.style : (style as DecorationStyle),
tint: identical(tint, unset) ? this.tint : tint as Color?,
isActive: identical(isActive, unset) ? this.isActive : (isActive as bool),
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,21 +84,23 @@ class PDFPreferences with Equatable implements JSONable {
..putOpt('visibleScrollbar', visibleScrollbar);

PDFPreferences copyWith({
PDFLayout? layout,
PDFReadingProgression? readingProgression,
double? pageSpacing,
PDFFit? fit,
bool? offsetFirstPage,
PDFSpread? spread,
bool? visibleScrollbar,
Object? layout = unset,
Object? readingProgression = unset,
Object? pageSpacing = unset,
Object? fit = unset,
Object? offsetFirstPage = unset,
Object? spread = unset,
Object? visibleScrollbar = unset,
}) => PDFPreferences(
layout: layout ?? this.layout,
readingProgression: readingProgression ?? this.readingProgression,
pageSpacing: pageSpacing ?? this.pageSpacing,
fit: fit ?? this.fit,
offsetFirstPage: offsetFirstPage ?? this.offsetFirstPage,
spread: spread ?? this.spread,
visibleScrollbar: visibleScrollbar ?? this.visibleScrollbar,
layout: identical(layout, unset) ? this.layout : (layout as PDFLayout?)!,
readingProgression: identical(readingProgression, unset)
? this.readingProgression
: (readingProgression as PDFReadingProgression?)!,
pageSpacing: identical(pageSpacing, unset) ? this.pageSpacing : (pageSpacing as double?)!,
fit: identical(fit, unset) ? this.fit : (fit as PDFFit?)!,
offsetFirstPage: identical(offsetFirstPage, unset) ? this.offsetFirstPage : (offsetFirstPage as bool?),
spread: identical(spread, unset) ? this.spread : (spread as PDFSpread?),
visibleScrollbar: identical(visibleScrollbar, unset) ? this.visibleScrollbar : (visibleScrollbar as bool?),
);

@override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';

import '../enums.dart';
import '../utils/constants.dart';
import '../utils/jsonable.dart';
import '../utils/readium_log.dart';
import 'index.dart';
Expand Down Expand Up @@ -119,20 +120,20 @@ class ReaderTTSVoice with Equatable implements JSONable {
];

ReaderTTSVoice copyWith({
String? identifier,
String? name,
String? language,
bool? networkRequired,
TTSVoiceGender? gender,
TTSVoiceQuality? quality,
bool? active,
Object identifier = unset,
Object name = unset,
Object language = unset,
Object networkRequired = unset,
Object gender = unset,
Object? quality = unset,
Object? active = unset,
}) => ReaderTTSVoice(
identifier: identifier ?? this.identifier,
name: name ?? this.name,
language: language ?? this.language,
networkRequired: networkRequired ?? this.networkRequired,
gender: gender ?? this.gender,
quality: quality ?? this.quality,
active: active ?? this.active,
identifier: identical(identifier, unset) ? this.identifier : (identifier as String),
name: identical(name, unset) ? this.name : (name as String),
language: identical(language, unset) ? this.language : (language as String),
networkRequired: identical(networkRequired, unset) ? this.networkRequired : (networkRequired as bool),
gender: identical(gender, unset) ? this.gender : (gender as TTSVoiceGender),
quality: identical(quality, unset) ? this.quality : (quality as TTSVoiceQuality?),
active: identical(active, unset) ? this.active : (active as bool?),
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:dartx/dartx.dart';
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';

import '../../utils/constants.dart';
import '../../utils/jsonable.dart';
import '../opds.dart' show OpdsMetadata;
import '../publication/link.dart' show Link;
Expand All @@ -23,8 +24,13 @@ class Facet with Equatable implements JSONable {
@override
String toString() => 'Facet{metadata: $metadata, links: $links}';

Facet copyWith({OpdsMetadata? metadata, List<Link>? links}) =>
Facet(metadata: metadata ?? this.metadata, links: links ?? this.links);
Facet copyWith({
Object? metadata = unset,
Object? links = unset,
}) => Facet(
metadata: identical(metadata, unset) ? this.metadata : (metadata as OpdsMetadata),
links: identical(links, unset) ? this.links : (links as List<Link>),
);

@override
Map<String, dynamic> toJson() {
Expand Down
34 changes: 16 additions & 18 deletions flutter_readium_platform_interface/lib/src/shared/opds/feed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,27 +50,25 @@ class Feed extends AdditionalProperties with Equatable implements JSONable {
'context: $context}';

Feed copyWith({
OpdsMetadata? metadata,
List<Link>? links,
List<Facet>? facets,
List<Group>? groups,
List<OpdsPublication>? publications,
List<Link>? navigation,
List<String>? context,
Map<String, dynamic>? additionalProperties,
Object metadata = unset,
Object links = unset,
Object facets = unset,
Object groups = unset,
Object publications = unset,
Object navigation = unset,
Object context = unset,
Object? additionalProperties = unset,
}) {
final mergeProperties = Map<String, dynamic>.of(this.additionalProperties)
..addAll(additionalProperties ?? {})
..removeWhere((key, value) => value == null);
final mergeProperties = copyAdditionalProperties(additionalProperties: additionalProperties);

return Feed(
metadata: metadata ?? this.metadata,
links: links ?? this.links,
facets: facets ?? this.facets,
groups: groups ?? this.groups,
publications: publications ?? this.publications,
navigation: navigation ?? this.navigation,
context: context ?? this.context,
metadata: identical(metadata, unset) ? this.metadata : (metadata as OpdsMetadata),
links: identical(links, unset) ? this.links : (links as List<Link>),
facets: identical(facets, unset) ? this.facets : (facets as List<Facet>),
groups: identical(groups, unset) ? this.groups : (groups as List<Group>),
publications: identical(publications, unset) ? this.publications : (publications as List<OpdsPublication>),
navigation: identical(navigation, unset) ? this.navigation : (navigation as List<Link>),
context: identical(context, unset) ? this.context : (context as List<String>),
additionalProperties: mergeProperties,
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';

import '../../utils/constants.dart';
import '../../utils/jsonable.dart';
import '../opds.dart';
import '../publication/link.dart';
Expand Down Expand Up @@ -34,15 +35,15 @@ class Group with Equatable implements JSONable {
'publications: $publications, navigation: $navigation}';

Group copyWith({
OpdsMetadata? metadata,
List<Link>? links,
List<OpdsPublication>? publications,
List<Link>? navigation,
Object metadata = unset,
Object links = unset,
Object publications = unset,
Object navigation = unset,
}) => Group(
metadata: metadata ?? this.metadata,
links: links ?? this.links,
publications: publications ?? this.publications,
navigation: navigation ?? this.navigation,
metadata: identical(metadata, unset) ? this.metadata : (metadata as OpdsMetadata),
links: identical(links, unset) ? this.links : (links as List<Link>),
publications: identical(publications, unset) ? this.publications : (publications as List<OpdsPublication>),
navigation: identical(navigation, unset) ? this.navigation : (navigation as List<Link>),
);

@override
Expand Down
Loading
Loading