improve tab reorder logic

This commit is contained in:
Fabian Freund
2025-07-17 21:59:33 +02:00
parent d41d56cbaf
commit 3c5bd4a8ad
9 changed files with 83 additions and 12 deletions
+1
View File
@@ -15,6 +15,7 @@ targets:
lexo_rank_next: "text (int, text null)" lexo_rank_next: "text (int, text null)"
lexo_rank_previous: "text (int, text null)" lexo_rank_previous: "text (int, text null)"
lexo_rank_reorder_after: "text (text null, text null)" lexo_rank_reorder_after: "text (text null, text null)"
lexo_rank_reorder_before: "text (text null, text null)"
modules: modules:
- json1 - json1
- fts5 - fts5
@@ -40,6 +40,19 @@ String _reorderAfter(List<Object?> args) {
} }
} }
String _reorderBefore(List<Object?> 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) { void registerLexorankFunctions(CommonDatabase database) {
database.createFunction( database.createFunction(
functionName: 'lexo_rank_next', functionName: 'lexo_rank_next',
@@ -56,4 +69,9 @@ void registerLexorankFunctions(CommonDatabase database) {
argumentCount: const AllowedArgumentCount(2), argumentCount: const AllowedArgumentCount(2),
function: _reorderAfter, function: _reorderAfter,
); );
database.createFunction(
functionName: 'lexo_rank_reorder_before',
argumentCount: const AllowedArgumentCount(2),
function: _reorderBefore,
);
} }
@@ -300,6 +300,8 @@ class ViewTabsSheetWidget extends HookConsumerWidget {
Expanded( Expanded(
child: HookConsumer( child: HookConsumer(
builder: (context, ref, child) { builder: (context, ref, child) {
final gridViewKey = useMemoized(() => GlobalKey());
final container = ref.watch(selectedContainerProvider); final container = ref.watch(selectedContainerProvider);
final filteredTabEntities = ref.watch( final filteredTabEntities = ref.watch(
@@ -387,10 +389,11 @@ class ViewTabsSheetWidget extends HookConsumerWidget {
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4.0), padding: const EdgeInsets.symmetric(horizontal: 4.0),
child: ReorderableBuilder( child: ReorderableBuilder.builder(
//Rebuild when cross axis count changes //Rebuild when cross axis count changes
key: ValueKey(crossAxisCount), key: ValueKey(crossAxisCount),
scrollController: sheetScrollController, scrollController: sheetScrollController,
itemCount: tabs.length,
onDragStarted: (index) { onDragStarted: (index) {
ref.read(willAcceptDropProvider.notifier).clear(); ref.read(willAcceptDropProvider.notifier).clear();
}, },
@@ -423,19 +426,27 @@ class ViewTabsSheetWidget extends HookConsumerWidget {
containerId, containerId,
); );
} else { } else {
final orderAfterIndex = newIndex; if (newIndex < oldIndex) {
key = await containerRepository.getOrderKeyAfterTab( key = await containerRepository.getOrderKeyAfterTab(
filteredTabEntities.value[orderAfterIndex].tabId, filteredTabEntities.value[newIndex - 1].tabId,
containerId, containerId,
); );
} else {
key = await containerRepository
.getOrderKeyBeforeTab(
filteredTabEntities.value[newIndex + 1].tabId,
containerId,
);
}
} }
await ref await ref
.read(tabDataRepositoryProvider.notifier) .read(tabDataRepositoryProvider.notifier)
.assignOrderKey(tabId, key); .assignOrderKey(tabId, key);
}, },
builder: (children) { childBuilder: (itemBuilder) {
return GridView.builder( return GridView.builder(
key: gridViewKey,
controller: sheetScrollController, controller: sheetScrollController,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
//Sync values for itemHeight calculation _calculateItemHeight //Sync values for itemHeight calculation _calculateItemHeight
@@ -444,11 +455,11 @@ class ViewTabsSheetWidget extends HookConsumerWidget {
crossAxisSpacing: 8.0, crossAxisSpacing: 8.0,
crossAxisCount: crossAxisCount, crossAxisCount: crossAxisCount,
), ),
itemCount: children.length, itemCount: tabs.length,
itemBuilder: (context, index) => children[index], itemBuilder: (context, index) =>
itemBuilder(tabs[index], index),
); );
}, },
children: tabs,
), ),
); );
}, },
@@ -96,4 +96,11 @@ class ContainerDao extends DatabaseAccessor<TabDatabase>
) { ) {
return db.orderKeyAfterTab(containerId: containerId, tabId: tabId); return db.orderKeyAfterTab(containerId: containerId, tabId: tabId);
} }
SingleSelectable<String> generateOrderKeyBeforeTabId(
String? containerId,
String tabId,
) {
return db.orderKeyBeforeTab(containerId: containerId, tabId: tabId);
}
} }
@@ -118,6 +118,18 @@ orderKeyAfterTab(:tab_id AS TEXT, :container_id AS TEXT OR NULL):
FROM ordered_table FROM ordered_table
WHERE id = :tab_id; 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: queryTabsBasic WITH TabQueryResult:
WITH weights AS ( WITH weights AS (
SELECT SELECT
@@ -1165,6 +1165,17 @@ abstract class _$TabDatabase extends GeneratedDatabase {
).map((QueryRow row) => row.read<String>('_c0')); ).map((QueryRow row) => row.read<String>('_c0'));
} }
Selectable<String> 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<String>(containerId), Variable<String>(tabId)],
readsFrom: {tab},
).map((QueryRow row) => row.read<String>('_c0'));
}
Selectable<TabQueryResult> queryTabsBasic({required String query}) { Selectable<TabQueryResult> queryTabsBasic({required String query}) {
return customSelect( 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', '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',
@@ -83,6 +83,14 @@ class ContainerRepository extends _$ContainerRepository {
.getSingle(); .getSingle();
} }
Future<String> getOrderKeyBeforeTab(String tabId, String? containerId) {
return ref
.read(tabDatabaseProvider)
.containerDao
.generateOrderKeyBeforeTabId(containerId, tabId)
.getSingle();
}
Future<Color> unusedRandomContainerColor() async { Future<Color> unusedRandomContainerColor() async {
final allColors = colorTypes.flattened.toList(); final allColors = colorTypes.flattened.toList();
final usedColors = await getDistinctColors(); final usedColors = await getDistinctColors();
@@ -7,7 +7,7 @@ part of 'container.dart';
// ************************************************************************** // **************************************************************************
String _$containerRepositoryHash() => String _$containerRepositoryHash() =>
r'5344ecde33c1c80c1fa2bc8383faca181da91593'; r'af4ccf6cc12eebae2313df6c8f9ca63db842ffe5';
/// See also [ContainerRepository]. /// See also [ContainerRepository].
@ProviderFor(ContainerRepository) @ProviderFor(ContainerRepository)
+4 -1
View File
@@ -24,7 +24,10 @@ dependencies:
flutter_material_design_icons: ^1.1.7447 flutter_material_design_icons: ^1.1.7447
flutter_mozilla_components: flutter_mozilla_components:
path: ../packages/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_secure_storage: ^10.0.0-beta.4
flutter_slidable: ^4.0.0 flutter_slidable: ^4.0.0
flutter_svg: ^2.2.0 flutter_svg: ^2.2.0