Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions lib/core/actions/querya_core_commands.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:querya_desktop/core/actions/data_grid_command_bridge.dart';
import 'package:querya_desktop/core/actions/querya_command.dart';
import 'package:querya_desktop/core/actions/querya_command_host.dart';
import 'package:querya_desktop/core/actions/sql_editor_command_bridge.dart';
import 'package:querya_desktop/core/actions/table_view_command_bridge.dart';
import 'package:querya_desktop/core/theme/theme_controller.dart';
import 'package:querya_desktop/features/connections/connection_creation_flow.dart';
import 'package:querya_desktop/features/connections/team_profile_actions.dart';
Expand All @@ -21,6 +22,7 @@ import 'package:querya_desktop/shared/services/data_export_service.dart';
List<QueryaCommand> queryaCoreCommands() {
final sql = SqlEditorCommandBridge.instance;
final grid = DataGridCommandBridge.instance;
final tables = TableViewCommandBridge.instance;
return [
// -- Workspace ----------------------------------------------------------
QueryaCommand(
Expand Down Expand Up @@ -160,6 +162,26 @@ List<QueryaCommand> queryaCoreCommands() {
QueryaCommandHost.maybeOf(context)?.onToggleReadOnly?.call(),
),

// -- Table --------------------------------------------------------------
QueryaCommand(
id: 'querya.table.showRelations',
title: 'Table: Show relations',
category: 'Table',
icon: Icons.account_tree_outlined,
aliases: const ['erd', 'diagram', 'foreign keys', 'references'],
isEnabled: (_) => tables.isActive,
execute: (_) => tables.invokeSelectView(1),
),
QueryaCommand(
id: 'querya.table.showData',
title: 'Table: Show data',
category: 'Table',
icon: Icons.table_rows_outlined,
aliases: const ['grid', 'rows'],
isEnabled: (_) => tables.isActive,
execute: (_) => tables.invokeSelectView(0),
),

// -- SQL ----------------------------------------------------------------
QueryaCommand(
id: 'querya.sql.execute',
Expand Down
39 changes: 39 additions & 0 deletions lib/core/actions/table_view_command_bridge.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import 'package:flutter/foundation.dart';

/// Lets the Command Palette switch the table browser on screen between Data
/// and Relations. Only a table that offers the switch registers.
class TableViewCommandBridge {
TableViewCommandBridge._();

static final TableViewCommandBridge instance = TableViewCommandBridge._();

Object? _owner;
void Function(int index)? _onSelectView;

bool get isActive => _onSelectView != null;

void register({
required Object owner,
required void Function(int index) onSelectView,
}) {
_owner = owner;
_onSelectView = onSelectView;
}

/// Clears the registration only while [owner] still holds it, so a table
/// that closes does not drop the one that registered after it.
void unregister({required Object owner}) {
if (!identical(_owner, owner)) return;
_owner = null;
_onSelectView = null;
}

/// 0 shows the grid (Data), 1 the neighbourhood (Relations).
void invokeSelectView(int index) => _onSelectView?.call(index);

@visibleForTesting
void resetForTest() {
_owner = null;
_onSelectView = null;
}
}
15 changes: 15 additions & 0 deletions lib/features/workspace/generic_table_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:flutter/material.dart' as material;
import 'package:flutter/services.dart' show LogicalKeyboardKey;
import 'package:querya_desktop/core/storage/local_db.dart' show MutationAuditSource;
import 'package:querya_desktop/core/storage/mutation_audit_recorder.dart';
import 'package:querya_desktop/core/actions/table_view_command_bridge.dart';
import 'package:querya_desktop/core/database/table_mutation_engine.dart';
import 'package:querya_desktop/core/database/table_schema_meta.dart';
import 'package:querya_desktop/features/erd/erd_source.dart';
Expand Down Expand Up @@ -216,6 +217,18 @@ class GenericTableViewState extends material.State<GenericTableView> {
void initState() {
super.initState();
_loadPage(refreshCount: true);
_syncRelationsCommand();
}

/// The Command Palette can switch Data / Relations only while this table
/// offers the switch.
void _syncRelationsCommand() {
final bridge = TableViewCommandBridge.instance;
if (showsRelations) {
bridge.register(owner: this, onSelectView: selectView);
} else {
bridge.unregister(owner: this);
}
}

@override
Expand All @@ -234,10 +247,12 @@ class GenericTableViewState extends material.State<GenericTableView> {
} else if (oldWidget.isReadOnly != widget.isReadOnly) {
_syncStagingToReadOnly();
}
_syncRelationsCommand();
}

@override
void dispose() {
TableViewCommandBridge.instance.unregister(owner: this);
_resetStaging();
widget.delegate.cancel(interruptIfBusy: true);
widget.delegate.dispose();
Expand Down
24 changes: 24 additions & 0 deletions test/features/workspace/generic_table_view_relations_test.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import 'package:flutter/material.dart' as material;
import 'package:flutter_test/flutter_test.dart';
import 'package:querya_desktop/core/actions/table_view_command_bridge.dart';
import 'package:querya_desktop/features/erd/erd_view.dart';
import 'package:querya_desktop/features/workspace/results_tab.dart';

import '../../support/fake_table_data_delegate.dart';
import '../../support/generic_table_view_harness.dart';

void main() {
setUp(() => TableViewCommandBridge.instance.resetForTest());

testWidgets('a table offers the Data | Relations switch', (t) async {
await pumpGenericTableView(t, FakeTableDataDelegate());
expect(find.text('Relations'), findsOneWidget);
Expand Down Expand Up @@ -46,4 +49,25 @@ void main() {
expect(find.byType(ErdView, skipOffstage: false), findsOneWidget);
expect(find.byType(ResultsTab, skipOffstage: false), findsOneWidget);
});

testWidgets('the palette switches a table between Data and Relations',
(t) async {
final bridge = TableViewCommandBridge.instance;
await pumpGenericTableView(t, FakeTableDataDelegate());
expect(bridge.isActive, isTrue);

bridge.invokeSelectView(1);
await t.pump();
await t.pump();
expect(find.byType(ErdView), findsOneWidget);

bridge.invokeSelectView(0);
await t.pump();
expect(find.text('Relations'), findsOneWidget);
});

testWidgets('a view leaves nothing for the palette to switch', (t) async {
await pumpGenericTableView(t, FakeTableDataDelegate(), isView: true);
expect(TableViewCommandBridge.instance.isActive, isFalse);
});
}
Loading