diff --git a/app/build.yaml b/app/build.yaml index 5965b238..50a3e5b4 100644 --- a/app/build.yaml +++ b/app/build.yaml @@ -15,6 +15,7 @@ targets: lexo_rank_next: "text (int, text null)" lexo_rank_previous: "text (int, text null)" lexo_rank_reorder_after: "text (text null, text null)" + lexo_rank_reorder_before: "text (text null, text null)" modules: - json1 - fts5 diff --git a/app/lib/data/database/functions/lexo_rank_functions.dart b/app/lib/data/database/functions/lexo_rank_functions.dart index aecd7522..486289f7 100644 --- a/app/lib/data/database/functions/lexo_rank_functions.dart +++ b/app/lib/data/database/functions/lexo_rank_functions.dart @@ -40,6 +40,19 @@ String _reorderAfter(List args) { } } +String _reorderBefore(List args) { + final first = args[0].mapNotNull((arg) => LexoRank.parse(arg as String)); + final last = args[1].mapNotNull((arg) => LexoRank.parse(arg as String)); + + if (first == null) { + throw Exception('Tab not found'); + } else if (last == null) { + return first.genPrev().value; + } else { + return last.genBetween(first).value; + } +} + void registerLexorankFunctions(CommonDatabase database) { database.createFunction( functionName: 'lexo_rank_next', @@ -56,4 +69,9 @@ void registerLexorankFunctions(CommonDatabase database) { argumentCount: const AllowedArgumentCount(2), function: _reorderAfter, ); + database.createFunction( + functionName: 'lexo_rank_reorder_before', + argumentCount: const AllowedArgumentCount(2), + function: _reorderBefore, + ); } diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/sheets/view_tabs.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/sheets/view_tabs.dart index d4ef7048..6a47d82a 100644 --- a/app/lib/features/geckoview/features/browser/presentation/widgets/sheets/view_tabs.dart +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/sheets/view_tabs.dart @@ -300,6 +300,8 @@ class ViewTabsSheetWidget extends HookConsumerWidget { Expanded( child: HookConsumer( builder: (context, ref, child) { + final gridViewKey = useMemoized(() => GlobalKey()); + final container = ref.watch(selectedContainerProvider); final filteredTabEntities = ref.watch( @@ -387,10 +389,11 @@ class ViewTabsSheetWidget extends HookConsumerWidget { return Padding( padding: const EdgeInsets.symmetric(horizontal: 4.0), - child: ReorderableBuilder( + child: ReorderableBuilder.builder( //Rebuild when cross axis count changes key: ValueKey(crossAxisCount), scrollController: sheetScrollController, + itemCount: tabs.length, onDragStarted: (index) { ref.read(willAcceptDropProvider.notifier).clear(); }, @@ -423,19 +426,27 @@ class ViewTabsSheetWidget extends HookConsumerWidget { containerId, ); } else { - final orderAfterIndex = newIndex; - key = await containerRepository.getOrderKeyAfterTab( - filteredTabEntities.value[orderAfterIndex].tabId, - containerId, - ); + if (newIndex < oldIndex) { + key = await containerRepository.getOrderKeyAfterTab( + filteredTabEntities.value[newIndex - 1].tabId, + containerId, + ); + } else { + key = await containerRepository + .getOrderKeyBeforeTab( + filteredTabEntities.value[newIndex + 1].tabId, + containerId, + ); + } } await ref .read(tabDataRepositoryProvider.notifier) .assignOrderKey(tabId, key); }, - builder: (children) { + childBuilder: (itemBuilder) { return GridView.builder( + key: gridViewKey, controller: sheetScrollController, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( //Sync values for itemHeight calculation _calculateItemHeight @@ -444,11 +455,11 @@ class ViewTabsSheetWidget extends HookConsumerWidget { crossAxisSpacing: 8.0, crossAxisCount: crossAxisCount, ), - itemCount: children.length, - itemBuilder: (context, index) => children[index], + itemCount: tabs.length, + itemBuilder: (context, index) => + itemBuilder(tabs[index], index), ); }, - children: tabs, ), ); }, diff --git a/app/lib/features/geckoview/features/tabs/data/database/daos/container.dart b/app/lib/features/geckoview/features/tabs/data/database/daos/container.dart index 725bbf12..200036f6 100644 --- a/app/lib/features/geckoview/features/tabs/data/database/daos/container.dart +++ b/app/lib/features/geckoview/features/tabs/data/database/daos/container.dart @@ -96,4 +96,11 @@ class ContainerDao extends DatabaseAccessor ) { return db.orderKeyAfterTab(containerId: containerId, tabId: tabId); } + + SingleSelectable generateOrderKeyBeforeTabId( + String? containerId, + String tabId, + ) { + return db.orderKeyBeforeTab(containerId: containerId, tabId: tabId); + } } diff --git a/app/lib/features/geckoview/features/tabs/data/database/database.drift b/app/lib/features/geckoview/features/tabs/data/database/database.drift index 634228ba..c7cffa69 100644 --- a/app/lib/features/geckoview/features/tabs/data/database/database.drift +++ b/app/lib/features/geckoview/features/tabs/data/database/database.drift @@ -118,6 +118,18 @@ orderKeyAfterTab(:tab_id AS TEXT, :container_id AS TEXT OR NULL): FROM ordered_table WHERE id = :tab_id; +orderKeyBeforeTab(:tab_id AS TEXT, :container_id AS TEXT OR NULL): + WITH ordered_table AS ( + SELECT id, + order_key, + LAG(order_key) OVER (ORDER BY order_key) AS prev_order_key + FROM tab + WHERE container_id IS :container_id + ) + SELECT lexo_rank_reorder_before(order_key, prev_order_key) + FROM ordered_table + WHERE id = :tab_id; + queryTabsBasic WITH TabQueryResult: WITH weights AS ( SELECT diff --git a/app/lib/features/geckoview/features/tabs/data/database/database.g.dart b/app/lib/features/geckoview/features/tabs/data/database/database.g.dart index 192eaf91..d8bd4bdd 100644 --- a/app/lib/features/geckoview/features/tabs/data/database/database.g.dart +++ b/app/lib/features/geckoview/features/tabs/data/database/database.g.dart @@ -1165,6 +1165,17 @@ abstract class _$TabDatabase extends GeneratedDatabase { ).map((QueryRow row) => row.read('_c0')); } + Selectable orderKeyBeforeTab({ + String? containerId, + required String tabId, + }) { + return customSelect( + 'WITH ordered_table AS (SELECT id, order_key, LAG(order_key)OVER (ORDER BY order_key RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE NO OTHERS) AS prev_order_key FROM tab WHERE container_id IS ?1) SELECT lexo_rank_reorder_before(order_key, prev_order_key) AS _c0 FROM ordered_table WHERE id = ?2', + variables: [Variable(containerId), Variable(tabId)], + readsFrom: {tab}, + ).map((QueryRow row) => row.read('_c0')); + } + Selectable queryTabsBasic({required String query}) { return customSelect( 'WITH weights AS (SELECT 10.0 AS title_weight, 5.0 AS url_weight) SELECT t.id, t.title, CAST(t.url AS TEXT) AS url, t.url AS clean_url, bm25(tab_fts, weights.title_weight, weights.url_weight) AS weighted_rank FROM tab_fts AS fts INNER JOIN tab AS t ON t."rowid" = fts."rowid" CROSS JOIN weights WHERE fts.title LIKE ?1 OR fts.url LIKE ?1 ORDER BY weighted_rank ASC, t.timestamp DESC', diff --git a/app/lib/features/geckoview/features/tabs/domain/repositories/container.dart b/app/lib/features/geckoview/features/tabs/domain/repositories/container.dart index 34712058..b164e509 100644 --- a/app/lib/features/geckoview/features/tabs/domain/repositories/container.dart +++ b/app/lib/features/geckoview/features/tabs/domain/repositories/container.dart @@ -83,6 +83,14 @@ class ContainerRepository extends _$ContainerRepository { .getSingle(); } + Future getOrderKeyBeforeTab(String tabId, String? containerId) { + return ref + .read(tabDatabaseProvider) + .containerDao + .generateOrderKeyBeforeTabId(containerId, tabId) + .getSingle(); + } + Future unusedRandomContainerColor() async { final allColors = colorTypes.flattened.toList(); final usedColors = await getDistinctColors(); diff --git a/app/lib/features/geckoview/features/tabs/domain/repositories/container.g.dart b/app/lib/features/geckoview/features/tabs/domain/repositories/container.g.dart index 8c2b982f..8f75b964 100644 --- a/app/lib/features/geckoview/features/tabs/domain/repositories/container.g.dart +++ b/app/lib/features/geckoview/features/tabs/domain/repositories/container.g.dart @@ -7,7 +7,7 @@ part of 'container.dart'; // ************************************************************************** String _$containerRepositoryHash() => - r'5344ecde33c1c80c1fa2bc8383faca181da91593'; + r'af4ccf6cc12eebae2313df6c8f9ca63db842ffe5'; /// See also [ContainerRepository]. @ProviderFor(ContainerRepository) diff --git a/app/pubspec.yaml b/app/pubspec.yaml index ddd53417..ee9954ec 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -24,7 +24,10 @@ dependencies: flutter_material_design_icons: ^1.1.7447 flutter_mozilla_components: path: ../packages/flutter_mozilla_components - flutter_reorderable_grid_view: ^5.5.0 + flutter_reorderable_grid_view: + git: + url: https://github.com/FaFre/flutter-reorderable-grid-view.git + ref: fix_builder_assert flutter_secure_storage: ^10.0.0-beta.4 flutter_slidable: ^4.0.0 flutter_svg: ^2.2.0