diff --git a/TODO.txt b/TODO.txt index e2f8f32de..f40025e6e 100644 --- a/TODO.txt +++ b/TODO.txt @@ -12,7 +12,6 @@ לתקן באג מילים לאורך -תפריט ניווט להפוך לעץ @@ -21,4 +20,5 @@ לתקן לינקים טור להוסיף סימנים מדרש רבה לבדוק חפץ חיים - באר מים חיים -לבדוק בדק הבית \ No newline at end of file +לבדוק בדק הבית +באג רשי נדה diff --git a/app_preferences.hive b/app_preferences.hive index 9b135962f..142d4d23c 100644 Binary files a/app_preferences.hive and b/app_preferences.hive differ diff --git a/lib/combined_book_commentary_view.dart b/lib/combined_book_commentary_view.dart index cc2b5d959..30da48e37 100644 --- a/lib/combined_book_commentary_view.dart +++ b/lib/combined_book_commentary_view.dart @@ -6,6 +6,7 @@ import 'package:flutter_settings_screen_ex/flutter_settings_screen_ex.dart'; import 'links_view.dart'; import 'dart:io'; import 'main_window_view.dart'; +import 'dart:isolate'; class CombinedView extends StatefulWidget { final List data; @@ -19,22 +20,21 @@ class CombinedView extends StatefulWidget { final String searchQuery; final Function(TabWindow) openBookCallback; final String libraryRootPath; - final ValueNotifier allTilesCollapsed; - const CombinedView( - {super.key, - required this.data, - required this.commentariesToShow, - required this.initalIndex, - required this.scrollController, - required this.scrollOffsetController, - required this.itemPositionsListener, - required this.searchQuery, - required this.links, - required this.openBookCallback, - required this.libraryRootPath, - required this.textSize, - required this.allTilesCollapsed}); + const CombinedView({ + super.key, + required this.data, + required this.commentariesToShow, + required this.initalIndex, + required this.scrollController, + required this.scrollOffsetController, + required this.itemPositionsListener, + required this.searchQuery, + required this.links, + required this.openBookCallback, + required this.libraryRootPath, + required this.textSize, + }); @override State createState() => _CombinedViewState(); @@ -44,9 +44,6 @@ class _CombinedViewState extends State with AutomaticKeepAliveClientMixin { FocusNode focusNode = FocusNode(); - late List combinedData; - bool isExpanded = false; - @override Widget build(BuildContext context) { super.build(context); @@ -73,129 +70,81 @@ class _CombinedViewState extends State child: buildSelectionArea()); } - SelectionArea buildSelectionArea() { - return SelectionArea( - child: ScrollablePositionedList.builder( - initialScrollIndex: widget.initalIndex, - itemPositionsListener: widget.itemPositionsListener, - itemScrollController: widget.scrollController, - scrollOffsetController: widget.scrollOffsetController, - itemCount: widget.data.length, - itemBuilder: (context, index) { - return ListenableBuilder( - listenable: widget.commentariesToShow, - builder: (context, child) { - return FutureBuilder( - future: widget.links, - builder: (context, snapshot) { - if (snapshot.hasData) { - List thisLinks = snapshot.data! - .where((link) => - link.index1 == index + 1 && - (link.connectionType == "commentary" || - link.connectionType == "targum") && - widget.commentariesToShow.value - .contains(link.path2.split('\\').last)) - .toList(); - //sort the links by the heref in order of the commentariesToShow list - thisLinks.sort((a, b) => widget - .commentariesToShow.value - .indexOf( - a.path2.split(Platform.pathSeparator).last) - .compareTo(widget.commentariesToShow.value - .indexOf(b.path2 - .split(Platform.pathSeparator) - .last))); - - if (thisLinks.isEmpty) { - return Padding( - padding: const EdgeInsets.fromLTRB(40, 0, 0, 0), - child: Html( - data: highLight( - widget.data[index], widget.searchQuery), - style: { - 'body': Style( - fontSize: FontSize(widget.textSize), - fontFamily: Settings.getValue( - 'key-font-family') ?? - 'candara', - textAlign: TextAlign.justify), - }), - ); - } else { - return ValueListenableBuilder( - valueListenable: widget.allTilesCollapsed, - builder: (context, allTilesCollapsed, child) { - ExpansionTileController controller = - ExpansionTileController(); - return ExpansionTile( - shape: const Border(), - maintainState: true, - controller: controller, - initiallyExpanded: !allTilesCollapsed, - key: PageStorageKey(widget.data[index]), - iconColor: Colors.transparent, - tilePadding: const EdgeInsets.all(0.0), - collapsedIconColor: Colors.transparent, - title: Html( - data: highLight(widget.data[index], - widget.searchQuery), - style: { - 'body': Style( - fontSize: - FontSize(widget.textSize), - fontFamily: Settings.getValue( - 'key-font-family') ?? - 'candara', - textAlign: TextAlign.justify), - }), - children: [ - buildDynamicContent( - thisLinks, controller) - ]); - }); - } - } - // until links are ready, view plain html without commentaries - return Html( - data: highLight( - widget.data[index], widget.searchQuery), - style: { - 'body': Style( - fontSize: FontSize(widget.textSize), - fontFamily: - Settings.getValue('key-font-family') ?? - 'candara', - textAlign: TextAlign.justify), - }); - }); - }); - })); + Widget buildSelectionArea() { + return ScrollablePositionedList.builder( + initialScrollIndex: widget.initalIndex, + itemPositionsListener: widget.itemPositionsListener, + itemScrollController: widget.scrollController, + scrollOffsetController: widget.scrollOffsetController, + itemCount: widget.data.length, + itemBuilder: (context, index) { + ExpansionTileController controller = ExpansionTileController(); + return ExpansionTile( + shape: const Border(), + //maintainState: true, + controller: controller, + key: PageStorageKey(widget.data[index]), + iconColor: Colors.transparent, + tilePadding: const EdgeInsets.all(0.0), + collapsedIconColor: Colors.transparent, + title: Html( + data: highLight(widget.data[index], widget.searchQuery), + style: { + 'body': Style( + fontSize: FontSize(widget.textSize), + fontFamily: + Settings.getValue('key-font-family') ?? 'candara', + textAlign: TextAlign.justify), + }), + children: [buildCommentaryList(index, controller)]); + }); } - Widget buildDynamicContent( - List thisLinks, ExpansionTileController controller) { - return thisLinks.isEmpty - ? const SizedBox.shrink() - : DynamicContent( - fixedHeight: 200.0, - listView: ListView.builder( - key: PageStorageKey(thisLinks[0].heRef), - physics: const ClampingScrollPhysics(), - primary: true, - shrinkWrap: true, - itemCount: thisLinks.length, - itemBuilder: (context, smallindex) => GestureDetector( - onTap: () { - controller.collapse(); - }, - child: ListTile( - title: Text(thisLinks[smallindex].heRef), - subtitle: buildCommentaryContent(thisLinks, smallindex), - ), - ), - ), + Widget buildCommentaryList(index, ExpansionTileController controller) { + //first get all the links + return FutureBuilder( + future: widget.links, + builder: (context, linksSnapshot) { + if (linksSnapshot.hasData) { + //then get the links for this index + Future> thisLinks = getThisLinks( + linksSnapshot.data!, widget.commentariesToShow.value, index); + return FutureBuilder( + future: thisLinks, + builder: (context, thisLinksSnapshot) { + if (thisLinksSnapshot.hasData) { + return thisLinksSnapshot.data!.isEmpty + ? const SizedBox.shrink() + : ListView.builder( + key: PageStorageKey( + thisLinksSnapshot.data![0].heRef), + physics: const ClampingScrollPhysics(), + primary: true, + shrinkWrap: true, + itemCount: thisLinksSnapshot.data!.length, + itemBuilder: (context, smallindex) => + GestureDetector( + onTap: () { + controller.collapse(); + }, + child: ListTile( + title: Text( + thisLinksSnapshot.data![smallindex].heRef), + subtitle: buildCommentaryContent( + thisLinksSnapshot.data!, smallindex), + ), + ), + ); + } + return const Center( + child: CircularProgressIndicator(), + ); + }); + } + return const Center( + child: CircularProgressIndicator(), ); + }); } FutureBuilder buildCommentaryContent( @@ -239,41 +188,32 @@ String highLight(String data, String searchQuery) { return data; } -Future getContent( - String libraryRootPath, String path, int index) async { - path = path.replaceAll('\\', Platform.pathSeparator); - List lines = await File(path).readAsLines(); - String line = lines[index - 1]; - return line; +Future> getThisLinks( + List links, List commentariesToShow, int index) async { + return Isolate.run(() { + List thisLinks = links + .where((link) => + link.index1 == index + 1 && + (link.connectionType == "commentary" || + link.connectionType == "targum") && + commentariesToShow.contains(link.path2.split('\\').last)) + .toList(); + //sort the links by the heref in order of the commentariesToShow list + thisLinks.sort((a, b) => commentariesToShow + .indexOf(a.path2.split(Platform.pathSeparator).last) + .compareTo(commentariesToShow + .indexOf(b.path2.split(Platform.pathSeparator).last))); + + return thisLinks; + }); } -class DynamicContent extends StatefulWidget { - final ListView listView; - final double fixedHeight; - const DynamicContent( - {Key? key, required this.listView, required this.fixedHeight}) - : super(key: key); - - @override - State createState() => _DynamicContentState(); -} - -class _DynamicContentState extends State { - bool isExpanded = true; - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: () { - setState(() { - isExpanded = !isExpanded; - }); - }, - child: AnimatedContainer( - duration: const Duration(milliseconds: 500), - curve: Curves.ease, - height: isExpanded ? null : widget.fixedHeight, - child: widget.listView), - ); - } +Future getContent( + String libraryRootPath, String path, int index) async { + return Isolate.run(() async { + path = path.replaceAll('\\', Platform.pathSeparator); + List lines = await File(path).readAsLines(); + String line = lines[index - 1]; + return line; + }); } diff --git a/lib/pdf_page.dart b/lib/pdf_page.dart index 0b53f3b4f..0dfde6c48 100644 --- a/lib/pdf_page.dart +++ b/lib/pdf_page.dart @@ -203,11 +203,14 @@ class _MyPdfPageState extends State PdfViewerScrollThumb( controller: controller, orientation: ScrollbarOrientation.bottom, - thumbSize: const Size(80, 30), + thumbSize: const Size(80, 5), thumbBuilder: (context, thumbSize, pageNumber, controller) => Container( - color: Colors.red, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(3), + ), ), ), ], diff --git a/lib/text_book_view.dart b/lib/text_book_view.dart index 27ea10a05..a2181ec15 100644 --- a/lib/text_book_view.dart +++ b/lib/text_book_view.dart @@ -116,7 +116,6 @@ class _TextBookViewerState extends State openBookCallback: widget.openBookCallback, libraryRootPath: widget.file.path.split('אוצריא').first, - allTilesCollapsed: allTilesCollapsed, ), ))))); } @@ -194,7 +193,7 @@ class _TextBookViewerState extends State scrollController: widget.tab.scrollController, closeLeftPaneCallback: closeLeftPane, ) - : const CircularProgressIndicator()); + : const Center(child: CircularProgressIndicator())); } LinksViewer buildLinkView() { diff --git a/lib/toc_viewer.dart b/lib/toc_viewer.dart index 34fa6fe12..6a5372d4e 100644 --- a/lib/toc_viewer.dart +++ b/lib/toc_viewer.dart @@ -1,14 +1,10 @@ -// a widget that takes an html strings array, finds all the headings, and displays it in a listview. on pressed the scrollcontroller scrolls to the index of the heading. - import 'dart:io'; - import 'package:flutter/material.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; class TocViewer extends StatefulWidget { final String data; final ItemScrollController scrollController; - late List toc; final void Function() closeLeftPaneCallback; TocViewer({ @@ -16,17 +12,7 @@ class TocViewer extends StatefulWidget { required this.data, required this.scrollController, required this.closeLeftPaneCallback, - }) { - final List data = this.data.split('\n'); - toc = []; - int index = 0; - for (String line in data) { - if (line.startsWith(' createState() => _TocViewerState(); @@ -34,34 +20,107 @@ class TocViewer extends StatefulWidget { class _TocViewerState extends State with AutomaticKeepAliveClientMixin { + late List _toc; + + @override + void initState() { + super.initState(); + _toc = _parseToc(widget.data); + } + + List _parseToc(String data) { + List lines = data.split('\n'); + List toc = []; + Map parents = {}; // Keep track of parent nodes + + for (int i = 0; i < lines.length; i++) { + final String line = lines[i]; + if (line.startsWith(' ListTile( - title: Text((stripHtmlIfNeeded(widget.toc[index].text))), - onTap: () { - widget.scrollController.scrollTo( - index: widget.toc[index].index, - duration: const Duration(milliseconds: 250), - curve: Curves.ease); - if (Platform.isAndroid) { - widget.closeLeftPaneCallback(); - } - }, - ), + return ListView( + children: _buildTree(_toc), ); } + List _buildTree(List entries) { + List widgets = []; + for (final TocEntry entry in entries) { + if (entry.children.isEmpty) { + // Leaf node (no children) + widgets.add( + ListTile( + title: Text(entry.text), + onTap: () { + widget.scrollController.scrollTo( + index: entry.index, + duration: const Duration(milliseconds: 250), + curve: Curves.ease, + ); + if (Platform.isAndroid) { + widget.closeLeftPaneCallback(); + } + }, + ), + ); + } else { + // Parent node with children + widgets.add( + ExpansionTile( + title: Text(entry.text), + children: _buildTree(entry.children), // Recursively build children + ), + ); + } + } + return widgets; + } + @override - get wantKeepAlive => true; + bool get wantKeepAlive => true; } class TocEntry { final String text; final int index; - TocEntry({required this.text, required this.index}); + final int level; + List children = []; + + TocEntry({ + required this.text, + required this.index, + this.level = 1, + }); } String stripHtmlIfNeeded(String text) { diff --git a/pubspec.yaml b/pubspec.yaml index 365f3f7ac..08855a588 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,7 +8,7 @@ msix_config: display_name: otzaria publisher_display_name: sivan22 identity_name: sivan22.Otzaria - msix_version: 0.0.1.2 + msix_version: 0.0.1.3 logo_path: windows/runner/resources/app_icon.ico # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 @@ -22,7 +22,7 @@ msix_config: # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 0.1.2+0 +version: 0.1.3+0 environment: sdk: ">=3.2.6 <4.0.0" diff --git "a/\327\220\327\225\327\246\327\250\327\231\327\220/\327\252\327\234\327\236\327\225\327\223/\327\221\327\221\327\234\327\231/\327\244\327\231\327\250\327\225\327\251\327\231\327\235 \327\236\327\225\327\223\327\250\327\240\327\231\327\231\327\235 \327\242\327\234 \327\224\327\252\327\234\327\236\327\225\327\223/\327\221\327\231\327\220\327\225\327\250 \327\227\327\221\327\250\327\225\327\252\327\220/\327\227\327\221\327\250\327\225\327\252\327\220 \327\242\327\234 \327\221\327\250\327\233\327\225\327\252" "b/\327\220\327\225\327\246\327\250\327\231\327\220/\327\252\327\234\327\236\327\225\327\223/\327\221\327\221\327\234\327\231/\327\244\327\231\327\250\327\225\327\251\327\231\327\235 \327\236\327\225\327\223\327\250\327\240\327\231\327\231\327\235 \327\242\327\234 \327\224\327\252\327\234\327\236\327\225\327\223/\327\221\327\231\327\220\327\225\327\250 \327\227\327\221\327\250\327\225\327\252\327\220/\327\227\327\221\327\250\327\225\327\252\327\220 \327\242\327\234 \327\221\327\250\327\233\327\225\327\252" index cc1cb9bc4..00e7fbe5c 100644 --- "a/\327\220\327\225\327\246\327\250\327\231\327\220/\327\252\327\234\327\236\327\225\327\223/\327\221\327\221\327\234\327\231/\327\244\327\231\327\250\327\225\327\251\327\231\327\235 \327\236\327\225\327\223\327\250\327\240\327\231\327\231\327\235 \327\242\327\234 \327\224\327\252\327\234\327\236\327\225\327\223/\327\221\327\231\327\220\327\225\327\250 \327\227\327\221\327\250\327\225\327\252\327\220/\327\227\327\221\327\250\327\225\327\252\327\220 \327\242\327\234 \327\221\327\250\327\233\327\225\327\252" +++ "b/\327\220\327\225\327\246\327\250\327\231\327\220/\327\252\327\234\327\236\327\225\327\223/\327\221\327\221\327\234\327\231/\327\244\327\231\327\250\327\225\327\251\327\231\327\235 \327\236\327\225\327\223\327\250\327\240\327\231\327\231\327\235 \327\242\327\234 \327\224\327\252\327\234\327\236\327\225\327\223/\327\221\327\231\327\220\327\225\327\250 \327\227\327\221\327\250\327\225\327\252\327\220/\327\227\327\221\327\250\327\225\327\252\327\220 \327\242\327\234 \327\221\327\250\327\233\327\225\327\252" @@ -565,7 +565,8 @@ ועוד ציווה רבי יהושע בן לוי לבניו: והזהרו בורידין, כששוחטים עוף הזהרו לחתוך את ורידי הדם שבצואר כדי שיצא כל הדם, וכפי שאמר רבי יהודה, דתנן במשנה [חולין כז.]: רבי יהודה אומר: עד שישחוט את הורידין. (62) ועוד ציווה לבניו: "והזהרו בזקן ששכח תלמודו מחמת אונסו, שמחמת מחלה או טורח המזונות שכח את תלמודו הזהרו בו לכבדו למרות ששכח את תלמודו דהרי אמרינן: לוחות ושברי לוחות מונחות בארון כלומר, אף שברי הלוחות נוהגים בהם כבוד ומונחים יחד עם הלוחות השניים, כך יש לנהוג כבוד גם בתלמיד חכם למרות ששכח תלמודו. (63) אמר להו רבא לבריה: כשאתם חותכים בשר, אל תחתכו כשהבשר מונח על גב היד, אלא כשהוא על השלחן. איכא דאמרי שהטעם הוא משום סכנה, שמא יחתוך את ידו, ואיכא דאמרי שהטעם הוא משום קלקול סעודה, שמא יפצע את ידו אף בפצע כל שהוא, והדם היוצא ילכלך את המאכל וימאיס את המאכל על המסובים. (64) -ועוד הורה להם רבא: "אל תשבו על מטה ארמית"! ובפירוש הוראה זו איכא דאמרי, שכוונתו לצוות: לא תגנו בלא קריאת שמע, (65) וכינה את השוכב לישן בלא קריאת שמע כ"מטה ארמית" כי העושה כן דומה מיטתו למיטת גוי. (66) ואיכא דאמרי, שהתכוון לצוות: דלא תנסבו גיורא, שלא תשאו גיורת, (67) ואיכא דאמרי, שהתכוון כפשוטו: ארמית ממש כלומר, שלא ישבו על מיטה השייכת לנכריה, ומשום מעשה דהיה עם רב פפא, דרב פפא אזל לגבי, הלך לביתה של ארמית, הוציאה לו מטה ואמרה לו: שב! אמר לה רב פפא: איני יושב עד שתגביהי את המטה ואראה מה מונח מתחתיה, הגביה את המטה ומצאו שם תינוק מת, וכוונתה הייתה להעליל עליו שהוא התיישב על התינוק והרגו. (68) מכאן אמרו חכמים: אסור לישב על מטה ארמית. (69) +ועוד הורה להם רבא: "אל תשבו על מטה ארמית"! ובפירוש הוראה זו איכא דאמרי, שכוונתו לצוות: לא תגנו בלא קריאת שמע, (65) וכינה את השוכב לישן בלא קריאת שמע כ"מטה ארמית" כי העושה כן דומה מיטתו למיטת גוי. (66) ואיכא דאמרי, שהתכוון לצוות: דלא תנסבו גיורא, שלא תשאו גיורת, (67) ואיכא דאמרי, שהתכוון כפשוטו: ארמית ממש כלומר, שלא ישבו על מיטה השייכת לנכריה, +ומשום מעשה דהיה עם רב פפא, דרב פפא אזל לגבי, הלך לביתה של ארמית, הוציאה לו מטה ואמרה לו: שב! אמר לה רב פפא: איני יושב עד שתגביהי את המטה ואראה מה מונח מתחתיה, הגביה את המטה ומצאו שם תינוק מת, וכוונתה הייתה להעליל עליו שהוא התיישב על התינוק והרגו. (68) מכאן אמרו חכמים: אסור לישב על מטה ארמית. (69) ועוד הורה להם רבא: אל תעברו אחורי בית הכנסת בשעה שהציבור מתפללין, (70) מסייע ליה לרבי יהושע בן לוי, (71) דאמר רבי יהושע בן לוי: אסור לו לאדם שיעבור אחורי בית הכנסת בשעה שהצבור מתפללין. אמר אביי: ולא אמרן שאסור לעבור שם אלא דליכא פתחא אחרינא לבית הכנסת, אבל היכא פתחא אחרינא, לית לן בה, אין לנו בה איסור. (72) ולא אמרן אלא דליכא בי כנישתא אחרינא באזור, אבל איכא בי כנישתא אחרינא, לית לן בה, כי הרואה סבור שהולך להתפלל בבית הכנסת האחר.