diff --git a/.github/workflows/flutter.yml b/.github/workflows/flutter.yml index 34c46e3e..06bf29fc 100644 --- a/.github/workflows/flutter.yml +++ b/.github/workflows/flutter.yml @@ -51,7 +51,7 @@ jobs: uses: subosito/flutter-action@v2 with: channel: stable - cache: true + cache: true - run: flutter pub get - run: flutter build apk - name: Upload apk @@ -72,11 +72,40 @@ jobs: cache: true - run: flutter build macos - name: Zip the app bundle - run: cd build/macos/Build/Products/Release && zip -r otzaria-macos.zip ./ + run: cd build/macos/Build/Products/Release && zip -r otzaria-macos.zip otzaria.app - name: Upload macos build uses: actions/upload-artifact@v4 with: name: otzaria-macos.zip path: build/macos/Build/Products/Release/otzaria-macos.zip - + build_ios: + runs-on: macos-latest + steps: + - name: Clone repository + uses: actions/checkout@v4 + - name: Set up Flutter + uses: subosito/flutter-action@v2 + with: + channel: stable + cache: true + - run: flutter pub get + - name: Build iOS + run: | + flutter build ios --release + cd build/ios/iphoneos + mkdir Payload + cp -r Runner.app Payload + zip -r otzaria-ios.ipa Payload + - name: Upload iOS build + uses: actions/upload-artifact@v4 + with: + name: otzaria-ios.ipa + path: build/ios/iphoneos/otzaria-ios.ipa + - name: Upload iOS archive + uses: actions/upload-artifact@v4 + with: + name: ios-build + path: | + build/ios/archive/Runner.xcarchive + build/ios/iphoneos/ diff --git a/lib/data/data_providers/isar_data_provider.dart b/lib/data/data_providers/isar_data_provider.dart index dc1164fd..f022691b 100644 --- a/lib/data/data_providers/isar_data_provider.dart +++ b/lib/data/data_providers/isar_data_provider.dart @@ -156,8 +156,10 @@ class IsarDataProvider { } Future getNumberOfBooksWithRefs() async { - final allRefs = isar.refs.where().findAll(); - final books = allRefs.groupBy((ref) => ref.bookTitle); + final allRefs = await isar.refs.where().findAllAsync(); + final books = await Isolate.run(() { + return allRefs.groupBy((ref) => ref.bookTitle); + }); return books.length; } diff --git a/lib/screens/find_ref_screen.dart b/lib/screens/find_ref_screen.dart index e0f85225..3f25148a 100644 --- a/lib/screens/find_ref_screen.dart +++ b/lib/screens/find_ref_screen.dart @@ -33,13 +33,8 @@ class _FindRefScreenState extends State Future _checkIndexStatus() async { final booksWithRefs = await DataRepository.instance.getNumberOfBooksWithRefs(); - final totalBooks = (await appModel.library).getAllBooks().length; - - // If there's a difference of more than 5 books or if there are no refs at all - if (booksWithRefs == 0 || (totalBooks - booksWithRefs) > 5) { - setState(() { - _needsIndexing = true; - }); + if (booksWithRefs == 0) { + appModel.createRefsFromLibrary(0); } } @@ -52,55 +47,6 @@ class _FindRefScreenState extends State ); } - Widget _buildIndexingMessage() { - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'האינדקס ריק או לא מעודכן. יש לעדכן את האינדקס כדי לחפש מקורות.', - textAlign: TextAlign.center, - style: TextStyle(fontSize: 16), - ), - const SizedBox(height: 20), - ElevatedButton( - onPressed: () async { - final result = await showDialog( - context: context, - builder: (context) => AlertDialog( - content: const Text( - 'האם ברצונך ליצור אינדקס מקורות? הדבר יאפס את האינדקס הקיים ועלול לקחת זמן ארוך מאד.', - ), - actions: [ - ElevatedButton( - child: const Text('ביטול'), - onPressed: () { - Navigator.pop(context, false); - }, - ), - ElevatedButton( - child: const Text('אישור'), - onPressed: () { - Navigator.pop(context, true); - }, - ), - ], - ), - ); - if (result == true) { - appModel.createRefsFromLibrary(0); - setState(() { - _needsIndexing = false; - }); - } - }, - child: const Text('יצירת אינדקס מקורות'), - ), - ], - ), - ); - } - @override Widget build(BuildContext context) { super.build(context); @@ -146,57 +92,49 @@ class _FindRefScreenState extends State }, ), Expanded( - child: _needsIndexing - ? _buildIndexingMessage() - : FutureBuilder>( - future: _refs, - builder: (context, snapshot) { - if (snapshot.connectionState == - ConnectionState.waiting) { - return const Center( - child: CircularProgressIndicator()); - } else if (snapshot.hasError) { - return Text('Error: ${snapshot.error}'); - } else if (snapshot.data!.isEmpty && - _searchController.text.length >= 3) { - return const Center( - child: Text( - 'אין תוצאות', - style: TextStyle(fontSize: 16), - ), - ); - } else { - return ListView.builder( - itemCount: snapshot.data!.length, - itemBuilder: (context, index) { - return ListTile( - title: Text(snapshot.data![index].ref), - onTap: () { - final appModel = Provider.of( - context, - listen: false); - if (snapshot.data![index].pdfBook) { - appModel.openBook( - PdfBook( - title: snapshot - .data![index].bookTitle, - path: snapshot - .data![index].pdfPath!), - snapshot.data![index].index); - } else { - appModel.openBook( - TextBook( - title: - snapshot.data![index].bookTitle, - ), - snapshot.data![index].index); - } - }); - }, - ); - } + child: FutureBuilder>( + future: _refs, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError) { + return Text('Error: ${snapshot.error}'); + } else if (snapshot.data!.isEmpty && + _searchController.text.length >= 3) { + return const Center( + child: Text( + 'אין תוצאות', + style: TextStyle(fontSize: 16), + ), + ); + } else { + return ListView.builder( + itemCount: snapshot.data!.length, + itemBuilder: (context, index) { + return ListTile( + title: Text(snapshot.data![index].ref), + onTap: () { + final appModel = + Provider.of(context, listen: false); + if (snapshot.data![index].pdfBook) { + appModel.openBook( + PdfBook( + title: snapshot.data![index].bookTitle, + path: snapshot.data![index].pdfPath!), + snapshot.data![index].index); + } else { + appModel.openBook( + TextBook( + title: snapshot.data![index].bookTitle, + ), + snapshot.data![index].index); + } + }); }, - ), + ); + } + }, + ), ), ], ), diff --git a/lib/screens/full_text_search/tantivy_full_text_search.dart b/lib/screens/full_text_search/tantivy_full_text_search.dart index c0b39d68..6f01d3fe 100644 --- a/lib/screens/full_text_search/tantivy_full_text_search.dart +++ b/lib/screens/full_text_search/tantivy_full_text_search.dart @@ -22,6 +22,7 @@ class _TantivyFullTextSearchState extends State bool get wantKeepAlive => true; ValueNotifier isLeftPaneOpen = ValueNotifier(false); + bool _showIndexWarning = false; @override void initState() { @@ -29,6 +30,12 @@ class _TantivyFullTextSearchState extends State () async { widget.tab.booksToSearch.value = (await context.read().library).getAllBooks().toSet(); + // Check if index is up to date + final totalBooks = widget.tab.booksToSearch.value.length; + final indexedBooks = TantivyDataProvider.instance.booksDone.length; + setState(() { + _showIndexWarning = totalBooks != indexedBooks; + }); }(); widget.tab.aproximateSearch.addListener(() => updateResults()); widget.tab.booksToSearch.addListener(() => updateResults()); @@ -75,199 +82,226 @@ class _TantivyFullTextSearchState extends State body: ValueListenableBuilder( valueListenable: isLeftPaneOpen, builder: (context, value, child) { - return Row( + return Column( children: [ - !isLeftPaneOpen.value - ? SizedBox.shrink() - : Column( - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(0, 0, 8, 0), - child: IconButton( - icon: const Icon(Icons.menu), - onPressed: () { - isLeftPaneOpen.value = !isLeftPaneOpen.value; - }, - ), - ), - Expanded(child: SizedBox.shrink()), - ], - ), - AnimatedSize( - duration: const Duration(milliseconds: 300), - child: SizedBox( - width: isLeftPaneOpen.value ? 350 : 0, - child: FullTextLeftPane(tab: widget.tab), - )), - NotificationListener( - onNotification: (scrollNotification) { - Future.microtask(() { - isLeftPaneOpen.value = false; - }); - return false; // Don't block the notification - }, - child: Expanded( - child: Column( - children: [ - Row( - children: [ - isLeftPaneOpen.value - ? SizedBox.shrink() - : Padding( - padding: - const EdgeInsets.fromLTRB(0, 0, 8, 0), - child: IconButton( - icon: const Icon(Icons.menu), - onPressed: () { - isLeftPaneOpen.value = - !isLeftPaneOpen.value; - }, - ), - ), - Expanded( - child: Padding( - padding: - const EdgeInsets.fromLTRB(60, 5, 60, 10), - child: TextField( - autofocus: true, - controller: widget.tab.queryController, - onChanged: (e) => updateResults(), - decoration: InputDecoration( - hintText: "חפש כאן..", - suffixIcon: IconButton( - icon: const Icon(Icons.clear), - onPressed: () { - widget.tab.queryController.clear(); - updateResults(); - }, - ), + if (_showIndexWarning) + Container( + width: double.infinity, + color: Colors.orange[100], + padding: const EdgeInsets.all(8.0), + child: const Text( + 'אינדקס החיפוש אינו מעודכן. חלק מהספרים עלולים להיות חסרים בתוצאות החיפוש. ניתן לעדכן אותו בתפריט הצד', + textAlign: TextAlign.center, + style: TextStyle(color: Colors.black87), + ), + ), + Expanded( + child: Row( + children: [ + !isLeftPaneOpen.value + ? SizedBox.shrink() + : Column( + children: [ + Padding( + padding: + const EdgeInsets.fromLTRB(0, 0, 8, 0), + child: IconButton( + icon: const Icon(Icons.menu), + onPressed: () { + isLeftPaneOpen.value = + !isLeftPaneOpen.value; + }, ), ), - ), + Expanded(child: SizedBox.shrink()), + ], ), - ], - ), - Expanded( - child: FutureBuilder( - future: widget.tab.results, - builder: (context, snapshot) { - if (snapshot.connectionState == - ConnectionState.waiting) { - return const Center( - child: CircularProgressIndicator()); - } - if (snapshot.hasError) { - return Center( - child: Text('Error: ${snapshot.error}')); - } - if (snapshot.data!.isEmpty) { - return const Center( - child: Text('אין תוצאות')); - } - return Column( - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: Center( - child: Text( - '${snapshot.data!.length} תוצאות', + AnimatedSize( + duration: const Duration(milliseconds: 300), + child: SizedBox( + width: isLeftPaneOpen.value ? 350 : 0, + child: FullTextLeftPane(tab: widget.tab), + )), + NotificationListener( + onNotification: (scrollNotification) { + Future.microtask(() { + isLeftPaneOpen.value = false; + }); + return false; // Don't block the notification + }, + child: Expanded( + child: Column( + children: [ + Row( + children: [ + isLeftPaneOpen.value + ? SizedBox.shrink() + : Padding( + padding: const EdgeInsets.fromLTRB( + 0, 0, 8, 0), + child: IconButton( + icon: const Icon(Icons.menu), + onPressed: () { + isLeftPaneOpen.value = + !isLeftPaneOpen.value; + }, + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.fromLTRB( + 60, 5, 60, 10), + child: TextField( + autofocus: true, + controller: widget.tab.queryController, + onChanged: (e) => updateResults(), + decoration: InputDecoration( + hintText: "חפש כאן..", + suffixIcon: IconButton( + icon: const Icon(Icons.clear), + onPressed: () { + widget.tab.queryController + .clear(); + updateResults(); + }, + ), ), ), ), - Expanded( - child: ListView.builder( - shrinkWrap: true, - itemCount: snapshot.data!.length, - itemBuilder: (context, index) { - return ListTile( - onTap: () { - if (snapshot.data![index].isPdf) { - context - .read() - .openTab( - PdfBookTab( - searchText: widget - .tab - .queryController - .text, - PdfBook( - title: snapshot - .data![ - index] - .title, - path: snapshot - .data![ - index] - .filePath), - snapshot + ), + ], + ), + Expanded( + child: FutureBuilder( + future: widget.tab.results, + builder: (context, snapshot) { + if (snapshot.connectionState == + ConnectionState.waiting) { + return const Center( + child: CircularProgressIndicator()); + } + if (snapshot.hasError) { + return Center( + child: Text( + 'Error: ${snapshot.error}')); + } + if (snapshot.data!.isEmpty) { + return const Center( + child: Text('אין תוצאות')); + } + return Column( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Center( + child: Text( + '${snapshot.data!.length} תוצאות', + ), + ), + ), + Expanded( + child: ListView.builder( + shrinkWrap: true, + itemCount: snapshot.data!.length, + itemBuilder: (context, index) { + return ListTile( + onTap: () { + if (snapshot + .data![index].isPdf) { + context.read().openTab( + PdfBookTab( + searchText: widget + .tab + .queryController + .text, + PdfBook( + title: snapshot + .data![ + index] + .title, + path: snapshot + .data![ + index] + .filePath), + snapshot + .data![ + index] + .segment + .toInt() + + 1), + index: snapshot + .data![index] + .segment + .toInt() + + 1); + } else { + context + .read() + .openTab( + TextBookTab( + book: TextBook( + title: snapshot + .data![ + index] + .title, + ), + index: snapshot .data![ index] .segment - .toInt() + - 1), - index: snapshot - .data![index] - .segment - .toInt() + - 1); - } else { - context - .read() - .openTab( - TextBookTab( - book: TextBook( - title: snapshot - .data![index] - .title, - ), - index: snapshot + .toInt(), + searchText: widget + .tab + .queryController + .text), + ); + } + }, + title: snapshot + .data![index].isPdf + ? Text(snapshot .data![index] - .segment - .toInt(), - searchText: widget - .tab - .queryController - .text), - ); - } - }, - title: snapshot.data![index].isPdf - ? Text(snapshot - .data![index].title + - ' עמוד ${snapshot.data![index].segment.toInt() + 1}') - : FutureBuilder( - future: refFromIndex( - snapshot.data![index] - .segment - .toInt(), - TextBook( - title: snapshot - .data![ - index] - .title) - .tableOfContents), - builder: (context, ref) { - if (!ref.hasData) { - return Text( - '[תוצאה ${index + 1}] ${snapshot.data![index].title} ...'); - } - return Text( - '[תוצאה ${index + 1}] ${ref.data!}', - ); - }), - subtitle: Html( - data: snapshot.data![index].text, + .title + + ' עמוד ${snapshot.data![index].segment.toInt() + 1}') + : FutureBuilder( + future: refFromIndex( + snapshot + .data![index] + .segment + .toInt(), + TextBook( + title: snapshot + .data![ + index] + .title) + .tableOfContents), + builder: + (context, ref) { + if (!ref.hasData) { + return Text( + '[תוצאה ${index + 1}] ${snapshot.data![index].title} ...'); + } + return Text( + '[תוצאה ${index + 1}] ${ref.data!}', + ); + }), + subtitle: Html( + data: snapshot + .data![index].text, + ), + ); + }, ), - ); - }, - ), - ), - ], - ); - }), - ) - ], - ), + ), + ], + ); + }), + ) + ], + ), + ), + ), + ], ), ), ],