feat: add label for newly installed vms

This commit is contained in:
ivo 2025-05-13 17:18:46 +02:00
parent 18e0c20ccb
commit fd2ccebafe
5 changed files with 141 additions and 30 deletions

View File

@ -5,6 +5,8 @@ import 'dart:io';
import 'package:desktop_notifications/desktop_notifications.dart';
import 'package:flutter/material.dart';
import 'package:gettext_i18n/gettext_i18n.dart';
import 'package:quickgui/src/widgets/downloader/run_image_button.dart';
import 'package:path/path.dart' as p;
import '../model/operating_system.dart';
import '../model/option.dart';
@ -36,6 +38,7 @@ class _DownloaderState extends State<Downloader> {
bool _downloadFinished = false;
var controller = StreamController<double>();
Process? _process;
String? _newestVmFolderName;
@override
void initState() {
@ -54,11 +57,26 @@ class _DownloaderState extends State<Downloader> {
}
}
Future<String?> findNewestFolder() async {
final dir = Directory.current;
final folders = await dir
.list()
.where((entity) => entity is Directory)
.cast<Directory>()
.toList();
folders
.sort((a, b) => b.statSync().modified.compareTo(a.statSync().modified));
return folders.isNotEmpty ? p.basename(folders.first.path) : null;
}
Stream<double> progressStream() {
var options = [widget.operatingSystem.code, widget.version.version];
if (widget.option != null) {
options.add(widget.option!.option);
}
Process.start('quickget', options).then((process) {
if (widget.option!.downloader != 'zsync') {
process.stderr.transform(utf8.decoder).forEach(parseCurlProgress);
@ -69,25 +87,30 @@ class _DownloaderState extends State<Downloader> {
process.exitCode.then((value) {
bool _cancelled = value.isNegative;
controller.close();
setState(() {
_downloadFinished = true;
notificationsClient?.notify(
_cancelled
? context.t('Download cancelled')
: context.t('Download complete'),
body: _cancelled
? context.t(
'Download of {0} has been canceled.',
args: [widget.operatingSystem.name],
)
: context.t(
'Download of {0} has completed.',
args: [widget.operatingSystem.name],
),
appName: 'Quickgui',
expireTimeoutMs: 10000, /* 10 seconds */
);
});
findNewestFolder().then(
(path) {
setState(() {
_downloadFinished = true;
_newestVmFolderName = path;
notificationsClient?.notify(
_cancelled
? context.t('Download cancelled')
: context.t('Download complete'),
body: _cancelled
? context.t(
'Download of {0} has been canceled.',
args: [widget.operatingSystem.name],
)
: context.t(
'Download of {0} has completed.',
args: [widget.operatingSystem.name],
),
appName: 'Quickgui',
expireTimeoutMs: 10000, /* 10 seconds */
);
});
},
);
});
setState(() {
@ -103,10 +126,7 @@ class _DownloaderState extends State<Downloader> {
appBar: AppBar(
title: Text(
context.t('Downloading {0}', args: [
'${widget.operatingSystem.name} ${widget.version.version}' +
(widget.option!.option.isNotEmpty
? ' (${widget.option!.option})'
: '')
'${widget.operatingSystem.name} ${widget.version.version}${widget.option!.option.isNotEmpty ? ' (${widget.option!.option})' : ''}'
]),
),
automaticallyImplyLeading: false,
@ -117,10 +137,10 @@ class _DownloaderState extends State<Downloader> {
child: StreamBuilder(
stream: _progressStream,
builder: (context, AsyncSnapshot<double> snapshot) {
var data = !snapshot.hasData ||
widget.option!.downloader != 'curl'
? null
: snapshot.data;
var data =
!snapshot.hasData || widget.option!.downloader != 'curl'
? null
: snapshot.data;
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
@ -143,6 +163,10 @@ class _DownloaderState extends State<Downloader> {
},
),
),
ManageMachinesAfterDownloadButton(
downloadFinished: _downloadFinished,
vmName: _newestVmFolderName ?? '',
),
CancelDismissButton(
onCancel: () {
_process?.kill();

View File

@ -9,6 +9,7 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:gettext_i18n/gettext_i18n.dart';
import 'package:path/path.dart' as path;
import 'package:process_run/shell.dart';
import 'package:quickgui/src/widgets/manager/new_vm_tag.dart';
import 'package:version/version.dart';
import '../globals.dart';
@ -20,7 +21,9 @@ import '../model/vminfo.dart';
/// Displays a list of available VMs, running state and connection info,
/// with buttons to start and stop VMs.
class Manager extends StatefulWidget {
const Manager({super.key});
const Manager({super.key, this.newVmName});
final String? newVmName;
@override
State<Manager> createState() => _ManagerState();
@ -249,6 +252,9 @@ class _ManagerState extends State<Manager> with PreferencesMixin {
List<Widget> _buildRow(String currentVm, Color buttonColor) {
final bool active = _activeVms.containsKey(currentVm);
final bool sshy = _sshVms.contains(currentVm);
final bool newVM = widget.newVmName == currentVm;
VmInfo vmInfo = VmInfo();
String connectInfo = '';
if (active) {
@ -287,7 +293,12 @@ class _ManagerState extends State<Manager> with PreferencesMixin {
return <Widget>[
ListTile(
leading: osIcon ?? const Icon(Icons.computer, size: 32),
title: Text(currentVm),
title: Row(
children: [
Text(currentVm),
newVM ? const NewVmTag() : const SizedBox.shrink()
],
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[

View File

@ -0,0 +1,46 @@
import 'package:flutter/material.dart';
import 'package:gettext_i18n/gettext_i18n.dart';
import 'package:quickgui/src/pages/manager.dart';
class ManageMachinesAfterDownloadButton extends StatelessWidget {
const ManageMachinesAfterDownloadButton({
required this.downloadFinished,
required this.vmName,
super.key,
});
final bool downloadFinished;
final String vmName;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
downloadFinished
? ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.surface,
foregroundColor: Theme.of(context).colorScheme.onSurface,
),
onPressed: () {
Navigator.of(context).pop();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Manager(
newVmName: vmName,
),
),
);
},
child: Text(context.t('Manage existing machines')),
)
: const SizedBox.shrink(),
],
),
);
}
}

View File

@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
class NewVmTag extends StatelessWidget {
const NewVmTag({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Row(
children: [
Icon(
Icons.new_releases,
color: Theme.of(context).colorScheme.primary,
),
Padding(
padding: const EdgeInsets.only(left: 4.0),
child: Text(
'new',
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
),
),
],
),
);
}
}

View File

@ -1,6 +1,6 @@
name: quickgui
description: An elegant virtual machine manager for the desktop
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
publish_to: "none" # Remove this line if you wish to publish to pub.dev
version: 1.2.10+1
environment:
@ -15,6 +15,7 @@ dependencies:
cupertino_icons: ^1.0.8
desktop_notifications: ^0.6.3
file_picker: ^8.0.6
path: ^1.9.1
flutter_svg: ^2.0.17
gettext: ^1.2.0
gettext_i18n: ^1.0.7