diff --git a/lib/models/app_model.dart b/lib/models/app_model.dart index 891d1c63..8d38bd1b 100644 --- a/lib/models/app_model.dart +++ b/lib/models/app_model.dart @@ -371,8 +371,11 @@ class AppModel with ChangeNotifier { .map((t) => Bookmark( ref: '', book: t is PdfBookTab ? (t).book : (t as TextBookTab).book, - index: - t is PdfBookTab ? (t).pageNumber : (t as TextBookTab).index, + index: t is PdfBookTab + ? (t).pdfViewerController.isReady + ? (t).pdfViewerController.pageNumber! + : 1 + : (t as TextBookTab).index, commentatorsToShow: t is TextBookTab ? t.commentatorsToShow.value : [], )) diff --git a/lib/screens/library_browser.dart b/lib/screens/library_browser.dart index 4ef23ace..d320aa57 100644 --- a/lib/screens/library_browser.dart +++ b/lib/screens/library_browser.dart @@ -86,7 +86,7 @@ class _LibraryBrowserState extends State ), DafYomi( onDafYomiTap: (tractate, daf) { - openDafYomiBook(context, tractate, daf); + openDafYomiBook(context, tractate, ' $daf.'); }, ) ], diff --git a/lib/screens/reading_screen.dart b/lib/screens/reading_screen.dart index 7c14e08a..6d4c8270 100644 --- a/lib/screens/reading_screen.dart +++ b/lib/screens/reading_screen.dart @@ -81,105 +81,108 @@ class _ReadingScreenState extends State try { return Scaffold( appBar: AppBar( - title: TabBar( - controller: controller, - isScrollable: true, - tabAlignment: TabAlignment.center, - tabs: appModel.tabs - .map((tab) => Listener( - // close tab on middle mouse button click - onPointerDown: (PointerDownEvent event) { - if (event.buttons == 4) { - appModel.closeTab(tab); - } - }, - child: ContextMenuRegion( - contextMenu: ContextMenu( - entries: [ - MenuItem( - label: 'סגור', - onSelected: () => - appModel.closeTab(tab), - ), - MenuItem( - label: 'סגור הכל', - onSelected: () => - appModel.closeAllTabs(), - ), - MenuItem( - label: 'סגור את האחרים', - onSelected: () => - appModel.closeOthers(tab), - ), - MenuItem( - label: 'שיכפול', - onSelected: () => - appModel.cloneTab(tab), - ), - MenuItem.submenu( - label: 'רשימת הכרטיסיות ', - items: getMenuItems( - appModel.tabs, appModel), - ) - ], - ), - child: Draggable( - axis: Axis.horizontal, - data: tab, - childWhenDragging: SizedBox.fromSize( - size: const Size.fromWidth(2)), - feedback: Container( - decoration: const BoxDecoration( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(10), - topRight: Radius.circular(10)), - color: Colors.white), - child: Padding( - padding: const EdgeInsets.fromLTRB( - 20, 10, 20, 15), - child: Text( - tab is SearchingTab - ? '${tab.title}: ${tab.searcher.queryController.text}' - : tab.title, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w600, - decoration: TextDecoration.none, - color: - Theme.of(context).primaryColor, - ), + title: Container( + constraints: BoxConstraints(maxHeight: 50), + child: TabBar( + controller: controller, + isScrollable: true, + tabAlignment: TabAlignment.center, + tabs: appModel.tabs + .map((tab) => Listener( + // close tab on middle mouse button click + onPointerDown: (PointerDownEvent event) { + if (event.buttons == 4) { + appModel.closeTab(tab); + } + }, + child: ContextMenuRegion( + contextMenu: ContextMenu( + entries: [ + MenuItem( + label: 'סגור', + onSelected: () => + appModel.closeTab(tab), ), - ), + MenuItem( + label: 'סגור הכל', + onSelected: () => + appModel.closeAllTabs(), + ), + MenuItem( + label: 'סגור את האחרים', + onSelected: () => + appModel.closeOthers(tab), + ), + MenuItem( + label: 'שיכפול', + onSelected: () => + appModel.cloneTab(tab), + ), + MenuItem.submenu( + label: 'רשימת הכרטיסיות ', + items: getMenuItems( + appModel.tabs, appModel), + ) + ], ), - child: DragTarget( - onAcceptWithDetails: (draggedTab) { - if (draggedTab.data == tab) return; - appModel.moveTab(draggedTab.data, - appModel.tabs.indexOf(tab)); - setState(() {}); - }, - builder: (context, candidateData, - rejectedData) => - Tab( - child: Row(children: [ - Text( + child: Draggable( + axis: Axis.horizontal, + data: tab, + childWhenDragging: SizedBox.fromSize( + size: const Size(0, 0)), + feedback: Container( + decoration: const BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10), + topRight: Radius.circular(10)), + color: Colors.white), + child: Padding( + padding: const EdgeInsets.fromLTRB( + 20, 10, 20, 15), + child: Text( tab is SearchingTab ? '${tab.title}: ${tab.searcher.queryController.text}' : tab.title, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + decoration: TextDecoration.none, + color: Theme.of(context) + .primaryColor, + ), ), - IconButton( - onPressed: () { - appModel.closeTab(tab); - }, - icon: const Icon(Icons.close, - size: 10)) - ]), + ), + ), + child: DragTarget( + onAcceptWithDetails: (draggedTab) { + if (draggedTab.data == tab) return; + appModel.moveTab(draggedTab.data, + appModel.tabs.indexOf(tab)); + setState(() {}); + }, + builder: (context, candidateData, + rejectedData) => + Tab( + child: Row(children: [ + Text( + tab is SearchingTab + ? '${tab.title}: ${tab.searcher.queryController.text}' + : tab.title, + ), + IconButton( + onPressed: () { + appModel.closeTab(tab); + }, + icon: const Icon(Icons.close, + size: 10)) + ]), + ), ), ), ), - ), - )) - .toList(), + )) + .toList(), + ), ), leading: IconButton( icon: const Icon(Icons.add_to_queue), @@ -196,14 +199,14 @@ class _ReadingScreenState extends State controller: _textFieldController, decoration: const InputDecoration(), ), - actions: [ + actions: [ TextButton( child: const Text('ביטול'), onPressed: () { Navigator.pop(context, false); }, ), - TextButton( + TextButton( child: const Text('אישור'), onPressed: () { Navigator.pop(context, true); diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index b479d191..93644134 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -4,6 +4,8 @@ import 'package:file_picker/file_picker.dart'; import 'dart:io'; import 'package:otzaria/models/app_model.dart'; import 'package:provider/provider.dart'; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:package_info_plus/package_info_plus.dart'; class MySettingsScreen extends StatefulWidget { const MySettingsScreen({ @@ -245,7 +247,26 @@ class _MySettingsScreenState extends State { 'קבלת עדכונים על גרסאות בדיקה, ייתכנו באגים וחוסר יציבות', disabledLabel: 'קבלת עדכונים על גרסאות יציבות בלבד', leading: Icon(Icons.bug_report), - ) + ), + FutureBuilder( + future: PackageInfo.fromPlatform(), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return SimpleSettingsTile( + title: 'גרסה נוכחית', + subtitle: 'המתן..', + leading: Icon(Icons.info_rounded), + ); + } + return Align( + alignment: Alignment.centerRight, + child: SimpleSettingsTile( + title: 'גרסה נוכחית', + subtitle: snapshot.data!.version, + leading: Icon(Icons.info_rounded), + ), + ); + }) ], ) ], diff --git a/lib/screens/text_book_screen.dart b/lib/screens/text_book_screen.dart index f74d2711..b1098d63 100644 --- a/lib/screens/text_book_screen.dart +++ b/lib/screens/text_book_screen.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:flutter_context_menu/flutter_context_menu.dart'; import 'package:otzaria/models/app_model.dart'; import 'package:otzaria/screens/combined_book_screen.dart'; import 'package:otzaria/screens/printing_screen.dart'; import 'package:otzaria/screens/splited_view_screen.dart'; +import 'package:otzaria/utils/daf_yomi_helper.dart'; import 'package:provider/provider.dart'; import 'package:otzaria/screens/text_book_search_screen.dart'; import 'dart:io'; @@ -53,7 +55,6 @@ class TextBookViewer extends StatefulWidget { class _TextBookViewerState extends State with TickerProviderStateMixin { final FocusNode textSearchFocusNode = FocusNode(); - late TabController tabController; @override @@ -80,8 +81,20 @@ class _TextBookViewerState extends State builder: (context, snapshot) => snapshot.hasData ? Center( child: SelectionArea( - child: Text(snapshot.data!, - style: const TextStyle(fontSize: 17)), + child: ContextMenuRegion( + contextMenu: ContextMenu(entries: [ + MenuItem( + label: 'פתח קובץ PDF מקביל', + onSelected: () { + openPdfBookFromRef( + snapshot.data!.split(',').first, + snapshot.data!.split(',')[1], + context); + }), + ]), + child: Text(snapshot.data!, + style: const TextStyle(fontSize: 17)), + ), ), ) : const SizedBox.shrink(), @@ -307,6 +320,7 @@ class _TextBookViewerState extends State LogicalKeyboardKey.keyF): () { widget.tab.showLeftPane.value = true; tabController.index = 1; + textSearchFocusNode.requestFocus(); }, }, child: Focus( @@ -421,7 +435,14 @@ class _TextBookViewerState extends State controller: tabController, children: [ buildTocViewer(), - buildSearchView(), + CallbackShortcuts(bindings: { + LogicalKeySet(LogicalKeyboardKey.control, + LogicalKeyboardKey.keyF): () { + widget.tab.showLeftPane.value = true; + tabController.index = 1; + textSearchFocusNode.requestFocus(); + }, + }, child: buildSearchView()), buildCommentaryView(), buildLinkView(), ], diff --git a/lib/utils/daf_yomi_helper.dart b/lib/utils/daf_yomi_helper.dart index c37a14e3..743b814e 100644 --- a/lib/utils/daf_yomi_helper.dart +++ b/lib/utils/daf_yomi_helper.dart @@ -25,7 +25,7 @@ Future _findDafInToc(TextBook book, String daf) async { TocEntry? findDafInEntries(List entries) { for (var entry in entries) { String ref = entry.text; - if (ref.contains('דף $daf.')) { + if (ref.contains('דף $daf')) { return entry; } // Recursively search in children @@ -41,12 +41,13 @@ Future _findDafInToc(TextBook book, String daf) async { } Future getDafYomiOutline(PdfBook book, String daf) async { + daf = daf.replaceAll('.', ', א').replaceAll(':', ', ב').replaceAll('דף ', ''); final outlines = await PdfDocument.openFile(book.path) .then((value) => value.loadOutline()); PdfOutlineNode? findDafInEntries(List entries) { for (var entry in entries) { String ref = entry.title; - if (ref.contains(' $daf, א')) { + if (ref == '${book.title}$daf') { return entry; } // Recursively search in children @@ -60,3 +61,19 @@ Future getDafYomiOutline(PdfBook book, String daf) async { return findDafInEntries(outlines); } + +openPdfBookFromRef(String bookname, String ref, BuildContext context) async { + final appModel = Provider.of(context, listen: false); + final book = await appModel.findBookByTitle(bookname); + + if (book != null && book is PdfBook) { + final outline = await getDafYomiOutline(book, ref); + appModel.openBook(book, outline?.dest?.pageNumber ?? 0, openLeftPane: true); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('הספר אינו קיים'), + ), + ); + } +} diff --git a/lib/widgets/my_updat_widget.dart b/lib/widgets/my_updat_widget.dart index 2c87545b..322198ad 100644 --- a/lib/widgets/my_updat_widget.dart +++ b/lib/widgets/my_updat_widget.dart @@ -1,56 +1,63 @@ import 'dart:io'; - import 'package:flutter/material.dart'; import 'package:flutter_settings_screens/flutter_settings_screens.dart'; import 'package:updat/theme/chips/flat.dart'; import 'package:updat/updat_window_manager.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; +import 'package:package_info_plus/package_info_plus.dart'; class MyUpdatWidget extends StatelessWidget { const MyUpdatWidget({Key? key, required this.child}) : super(key: key); final Widget child; @override - Widget build(BuildContext context) => UpdatWindowManager( - getLatestVersion: () async { - // Github gives us a super useful latest endpoint, and we can use it to get the latest stable release - final data = await http.get(Uri.parse( - Settings.getValue('key-dev-channel') ?? false - ? "https://api.github.com/repos/sivan22/otzaria-dev-channel/releases/latest" - : "https://api.github.com/repos/sivan22/otzaria/releases/latest", - )); + Widget build(BuildContext context) => FutureBuilder( + future: PackageInfo.fromPlatform(), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return child; + } + return UpdatWindowManager( + getLatestVersion: () async { + // Github gives us a super useful latest endpoint, and we can use it to get the latest stable release + final data = await http.get(Uri.parse( + Settings.getValue('key-dev-channel') ?? false + ? "https://api.github.com/repos/sivan22/otzaria-dev-channel/releases/latest" + : "https://api.github.com/repos/sivan22/otzaria/releases/latest", + )); - // Return the tag name, which is always a semantically versioned string. - return jsonDecode(data.body)["tag_name"]; - }, - getBinaryUrl: (version) async { - // Github also gives us a great way to download the binary for a certain release (as long as we use a consistent naming scheme) + // Return the tag name, which is always a semantically versioned string. + return jsonDecode(data.body)["tag_name"]; + }, + getBinaryUrl: (version) async { + // Github also gives us a great way to download the binary for a certain release (as long as we use a consistent naming scheme) - // Make sure that this link includes the platform extension with which to save your binary. - // If you use https://exapmle.com/latest/macos for instance then you need to create your own file using `getDownloadFileLocation` + // Make sure that this link includes the platform extension with which to save your binary. + // If you use https://exapmle.com/latest/macos for instance then you need to create your own file using `getDownloadFileLocation` - final repo = Settings.getValue('key-dev-channel') ?? false - ? "otzaria-dev-channel" - : "otzaria"; - return "https://github.com/sivan22/$repo/releases/download/$version/otzaria-$version-${Platform.operatingSystem}.$platformExt"; - }, - appName: "otzaria", // This is used to name the downloaded files. - getChangelog: (_, __) async { - // That same latest endpoint gives us access to a markdown-flavored release body. Perfect! - final repo = Settings.getValue('key-dev-channel') ?? false - ? "otzaria-dev-channel" - : "otzaria"; - final data = await http.get(Uri.parse( - "https://api.github.com/repos/sivan22/$repo/releases/latest", - )); - return jsonDecode(data.body)["body"]; - }, - currentVersion: '0.1.8-dev.5', - updateChipBuilder: flatChip, + final repo = Settings.getValue('key-dev-channel') ?? false + ? "otzaria-dev-channel" + : "otzaria"; + return "https://github.com/sivan22/$repo/releases/download/$version/otzaria-$version-${Platform.operatingSystem}.$platformExt"; + }, + appName: "otzaria", // This is used to name the downloaded files. + getChangelog: (_, __) async { + // That same latest endpoint gives us access to a markdown-flavored release body. Perfect! + final repo = Settings.getValue('key-dev-channel') ?? false + ? "otzaria-dev-channel" + : "otzaria"; + final data = await http.get(Uri.parse( + "https://api.github.com/repos/sivan22/$repo/releases/latest", + )); + return jsonDecode(data.body)["body"]; + }, + currentVersion: snapshot.data!.version, + updateChipBuilder: flatChip, - callback: (status) {}, - child: child, - ); + callback: (status) {}, + child: child, + ); + }); String get platformExt { switch (Platform.operatingSystem) { diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index ce698904..8e44af2a 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,6 +6,7 @@ import FlutterMacOS import Foundation import isar_flutter_libs +import package_info_plus import path_provider_foundation import printing import screen_retriever @@ -15,6 +16,7 @@ import window_manager func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { IsarFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "IsarFlutterLibsPlugin")) + FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PrintingPlugin.register(with: registry.registrar(forPlugin: "PrintingPlugin")) ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin")) diff --git a/packages/filter_list-1.0.3/lib/src/filter_list_widget.dart b/packages/filter_list-1.0.3/lib/src/filter_list_widget.dart index d3421f08..f051cc59 100644 --- a/packages/filter_list-1.0.3/lib/src/filter_list_widget.dart +++ b/packages/filter_list-1.0.3/lib/src/filter_list_widget.dart @@ -178,7 +178,7 @@ class FilterListWidget extends StatelessWidget { padding: const EdgeInsets.all(8.0), child: ConstrainedBox( constraints: BoxConstraints( - maxHeight: MediaQuery.of(context).size.height * 0.3, + maxHeight: MediaQuery.of(context).size.height * 0.2, ), child: ChoiceList( choiceChipBuilder: choiceChipBuilder, diff --git a/pubspec.lock b/pubspec.lock index 1a8299a1..1f79efd7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -588,6 +588,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" + package_info_plus: + dependency: "direct main" + description: + name: package_info_plus + sha256: a75164ade98cb7d24cfd0a13c6408927c6b217fa60dee5a7ff5c116a58f28918 + url: "https://pub.dev" + source: hosted + version: "8.0.2" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: ac1f4a4847f1ade8e6a87d1f39f5d7c67490738642e2542f559ec38c37489a66 + url: "https://pub.dev" + source: hosted + version: "3.0.1" path: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 0cc51c4e..2644efb5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,7 +8,7 @@ msix_config: display_name: אוצריא publisher_display_name: sivan22 identity_name: sivan22.Otzaria - msix_version: 0.1.8.5 + msix_version: 0.1.8.6 logo_path: assets/icon/icon.png publisher: CN=sivan22, O=sivan22, C=IL certificate_path: C:\dev\sivan22.pfx @@ -33,7 +33,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.8-dev.5 +version: 0.1.8-beta.1 environment: sdk: ">=3.2.6 <4.0.0" @@ -77,6 +77,7 @@ dependencies: csv: ^6.0.0 filter_list: path: packages/filter_list-1.0.3 + package_info_plus: ^8.0.2 dependency_overrides: # it forces the version of the intl package to be 0.19.0 across all dependencies, even if some packages specify a different compatible version. intl: ^0.19.0