12 imports flagged by `unnecessary_import` because the symbols
they bring in are also re-exported by the umbrella import already
present in the same file:
- bin/clide.dart: src/git/client.dart, src/pql/client.dart
(covered by package:clide/clide.dart).
- lib/builtin/decisions/, lib/builtin/tickets/ (4 files):
kernel/src/events/message_bus.dart (covered by kernel.dart).
- lib/kernel/src/ipc/in_process.dart: src/daemon/dispatcher.dart
(covered by clide.dart).
- lib/main.dart: kernel/src/toolchain.dart (covered by kernel.dart).
- test/builtin/ipc_status/widget_test.dart:
builtin/ipc_status/src/status_item.dart (covered by
ipc_status.dart).
- test/daemon/{git,pql}_commands_test.dart: src/git/client.dart and
src/pql/client.dart (covered by clide.dart).
- test/widgets/multitab_pane_test.dart: widgets/src/icons/x.dart
(covered by widgets.dart).
Mechanical change — every removed line was already a no-op for
symbol resolution; the umbrella imports define the public surface
each file is actually using.
Co-Authored-By: Claude <noreply@anthropic.com>
74 lines
2.3 KiB
Dart
74 lines
2.3 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:clide/kernel/kernel.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
class TicketDetail {
|
|
const TicketDetail({required this.ticket, this.parents = const [], this.decisions = const []});
|
|
final Map<String, Object?> ticket;
|
|
final List<Map<String, Object?>> parents;
|
|
final List<Map<String, Object?>> decisions;
|
|
|
|
String get id => ticket['id'] as String? ?? '';
|
|
String get title => ticket['title'] as String? ?? '';
|
|
String? get type => ticket['type'] as String?;
|
|
String? get status => ticket['status'] as String?;
|
|
String? get priority => ticket['priority'] as String?;
|
|
String? get description => ticket['description'] as String?;
|
|
String? get parentId => ticket['parent_id'] as String?;
|
|
String? get decisionRef => ticket['decision_ref'] as String?;
|
|
String? get assignedTo => ticket['assigned_to'] as String?;
|
|
}
|
|
|
|
class TicketDetailController extends ChangeNotifier {
|
|
TicketDetailController({required this.ipc, required this.messages, required this.panels}) {
|
|
_sub = messages.subscribe(publisher: 'builtin.tickets', channel: 'selection').listen(_onSelection);
|
|
}
|
|
|
|
final DaemonClient ipc;
|
|
final MessageBus messages;
|
|
final PanelRegistry panels;
|
|
StreamSubscription<Message>? _sub;
|
|
|
|
TicketDetail? _detail;
|
|
TicketDetail? get detail => _detail;
|
|
|
|
bool _loading = false;
|
|
bool get loading => _loading;
|
|
|
|
void _onSelection(Message msg) {
|
|
final id = msg.data['id'] as String?;
|
|
if (id != null) {
|
|
panels.activateTab(Slots.contextPanel, 'tickets.detail');
|
|
unawaited(load(id));
|
|
}
|
|
}
|
|
|
|
Future<void> load(String id) async {
|
|
_loading = true;
|
|
notifyListeners();
|
|
|
|
final resp = await ipc.request('pql.tickets.show', args: {'id': id, 'withContext': true});
|
|
if (!resp.ok) {
|
|
_loading = false;
|
|
notifyListeners();
|
|
return;
|
|
}
|
|
|
|
final ticket = resp.data;
|
|
final ancestors = (ticket['ancestors'] as List?)?.cast<Map<String, Object?>>() ?? const [];
|
|
final decisions = (ticket['decisions'] as List?)?.cast<Map<String, Object?>>() ?? const [];
|
|
|
|
_detail = TicketDetail(ticket: ticket, parents: ancestors, decisions: decisions);
|
|
_loading = false;
|
|
messages.publish('builtin.tickets', 'focus', {'id': id});
|
|
notifyListeners();
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_sub?.cancel();
|
|
super.dispose();
|
|
}
|
|
}
|