Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
77956d0
refactor: move connection management files to features/connections/
ZhuchkaTriplesix Mar 5, 2026
7b5a929
refactor: move MongoDB files to features/mongodb/
ZhuchkaTriplesix Mar 5, 2026
5aa0154
refactor: move Redis files to features/redis/
ZhuchkaTriplesix Mar 5, 2026
d8c9ef9
refactor: update imports in main_screen and workspace_panel
ZhuchkaTriplesix Mar 5, 2026
d9bdae9
fix: close stale Redis connections on page refresh and widget dispose
ZhuchkaTriplesix Mar 5, 2026
e467b63
chore: update auto-generated Flutter plugin files
ZhuchkaTriplesix Mar 5, 2026
3122756
test: fix broken widget_test.dart placeholder
ZhuchkaTriplesix Mar 5, 2026
a131cb8
test: add Redis connection and service tests
ZhuchkaTriplesix Mar 5, 2026
d87b439
test: add MongoDB service and connection data tests
ZhuchkaTriplesix Mar 5, 2026
93ed11a
test: add existing MongoDB connection and ConnectionRow tests
ZhuchkaTriplesix Mar 5, 2026
0ebbf64
ci: add GitHub Actions workflow for tests and analysis
ZhuchkaTriplesix Mar 5, 2026
886df8f
fix: resolve analyzer issues in core files
ZhuchkaTriplesix Mar 5, 2026
a232761
fix: add const constructors in connection dialogs
ZhuchkaTriplesix Mar 5, 2026
e6e2080
fix: add const constructors and fix child property order in main screen
ZhuchkaTriplesix Mar 5, 2026
90e9a4e
fix: add const constructors and remove unused imports in MongoDB views
ZhuchkaTriplesix Mar 5, 2026
8822c06
fix: add const constructors in Redis connection forms and views
ZhuchkaTriplesix Mar 5, 2026
06ada20
fix: add const to ConnectionRow constructors in tests
ZhuchkaTriplesix Mar 5, 2026
02bb290
Merge pull request #6 from QueryaHub/redis-connection-pull
ZhuchkaTriplesix Mar 5, 2026
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
2 changes: 1 addition & 1 deletion .flutter-plugins-dependencies
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"path_provider_foundation","path":"/home/reei/.pub-cache/hosted/pub.dev/path_provider_foundation-2.6.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"sqflite_darwin","path":"/home/reei/.pub-cache/hosted/pub.dev/sqflite_darwin-2.4.2/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"path_provider_android","path":"/home/reei/.pub-cache/hosted/pub.dev/path_provider_android-2.2.22/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"sqflite_android","path":"/home/reei/.pub-cache/hosted/pub.dev/sqflite_android-2.4.2+2/","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"bitsdojo_window_macos","path":"/home/reei/.pub-cache/hosted/pub.dev/bitsdojo_window_macos-0.1.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/home/reei/.pub-cache/hosted/pub.dev/path_provider_foundation-2.6.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"sqflite_darwin","path":"/home/reei/.pub-cache/hosted/pub.dev/sqflite_darwin-2.4.2/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"bitsdojo_window_linux","path":"/home/reei/.pub-cache/hosted/pub.dev/bitsdojo_window_linux-0.1.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_linux","path":"/home/reei/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[],"dev_dependency":false}],"windows":[{"name":"bitsdojo_window_windows","path":"/home/reei/.pub-cache/hosted/pub.dev/bitsdojo_window_windows-0.1.6/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_windows","path":"/home/reei/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[],"dev_dependency":false}],"web":[]},"dependencyGraph":[{"name":"bitsdojo_window","dependencies":["bitsdojo_window_windows","bitsdojo_window_macos","bitsdojo_window_linux"]},{"name":"bitsdojo_window_linux","dependencies":[]},{"name":"bitsdojo_window_macos","dependencies":[]},{"name":"bitsdojo_window_windows","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"sqflite","dependencies":["sqflite_android","sqflite_darwin"]},{"name":"sqflite_android","dependencies":[]},{"name":"sqflite_darwin","dependencies":[]}],"date_created":"2026-03-05 04:09:30.456439","version":"3.38.9","swift_package_manager_enabled":{"ios":false,"macos":false}}
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"path_provider_foundation","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.6.0\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"sqflite_darwin","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\sqflite_darwin-2.4.2\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"path_provider_android","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_android-2.2.22\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"sqflite_android","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\sqflite_android-2.4.2+2\\\\","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"bitsdojo_window_macos","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\bitsdojo_window_macos-0.1.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.6.0\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"sqflite_darwin","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\sqflite_darwin-2.4.2\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"bitsdojo_window_linux","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\bitsdojo_window_linux-0.1.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_linux","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_linux-2.2.1\\\\","native_build":false,"dependencies":[],"dev_dependency":false}],"windows":[{"name":"bitsdojo_window_windows","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\bitsdojo_window_windows-0.1.6\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_windows","path":"C:\\\\Users\\\\junte\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_windows-2.3.0\\\\","native_build":false,"dependencies":[],"dev_dependency":false}],"web":[]},"dependencyGraph":[{"name":"bitsdojo_window","dependencies":["bitsdojo_window_windows","bitsdojo_window_macos","bitsdojo_window_linux"]},{"name":"bitsdojo_window_linux","dependencies":[]},{"name":"bitsdojo_window_macos","dependencies":[]},{"name":"bitsdojo_window_windows","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"sqflite","dependencies":["sqflite_android","sqflite_darwin"]},{"name":"sqflite_android","dependencies":[]},{"name":"sqflite_darwin","dependencies":[]}],"date_created":"2026-03-05 22:16:33.139337","version":"3.38.5","swift_package_manager_enabled":{"ios":false,"macos":false}}
46 changes: 46 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: CI

on:
push:
branches: [ main, dev, '**-connector', '**-connection-pull' ]
pull_request:
branches: [ main, dev ]

jobs:
test:
name: Run Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.38.5'
channel: 'stable'
cache: true

- name: Get dependencies
run: flutter pub get

- name: Run tests
run: flutter test

analyze:
name: Code Analysis
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.38.5'
channel: 'stable'
cache: true

- name: Get dependencies
run: flutter pub get

- name: Analyze code
run: flutter analyze
11 changes: 8 additions & 3 deletions lib/core/database/redis_connection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,15 @@ class RedisConnection {
}

Future<void> disconnect() async {
await _conn?.close();
_conn = null;
_command = null;
_isConnected = false;
_command = null;
final c = _conn;
_conn = null;
try {
await c?.close();
} catch (_) {
// Connection may already be closed — ignore.
}
}

Future<String> info() async {
Expand Down
15 changes: 13 additions & 2 deletions lib/core/database/redis_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,30 @@ class RedisService {

final Map<int, RedisConnection> _connections = {};

/// Creates (or replaces) a [RedisConnection] for the given [ConnectionRow].
/// If a connection with the same ID already exists it is disconnected first.
RedisConnection createConnection(ConnectionRow row) {
if (row.type != 'redis') {
throw ArgumentError('Connection type must be redis');
}

final id = row.id ?? 0;

// Disconnect previous connection for this ID, if any.
final existing = _connections[id];
if (existing != null) {
existing.disconnect(); // fire-and-forget; disconnect is safe
}

final conn = RedisConnection(
id: row.id ?? 0,
id: id,
name: row.name,
host: row.host ?? 'localhost',
port: row.port ?? 6379,
username: row.username,
password: row.password,
);
_connections[conn.id] = conn;
_connections[id] = conn;
return conn;
}

Expand Down
1 change: 1 addition & 0 deletions lib/core/drivers/driver_urls.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/// Official download URLs and file names for database drivers.
/// Used by [DriverStorage] for download/install.
/// Redis and MongoDB use Dart packages (built-in); only JAR-based drivers are listed here.
library;

/// PostgreSQL JDBC driver (official jdbc.postgresql.org).
const postgresqlDriverUrl = 'https://jdbc.postgresql.org/download/postgresql-42.7.10.jar';
Expand Down
4 changes: 3 additions & 1 deletion lib/core/storage/folders_storage.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ class FoldersStorage {
Future<void> save(List<String> folders) async {
_folders = List.from(folders);
await LocalDb.instance.clearFolders();
for (final name in _folders) await LocalDb.instance.addFolder(name);
for (final name in _folders) {
await LocalDb.instance.addFolder(name);
}
}

Future<void> add(String name) async {
Expand Down
4 changes: 2 additions & 2 deletions lib/core/theme/app_theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import 'package:shadcn_flutter/shadcn_flutter.dart';

/// App theme: dark only. Used for both theme and darkTheme so the app is always dark.
abstract class AppTheme {
static ThemeData get dark => ThemeData.dark(
static ThemeData get dark => const ThemeData.dark(
colorScheme: ColorSchemes.darkSlate,
radius: 0.5,
scaling: 1,
typography: const Typography.geist(),
typography: Typography.geist(),
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import 'package:querya_desktop/core/storage/folders_storage.dart';
import 'package:querya_desktop/core/storage/local_db.dart';
import 'package:querya_desktop/shared/widgets/widgets.dart';

import 'mongodb_connection_form.dart';
import 'package:querya_desktop/features/mongodb/mongodb_connection_form.dart';
import 'package:querya_desktop/features/redis/redis_connection_form.dart';
import 'new_connection_dialog.dart';
import 'new_folder_dialog.dart';
import 'redis_connection_form.dart';

/// Left panel: Browser tree (pgAdmin-style). Uses shadcn layout widgets.
class ConnectionsPanel extends StatefulWidget {
Expand Down Expand Up @@ -142,7 +142,7 @@ class _ConnectionsPanelState extends State<ConnectionsPanel> {
padding: const material.EdgeInsets.fromLTRB(20, 24, 16, 16),
child: material.DefaultTextStyle(
style: material.TextStyle(color: theme.colorScheme.mutedForeground),
child: Text('Browser').semiBold().small(),
child: const Text('Browser').semiBold().small(),
),
),
Divider(height: 1, color: theme.colorScheme.border.withValues(alpha: 0.3)),
Expand Down Expand Up @@ -198,8 +198,8 @@ class _ConnectionsPanelState extends State<ConnectionsPanel> {
),
// Empty state
if (_connections.isEmpty && _folders.isEmpty)
material.Padding(
padding: const material.EdgeInsets.only(top: 8),
const material.Padding(
padding: material.EdgeInsets.only(top: 8),
child: _EmptyState(message: 'No connections yet'),
),
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ void showDriverManagerDialog(BuildContext context) {
showDialog<void>(
context: context,
barrierColor: material.Colors.black54,
builder: (context) => material.Dialog(
builder: (context) => const material.Dialog(
backgroundColor: material.Colors.transparent,
insetPadding: const material.EdgeInsets.symmetric(horizontal: 40, vertical: 24),
child: const _DriverManagerDialogContent(),
insetPadding: material.EdgeInsets.symmetric(horizontal: 40, vertical: 24),
child: _DriverManagerDialogContent(),
),
);
}
Expand Down Expand Up @@ -64,11 +64,6 @@ class _DriverManagerDialogContentState extends material.State<_DriverManagerDial
}
}

Future<void> _refreshStatus(DownloadableDriver driver) async {
final installed = await DriverStorage.instance.isInstalled(driver);
if (mounted) setState(() => _installed[driver] = installed);
}

Future<void> _download(DownloadableDriver driver) async {
setState(() {
_errors.remove(driver);
Expand Down Expand Up @@ -126,9 +121,9 @@ class _DriverManagerDialogContentState extends material.State<_DriverManagerDial
child: material.Column(
crossAxisAlignment: material.CrossAxisAlignment.start,
children: [
Text('Driver Manager').large().semiBold(),
material.SizedBox(height: 6),
Text(
const Text('Driver Manager').large().semiBold(),
const material.SizedBox(height: 6),
const Text(
'Built-in database drivers. Add new connection types via Connection → New Database Connection.',
).muted().small(),
],
Expand Down Expand Up @@ -238,17 +233,17 @@ class _DriverRow extends material.StatelessWidget {
)
: material.Icon(type.icon, size: 40, color: theme.primary),
),
material.SizedBox(width: 16),
const material.SizedBox(width: 16),
material.Expanded(
child: material.Column(
crossAxisAlignment: material.CrossAxisAlignment.start,
mainAxisSize: material.MainAxisSize.min,
children: [
Text(type.label).semiBold().small(),
material.SizedBox(height: 2),
const material.SizedBox(height: 2),
Text(description).muted().xSmall(),
if (error != null) ...[
material.SizedBox(height: 4),
const material.SizedBox(height: 4),
Text(
error!,
style: material.TextStyle(
Expand All @@ -261,7 +256,7 @@ class _DriverRow extends material.StatelessWidget {
),
),
if (isBuiltIn) ...[
material.SizedBox(width: 8),
const material.SizedBox(width: 8),
material.Container(
padding: const material.EdgeInsets.symmetric(horizontal: 10, vertical: 6),
decoration: material.BoxDecoration(
Expand All @@ -281,7 +276,7 @@ class _DriverRow extends material.StatelessWidget {
),
),
] else if (showDownload || showUninstall) ...[
material.SizedBox(width: 8),
const material.SizedBox(width: 8),
if (showDownload)
loading
? material.SizedBox(
Expand Down Expand Up @@ -324,7 +319,7 @@ class _DriverRow extends material.StatelessWidget {
)
: IconButton.destructive(
onPressed: onUninstall,
icon: material.Icon(
icon: const material.Icon(
material.Icons.delete_outline_rounded,
size: 20,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ Future<ConnectionType?> showNewConnectionDialog(BuildContext context) {
return showDialog<ConnectionType>(
context: context,
barrierColor: material.Colors.black54,
builder: (context) => material.Dialog(
builder: (context) => const material.Dialog(
backgroundColor: material.Colors.transparent,
insetPadding: const material.EdgeInsets.symmetric(horizontal: 40, vertical: 24),
child: const _NewConnectionDialogContent(),
insetPadding: material.EdgeInsets.symmetric(horizontal: 40, vertical: 24),
child: _NewConnectionDialogContent(),
),
);
}
Expand Down Expand Up @@ -109,12 +109,12 @@ class _NewConnectionDialogContentState extends material.State<_NewConnectionDial
child: Column(
crossAxisAlignment: material.CrossAxisAlignment.stretch,
children: [
Text('Select your database').large().semiBold(),
material.SizedBox(height: 6),
Text(
const Text('Select your database').large().semiBold(),
const material.SizedBox(height: 6),
const Text(
'Create new database connection. Find your database driver in the list below.',
).muted().small(),
material.SizedBox(height: 12),
const material.SizedBox(height: 12),
material.Container(
decoration: material.BoxDecoration(
color: theme.muted.withValues(alpha: 0.2),
Expand All @@ -129,7 +129,7 @@ class _NewConnectionDialogContentState extends material.State<_NewConnectionDial
size: 20,
color: theme.mutedForeground,
),
material.SizedBox(width: 10),
const material.SizedBox(width: 10),
material.Expanded(
child: TextField(
controller: _searchController,
Expand Down Expand Up @@ -230,7 +230,7 @@ class _NewConnectionDialogContentState extends material.State<_NewConnectionDial
onPressed: () => material.Navigator.of(context).pop(),
child: const Text('Cancel'),
),
material.SizedBox(width: 12),
const material.SizedBox(width: 12),
PrimaryButton(
onPressed: _selectedType == null
? null
Expand Down
Loading