From 4ff2faaae1ba8ef3f9ed24a6cb4fa90bc3099e03 Mon Sep 17 00:00:00 2001 From: SifAa <43337067+SifAa@users.noreply.github.com> Date: Thu, 5 Mar 2026 11:24:44 +0100 Subject: [PATCH 1/3] feat(ios): implement text search in publication --- .../FlutterReadiumPlugin.swift | 30 +++++++++++++++++++ .../flutter_readium/ReadiumReaderView.swift | 30 +++++++++++++++++++ .../utils/ReadiumExtensions.swift | 24 +++++++++++++++ 3 files changed, 84 insertions(+) diff --git a/flutter_readium/ios/flutter_readium/Sources/flutter_readium/FlutterReadiumPlugin.swift b/flutter_readium/ios/flutter_readium/Sources/flutter_readium/FlutterReadiumPlugin.swift index 1577127b..0c72293e 100644 --- a/flutter_readium/ios/flutter_readium/Sources/flutter_readium/FlutterReadiumPlugin.swift +++ b/flutter_readium/ios/flutter_readium/Sources/flutter_readium/FlutterReadiumPlugin.swift @@ -394,6 +394,36 @@ public class FlutterReadiumPlugin: NSObject, FlutterPlugin, ReadiumShared.Warnin let _ = await self.timebasedNavigator?.seekRelative(byOffsetSeconds: seekOffset) result(nil) } + case "searchInPublication": + guard let publication = getCurrentPublication(), + let query = call.arguments as? String + else { + result( + FlutterError( + code: "InvalidArgument", + message: "No publication open or invalid parameters to searchInPublication", + details: nil)) + return + } + Task { + do { + let searchResults = await publication.searchInContentForQuery(query) + let searchResultsJson = searchResults.map { $0.json } + let jsonData = try JSONSerialization.data(withJSONObject: searchResultsJson) + let jsonString = String(data: jsonData, encoding: .utf8) ?? "[]" + await MainActor.run { + result(jsonString) + } + } catch { + await MainActor.run { + result( + FlutterError( + code: "SearchError", + message: "Failed to perform search with query: \(query)", + details: error.localizedDescription)) + } + } + } default: result(FlutterMethodNotImplemented) diff --git a/flutter_readium/ios/flutter_readium/Sources/flutter_readium/ReadiumReaderView.swift b/flutter_readium/ios/flutter_readium/Sources/flutter_readium/ReadiumReaderView.swift index a8bf7ae3..8c064e31 100644 --- a/flutter_readium/ios/flutter_readium/Sources/flutter_readium/ReadiumReaderView.swift +++ b/flutter_readium/ios/flutter_readium/Sources/flutter_readium/ReadiumReaderView.swift @@ -39,6 +39,7 @@ public class ReadiumReaderView: NSObject, FlutterPlatformView, EPUBNavigatorDele private let readiumViewController: EPUBNavigatorViewController private var isVerticalScroll = false private var hasSentReady = false + private let publication: Publication var publicationIdentifier: String? @@ -65,6 +66,7 @@ public class ReadiumReaderView: NSObject, FlutterPlatformView, EPUBNavigatorDele let creationParams = args as! Dictionary let publication = FlutterReadiumPlugin.instance!.getCurrentPublication()! + self.publication = publication let preferencesMap = creationParams["preferences"] as? Dictionary? let defaultPreferences = preferencesMap == nil ? nil : EPUBPreferences.init(fromMap: preferencesMap!!) @@ -469,6 +471,34 @@ public class ReadiumReaderView: NSObject, FlutterPlatformView, EPUBNavigatorDele emitReaderStatusChanged(status: ReadiumReaderStatusClosed) result(nil) break + case "searchInPublication": + Task.detached(priority: .high) { + guard let query = call.arguments as? String else { + await MainActor.run { + result(FlutterError.init( + code: "InvalidArgument", + message: "Invalid parameters to searchInPublication: \(call.arguments.debugDescription)", + details: nil)) + } + return + } + do { + let searchResults = await self.publication.searchInContentForQuery(query) + let searchResultsJson = searchResults.map { $0.json } + let jsonData = try JSONSerialization.data(withJSONObject: searchResultsJson) + let jsonString = String(data: jsonData, encoding: .utf8) ?? "[]" + await MainActor.run { + result(jsonString) + } + } catch let err { + await MainActor.run { + result(FlutterError.init( + code: "SearchError", + message: "Failed to perform search with query: \(query)", + details: err.localizedDescription)) + } + } + } default: print(TAG, "Unhandled call \(call.method)") result(FlutterMethodNotImplemented) diff --git a/flutter_readium/ios/flutter_readium/Sources/flutter_readium/utils/ReadiumExtensions.swift b/flutter_readium/ios/flutter_readium/Sources/flutter_readium/utils/ReadiumExtensions.swift index e1f3f9f2..27321bc6 100644 --- a/flutter_readium/ios/flutter_readium/Sources/flutter_readium/utils/ReadiumExtensions.swift +++ b/flutter_readium/ios/flutter_readium/Sources/flutter_readium/utils/ReadiumExtensions.swift @@ -22,6 +22,30 @@ extension Publication { var containsMediaOverlays: Bool { self.readingOrder.contains(where: { $0.alternates.contains(where: { $0.mediaType?.matches(MediaType("application/vnd.syncnarr+json")) == true })}) } + + func searchInContentForQuery(_ query: String) async -> [LocatorCollection] { + guard let searchService: SearchService = findService(SearchService.self) else { + debugPrint("No SearchService available") + return [] + } + var collections: [LocatorCollection] = [] + switch await searchService.search(query: query, options: .init()) { + case .failure(let err): + switch err { + case .badQuery(let queryErr): + debugPrint("Search failed, bad query: \(queryErr)") + case .reading(let readErr): + debugPrint("Search failed, reading error: \(readErr)") + case .publicationNotSearchable: + debugPrint("Search failed, publication is not searchable") + } + case .success(let iterator): + _ = await iterator.forEach { collection in + collections.append(collection) + } + } + return collections + } } extension MediaPlaybackState { From 401bc7ec668dd76fc28c8e8c81cc467432f3dc5d Mon Sep 17 00:00:00 2001 From: SifAa <43337067+SifAa@users.noreply.github.com> Date: Thu, 5 Mar 2026 11:38:33 +0100 Subject: [PATCH 2/3] feat: implement text search functionality in publication --- flutter_readium/lib/flutter_readium.dart | 3 + .../test/flutter_readium_test.dart | 6 + .../flutter_readium_platform_interface.dart | 4 + .../lib/method_channel_flutter_readium.dart | 42 +++++ .../lib/src/shared/publication/dto/index.dart | 1 + .../publication/dto/text_search_result.dart | 45 +++++ .../lib/src/shared/publication/index.dart | 2 + .../publication/locator_collection.dart | 161 ++++++++++++++++++ 8 files changed, 264 insertions(+) create mode 100644 flutter_readium_platform_interface/lib/src/shared/publication/dto/index.dart create mode 100644 flutter_readium_platform_interface/lib/src/shared/publication/dto/text_search_result.dart create mode 100644 flutter_readium_platform_interface/lib/src/shared/publication/locator_collection.dart diff --git a/flutter_readium/lib/flutter_readium.dart b/flutter_readium/lib/flutter_readium.dart index 1b7d5f28..9cdfcd3a 100644 --- a/flutter_readium/lib/flutter_readium.dart +++ b/flutter_readium/lib/flutter_readium.dart @@ -130,4 +130,7 @@ class FlutterReadium { return goByLink(pageLink, pub); } + + Future> searchInPublication(String searchKey) async => + _platform.searchInPublication(searchKey); } diff --git a/flutter_readium/test/flutter_readium_test.dart b/flutter_readium/test/flutter_readium_test.dart index afd3a569..0c2bc1ca 100644 --- a/flutter_readium/test/flutter_readium_test.dart +++ b/flutter_readium/test/flutter_readium_test.dart @@ -187,6 +187,12 @@ class MockFlutterReadiumPlatform with MockPlatformInterfaceMixin implements Flut // TODO: implement audioSeekBy throw UnimplementedError(); } + + @override + Future> searchInPublication(String searchKey) { + // TODO: implement searchInPublication + throw UnimplementedError(); + } } void main() { diff --git a/flutter_readium_platform_interface/lib/flutter_readium_platform_interface.dart b/flutter_readium_platform_interface/lib/flutter_readium_platform_interface.dart index cc26122c..025984ef 100644 --- a/flutter_readium_platform_interface/lib/flutter_readium_platform_interface.dart +++ b/flutter_readium_platform_interface/lib/flutter_readium_platform_interface.dart @@ -138,6 +138,10 @@ abstract class FlutterReadiumPlatform extends PlatformInterface { Future audioSeekBy(Duration offset) => throw UnimplementedError('seekInAudio() has not been implemented'); // AUDIOBOOK API - END + Future> searchInPublication(final String searchKey) { + throw UnimplementedError('searchInPublication() has not been implemented'); + } + // State stream for reader status changes Stream get onReaderStatusChanged { throw UnimplementedError('onReaderStatus stream has not been implemented.'); diff --git a/flutter_readium_platform_interface/lib/method_channel_flutter_readium.dart b/flutter_readium_platform_interface/lib/method_channel_flutter_readium.dart index 659f1ef5..059ce360 100644 --- a/flutter_readium_platform_interface/lib/method_channel_flutter_readium.dart +++ b/flutter_readium_platform_interface/lib/method_channel_flutter_readium.dart @@ -188,4 +188,46 @@ class MethodChannelFlutterReadium extends FlutterReadiumPlatform { @override Future audioSeekBy(Duration offset) => methodChannel.invokeMethod('audioSeekBy', offset.inSeconds); + + @override + Future> searchInPublication(String searchKey) async { + final resultString = await methodChannel.invokeMethod('searchInPublication', searchKey); + + if (resultString == null || resultString.isEmpty) { + return []; + } + + try { + final decoded = json.decode(resultString); + if (decoded is List) { + final collections = decoded + .map((e) => LocatorCollection.fromJson(e as Map?)) + .whereType() + .toList(); + + return _convertLocatorCollectionsToTextSearchResults(collections); + } + return []; + } catch (e) { + throw Exception('Failed to parse search results: $e'); + } + } + + List _convertLocatorCollectionsToTextSearchResults(final List collections) { + final results = []; + + for (final collection in collections) { + for (final locator in collection.locators) { + final result = TextSearchResult( + locator: locator, + chapterTitle: locator.title ?? collection.metadata.title ?? '', + pageNumbers: null, // pageNumbers not currently available from Readium search + ); + + results.add(result); + } + } + + return results; + } } diff --git a/flutter_readium_platform_interface/lib/src/shared/publication/dto/index.dart b/flutter_readium_platform_interface/lib/src/shared/publication/dto/index.dart new file mode 100644 index 00000000..92a880c1 --- /dev/null +++ b/flutter_readium_platform_interface/lib/src/shared/publication/dto/index.dart @@ -0,0 +1 @@ +export 'text_search_result.dart'; diff --git a/flutter_readium_platform_interface/lib/src/shared/publication/dto/text_search_result.dart b/flutter_readium_platform_interface/lib/src/shared/publication/dto/text_search_result.dart new file mode 100644 index 00000000..abccf609 --- /dev/null +++ b/flutter_readium_platform_interface/lib/src/shared/publication/dto/text_search_result.dart @@ -0,0 +1,45 @@ +// NOTE: This is a Nota type +import 'package:equatable/equatable.dart'; + +import '../../../utils/jsonable.dart'; +import '../index.dart'; + +class TextSearchResult with EquatableMixin implements JSONable { + const TextSearchResult({required this.locator, this.chapterTitle, this.pageNumbers}); + + final Locator locator; + final String? chapterTitle; + final List? pageNumbers; + + factory TextSearchResult.fromJson(Map? json) { + if (json == null) { + throw ArgumentError('json cannot be null'); + } + + final jsonObject = Map.of(json); + + return TextSearchResult( + locator: Locator.fromJson(jsonObject['locator'] as Map)!, + chapterTitle: jsonObject['chapterTitle'] as String?, + pageNumbers: (jsonObject['pageNumbers'] as List?)?.map((e) => e as String).toList(), + ); + } + + @override + Map toJson() => {} + ..put('locator', locator.toJson()) + ..putOpt('chapterTitle', chapterTitle) + ..putIterableIfNotEmpty('pageNumbers', pageNumbers); + + @override + List get props => [locator, chapterTitle, pageNumbers]; + + @override + String toString() => + 'TextSearchResult{locator: $locator, chapterTitle: $chapterTitle, ' + 'pageNumbers: $pageNumbers}'; +} + +extension TextSearchResultExtension on TextSearchResult { + LocatorText? get text => locator.text; +} diff --git a/flutter_readium_platform_interface/lib/src/shared/publication/index.dart b/flutter_readium_platform_interface/lib/src/shared/publication/index.dart index cecc3e21..ee0f71f4 100644 --- a/flutter_readium_platform_interface/lib/src/shared/publication/index.dart +++ b/flutter_readium_platform_interface/lib/src/shared/publication/index.dart @@ -1,4 +1,5 @@ export 'drm.dart'; +export 'dto/index.dart'; export 'encryption.dart'; export 'epub/index.dart'; export 'format.dart'; @@ -7,6 +8,7 @@ export 'link.dart'; export 'link_list_extension.dart'; export 'localized_string.dart'; export 'locator.dart'; +export 'locator_collection.dart'; export 'metadata/index.dart'; export 'opds/index.dart'; export 'positions_list.dart'; diff --git a/flutter_readium_platform_interface/lib/src/shared/publication/locator_collection.dart b/flutter_readium_platform_interface/lib/src/shared/publication/locator_collection.dart new file mode 100644 index 00000000..766e0f03 --- /dev/null +++ b/flutter_readium_platform_interface/lib/src/shared/publication/locator_collection.dart @@ -0,0 +1,161 @@ +// Copyright (c) 2021 Mantano. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE.Iridium file. + +import 'package:equatable/equatable.dart'; +import 'package:meta/meta.dart'; + +import '../../utils/additional_properties.dart'; +import '../../utils/jsonable.dart'; +import 'link.dart'; +import 'locator.dart'; + +/// Represents a sequential list of [Locator] objects. +/// +/// For example, a search result or a list of positions. +@immutable +class LocatorCollection with EquatableMixin implements JSONable { + const LocatorCollection({ + this.metadata = const LocatorCollectionMetadata(), + this.links = const [], + this.locators = const [], + }); + + final LocatorCollectionMetadata metadata; + final List links; + final List locators; + + static LocatorCollection? fromJson(Map? json) { + if (json == null) { + return null; + } + + final jsonObject = Map.of(json); + + final metadata = LocatorCollectionMetadata.fromJson(jsonObject['metadata'] as Map?); + + final linksJson = jsonObject['links'] as List?; + final links = linksJson?.map((e) => Link.fromJson(e as Map?)).whereType().toList() ?? []; + + final locatorsJson = jsonObject['locators'] as List?; + final locators = + locatorsJson?.map((e) => Locator.fromJson(e as Map?)).whereType().toList() ?? []; + + return LocatorCollection(metadata: metadata, links: links, locators: locators); + } + + @override + Map toJson() { + final json = {}; + + final metadataJson = metadata.toJson(); + if (metadataJson.isNotEmpty) { + json['metadata'] = metadataJson; + } + + if (links.isNotEmpty) { + json['links'] = links.map((e) => e.toJson()).toList(); + } + + json['locators'] = locators.map((e) => e.toJson()).toList(); + + return json; + } + + LocatorCollection copyWith({LocatorCollectionMetadata? metadata, List? links, List? locators}) => + LocatorCollection( + metadata: metadata ?? this.metadata, + links: links ?? this.links, + locators: locators ?? this.locators, + ); + + @override + List get props => [metadata, links, locators]; + + @override + String toString() => 'LocatorCollection{metadata: $metadata, links: $links, locators: $locators}'; +} + +/// Holds the metadata of a [LocatorCollection]. +@immutable +class LocatorCollectionMetadata extends AdditionalProperties with EquatableMixin implements JSONable { + const LocatorCollectionMetadata({this.localizedTitle, this.numberOfItems, super.additionalProperties}); + + /// The localized title. Can be a simple string or a map of language codes to strings. + final dynamic localizedTitle; + + /// Indicates the total number of locators in the collection. + final int? numberOfItems; + + /// Returns the title as a simple string. + String? get title { + if (localizedTitle == null) { + return null; + } + if (localizedTitle is String) { + return localizedTitle as String; + } + if (localizedTitle is Map) { + final map = localizedTitle as Map; + // Return the first available value or the 'en' value if available + if (map.containsKey('en')) { + return map['en'] as String?; + } + return map.values.firstOrNull as String?; + } + return null; + } + + static LocatorCollectionMetadata fromJson(Map? json) { + if (json == null) { + return const LocatorCollectionMetadata(); + } + + final jsonObject = Map.of(json); + + final localizedTitle = jsonObject.remove('title'); + final numberOfItems = jsonObject.remove('numberOfItems') as int?; + + // Validate numberOfItems is positive + final validNumberOfItems = (numberOfItems != null && numberOfItems > 0) ? numberOfItems : null; + + return LocatorCollectionMetadata( + localizedTitle: localizedTitle, + numberOfItems: validNumberOfItems, + additionalProperties: jsonObject, + ); + } + + @override + Map toJson() { + final json = Map.of(additionalProperties); + + if (localizedTitle != null) { + json['title'] = localizedTitle; + } + + if (numberOfItems != null) { + json['numberOfItems'] = numberOfItems; + } + + return json; + } + + LocatorCollectionMetadata copyWith({ + dynamic localizedTitle, + int? numberOfItems, + Map? additionalProperties, + }) => LocatorCollectionMetadata( + localizedTitle: localizedTitle ?? this.localizedTitle, + numberOfItems: numberOfItems ?? this.numberOfItems, + additionalProperties: additionalProperties ?? this.additionalProperties, + ); + + @override + List get props => [localizedTitle, numberOfItems, additionalProperties]; + + @override + String toString() => + 'LocatorCollectionMetadata{title: $title, numberOfItems: $numberOfItems, ' + 'otherMetadata: $additionalProperties}'; +} From 476806284d05a4b2d1bdbdeb5b7c2b07427b9bf6 Mon Sep 17 00:00:00 2001 From: Daniel Freiling Date: Fri, 13 Mar 2026 10:37:05 +0100 Subject: [PATCH 3/3] refactor: move TextSearchResult to native side --- .../FlutterReadiumPlugin.swift | 16 ++++++---- .../flutter_readium/ReadiumReaderView.swift | 28 ------------------ .../model/TextSearchResult.swift | 29 +++++++++++++++++++ .../utils/ReadiumExtensions.swift | 16 ++++------ .../lib/method_channel_flutter_readium.dart | 26 +++-------------- 5 files changed, 49 insertions(+), 66 deletions(-) create mode 100644 flutter_readium/ios/flutter_readium/Sources/flutter_readium/model/TextSearchResult.swift diff --git a/flutter_readium/ios/flutter_readium/Sources/flutter_readium/FlutterReadiumPlugin.swift b/flutter_readium/ios/flutter_readium/Sources/flutter_readium/FlutterReadiumPlugin.swift index d6983779..7a251447 100644 --- a/flutter_readium/ios/flutter_readium/Sources/flutter_readium/FlutterReadiumPlugin.swift +++ b/flutter_readium/ios/flutter_readium/Sources/flutter_readium/FlutterReadiumPlugin.swift @@ -416,11 +416,17 @@ public class FlutterReadiumPlugin: NSObject, FlutterPlugin, ReadiumShared.Warnin Task { do { let searchResults = await publication.searchInContentForQuery(query) - let searchResultsJson = searchResults.map { $0.json } - let jsonData = try JSONSerialization.data(withJSONObject: searchResultsJson) - let jsonString = String(data: jsonData, encoding: .utf8) ?? "[]" - await MainActor.run { - result(jsonString) + switch searchResults { + case .failure(let err): + throw err + case .success(let searchResultsCols): + let fallbackTitle = searchResultsCols.first?.metadata.title ?? publication.metadata.title ?? "Unknown chapter" + // TODO: Should we try to find physical page-numbers for the results? + let results = searchResultsCols.flatMap { $0.locators.map { l in TextSearchResult(locator: l, chapterTitle: l.title ?? fallbackTitle, pageNumbers: nil) } } + let searchResultsJson = try results.map { try $0.toJsonString() } + await MainActor.run { + result(searchResultsJson) + } } } catch { await MainActor.run { diff --git a/flutter_readium/ios/flutter_readium/Sources/flutter_readium/ReadiumReaderView.swift b/flutter_readium/ios/flutter_readium/Sources/flutter_readium/ReadiumReaderView.swift index f669d6d9..90b2d322 100644 --- a/flutter_readium/ios/flutter_readium/Sources/flutter_readium/ReadiumReaderView.swift +++ b/flutter_readium/ios/flutter_readium/Sources/flutter_readium/ReadiumReaderView.swift @@ -383,34 +383,6 @@ public class ReadiumReaderView: NSObject, FlutterPlatformView, EPUBNavigatorDele emitReaderStatusChanged(status: ReadiumReaderStatusClosed) result(nil) break - case "searchInPublication": - Task.detached(priority: .high) { - guard let query = call.arguments as? String else { - await MainActor.run { - result(FlutterError.init( - code: "InvalidArgument", - message: "Invalid parameters to searchInPublication: \(call.arguments.debugDescription)", - details: nil)) - } - return - } - do { - let searchResults = await self.publication.searchInContentForQuery(query) - let searchResultsJson = searchResults.map { $0.json } - let jsonData = try JSONSerialization.data(withJSONObject: searchResultsJson) - let jsonString = String(data: jsonData, encoding: .utf8) ?? "[]" - await MainActor.run { - result(jsonString) - } - } catch let err { - await MainActor.run { - result(FlutterError.init( - code: "SearchError", - message: "Failed to perform search with query: \(query)", - details: err.localizedDescription)) - } - } - } default: Log.reader.warn("Unhandled call: \(call.method)") result(FlutterMethodNotImplemented) diff --git a/flutter_readium/ios/flutter_readium/Sources/flutter_readium/model/TextSearchResult.swift b/flutter_readium/ios/flutter_readium/Sources/flutter_readium/model/TextSearchResult.swift new file mode 100644 index 00000000..d41a7318 --- /dev/null +++ b/flutter_readium/ios/flutter_readium/Sources/flutter_readium/model/TextSearchResult.swift @@ -0,0 +1,29 @@ +import ReadiumShared + +struct TextSearchResult { + let locator: Locator + let chapterTitle: String? + let pageNumbers: [String]? + + public init(locator: Locator, chapterTitle: String? = nil, pageNumbers: [String]? = nil) { + self.locator = locator + self.chapterTitle = chapterTitle + self.pageNumbers = pageNumbers + } + + func toJson() -> [String: Any?] { + let map: [String: Any?] = [ + "locator": locator.jsonString, + "chapterTitle": chapterTitle, + "pageNumbers": pageNumbers?.joined(separator: ","), + ] + + return map + } + + func toJsonString(pretty: Bool = false) throws -> String? { + let options: JSONSerialization.WritingOptions = pretty ? [.prettyPrinted] : [] + let data = try JSONSerialization.data(withJSONObject: toJson(), options: options) + return String(data: data, encoding: .utf8) + } +} diff --git a/flutter_readium/ios/flutter_readium/Sources/flutter_readium/utils/ReadiumExtensions.swift b/flutter_readium/ios/flutter_readium/Sources/flutter_readium/utils/ReadiumExtensions.swift index 72e25521..050371bb 100644 --- a/flutter_readium/ios/flutter_readium/Sources/flutter_readium/utils/ReadiumExtensions.swift +++ b/flutter_readium/ios/flutter_readium/Sources/flutter_readium/utils/ReadiumExtensions.swift @@ -74,28 +74,22 @@ extension Publication { } } - func searchInContentForQuery(_ query: String) async -> [LocatorCollection] { + func searchInContentForQuery(_ query: String) async -> Result<[LocatorCollection]> { guard let searchService: SearchService = findService(SearchService.self) else { Log.readium.warn("No SearchService available") - return [] + return Result.failure(SearchError.publicationNotSearchable) } var collections: [LocatorCollection] = [] switch await searchService.search(query: query, options: .init()) { case .failure(let err): - switch err { - case .badQuery(let queryErr): - Log.readium.error("Search failed, bad query: \(queryErr)") - case .reading(let readErr): - Log.readium.error("Search failed, reading error: \(readErr)") - case .publicationNotSearchable: - Log.readium.error("Search failed, publication is not searchable") - } + Log.readium.error("Search in publication content failed: \(err)") + return Result.failure(err) case .success(let iterator): _ = await iterator.forEach { collection in collections.append(collection) } } - return collections + return .success(collections) } /** diff --git a/flutter_readium_platform_interface/lib/method_channel_flutter_readium.dart b/flutter_readium_platform_interface/lib/method_channel_flutter_readium.dart index e7bc0a15..42d720c5 100644 --- a/flutter_readium_platform_interface/lib/method_channel_flutter_readium.dart +++ b/flutter_readium_platform_interface/lib/method_channel_flutter_readium.dart @@ -205,34 +205,16 @@ class MethodChannelFlutterReadium extends FlutterReadiumPlatform { try { final decoded = json.decode(resultString); if (decoded is List) { - final collections = decoded - .map((e) => LocatorCollection.fromJson(e as Map?)) - .whereType() + final results = decoded + .map((e) => TextSearchResult.fromJson(e as Map?)) + .whereType() .toList(); - return _convertLocatorCollectionsToTextSearchResults(collections); + return results; } return []; } catch (e) { throw Exception('Failed to parse search results: $e'); } } - - List _convertLocatorCollectionsToTextSearchResults(final List collections) { - final results = []; - - for (final collection in collections) { - for (final locator in collection.locators) { - final result = TextSearchResult( - locator: locator, - chapterTitle: locator.title ?? collection.metadata.title ?? '', - pageNumbers: null, // pageNumbers not currently available from Readium search - ); - - results.add(result); - } - } - - return results; - } }