240 lines
9.1 KiB
Dart
240 lines
9.1 KiB
Dart
import 'package:flutter/cupertino.dart';
|
|
import 'package:macos_ui/macos_ui.dart';
|
|
import '../../rust/api/packages.dart';
|
|
import '../../rust/frb_generated.dart';
|
|
|
|
class InstalledScreen extends StatefulWidget {
|
|
final Function(String name, BackendType backend)? onInspectPackage;
|
|
|
|
const InstalledScreen({
|
|
super.key,
|
|
this.onInspectPackage,
|
|
});
|
|
|
|
@override
|
|
State<InstalledScreen> createState() => _InstalledScreenState();
|
|
}
|
|
|
|
class _InstalledScreenState extends State<InstalledScreen> {
|
|
List<PackageInfo> _packages = [];
|
|
String _searchQuery = '';
|
|
bool _isLoading = true;
|
|
BackendType? _filterBackend;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_loadInstalled();
|
|
}
|
|
|
|
Future<void> _loadInstalled() async {
|
|
setState(() => _isLoading = true);
|
|
try {
|
|
final list =
|
|
await RustBridgeApi.getInstalledPackages(backend: _filterBackend);
|
|
if (mounted) {
|
|
setState(() {
|
|
_packages = list;
|
|
_isLoading = false;
|
|
});
|
|
}
|
|
} catch (e) {
|
|
if (mounted) setState(() => _isLoading = false);
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final filtered = _packages.where((p) {
|
|
return p.name.toLowerCase().contains(_searchQuery.toLowerCase()) ||
|
|
p.description.toLowerCase().contains(_searchQuery.toLowerCase());
|
|
}).toList();
|
|
|
|
return MacosScaffold(
|
|
toolBar: ToolBar(
|
|
title: const Text('Установленные пакеты'),
|
|
leading: MacosTooltip(
|
|
message: 'Боковая панель',
|
|
child: MacosIconButton(
|
|
icon: const MacosIcon(CupertinoIcons.sidebar_left),
|
|
onPressed: () => MacosWindowScope.of(context).toggleSidebar(),
|
|
),
|
|
),
|
|
actions: [
|
|
// Filter Popup
|
|
CustomToolbarItem(
|
|
inToolbarBuilder: (context) => Padding(
|
|
padding: const EdgeInsets.only(right: 8.0),
|
|
child: MacosPopupButton<BackendType?>(
|
|
value: _filterBackend,
|
|
items: const [
|
|
MacosPopupMenuItem(value: null, child: Text('Все бэкенды')),
|
|
MacosPopupMenuItem(
|
|
value: BackendType.pacman, child: Text('Pacman')),
|
|
MacosPopupMenuItem(
|
|
value: BackendType.flatpak, child: Text('Flatpak')),
|
|
],
|
|
onChanged: (b) {
|
|
setState(() => _filterBackend = b);
|
|
_loadInstalled();
|
|
},
|
|
),
|
|
),
|
|
),
|
|
CustomToolbarItem(
|
|
inToolbarBuilder: (context) => Padding(
|
|
padding: const EdgeInsets.only(right: 8.0),
|
|
child: SizedBox(
|
|
width: 220,
|
|
child: MacosTextField(
|
|
placeholder: 'Поиск пакета...',
|
|
placeholderStyle: const TextStyle(
|
|
color: CupertinoColors.systemGrey2,
|
|
fontSize: 13,
|
|
),
|
|
prefix: const Padding(
|
|
padding: EdgeInsets.symmetric(horizontal: 6),
|
|
child: MacosIcon(
|
|
CupertinoIcons.search,
|
|
size: 14,
|
|
color: CupertinoColors.systemGrey,
|
|
),
|
|
),
|
|
onChanged: (val) => setState(() => _searchQuery = val),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
children: [
|
|
ContentArea(
|
|
builder: (context, scrollController) {
|
|
if (_isLoading) {
|
|
return const Center(child: ProgressCircle(radius: 16));
|
|
}
|
|
|
|
return ListView.separated(
|
|
controller: scrollController,
|
|
padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 20),
|
|
itemCount: filtered.length,
|
|
separatorBuilder: (_, _) => const SizedBox(height: 8),
|
|
itemBuilder: (context, idx) {
|
|
final pkg = filtered[idx];
|
|
final isFlatpak = pkg.backend == BackendType.flatpak;
|
|
|
|
return Container(
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFF222224),
|
|
borderRadius: BorderRadius.circular(8),
|
|
border: Border.all(
|
|
color: CupertinoColors.white.withValues(alpha: 0.08),
|
|
width: 0.8,
|
|
),
|
|
),
|
|
child: CupertinoButton(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 14, vertical: 12),
|
|
alignment: Alignment.centerLeft,
|
|
pressedOpacity: 0.7,
|
|
onPressed: () {
|
|
widget.onInspectPackage?.call(pkg.name, pkg.backend);
|
|
},
|
|
child: Row(
|
|
children: [
|
|
MacosIcon(
|
|
isFlatpak
|
|
? CupertinoIcons.app_badge_fill
|
|
: CupertinoIcons.cube_box_fill,
|
|
size: 20,
|
|
color: isFlatpak
|
|
? const Color(0xFF4A90E2)
|
|
: const Color(0xFF1793D1),
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Text(
|
|
pkg.name,
|
|
style: const TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 14,
|
|
color: CupertinoColors.white,
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
Text(
|
|
pkg.version,
|
|
style: const TextStyle(
|
|
fontFamily: 'monospace',
|
|
color: CupertinoColors.systemGrey,
|
|
fontSize: 12,
|
|
),
|
|
),
|
|
const Spacer(),
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 6, vertical: 2),
|
|
decoration: BoxDecoration(
|
|
color: isFlatpak
|
|
? const Color(0xFF4A90E2)
|
|
.withValues(alpha: 0.12)
|
|
: const Color(0xFF1793D1)
|
|
.withValues(alpha: 0.12),
|
|
borderRadius: BorderRadius.circular(4),
|
|
),
|
|
child: Text(
|
|
pkg.backend.displayName,
|
|
style: TextStyle(
|
|
fontSize: 10.5,
|
|
fontWeight: FontWeight.bold,
|
|
color: isFlatpak
|
|
? const Color(0xFF4A90E2)
|
|
: const Color(0xFF1793D1),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 3),
|
|
Text(
|
|
pkg.description,
|
|
style: const TextStyle(
|
|
fontSize: 12,
|
|
color: CupertinoColors.systemGrey,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(width: 14),
|
|
Text(
|
|
pkg.formattedSize,
|
|
style: const TextStyle(
|
|
fontSize: 11.5,
|
|
color: CupertinoColors.systemGrey,
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
const MacosIcon(
|
|
CupertinoIcons.chevron_right,
|
|
size: 13,
|
|
color: CupertinoColors.systemGrey,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
},
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|