Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Device release page #60

Merged
merged 5 commits into from Sep 20, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 27 additions & 0 deletions lib/device_model.dart
@@ -0,0 +1,27 @@
import 'package:fwupd/fwupd.dart';
import 'package:safe_change_notifier/safe_change_notifier.dart';

import 'firmware_model.dart';

class DeviceModel extends SafeChangeNotifier {
DeviceModel(this._firmwareModel, this._device)
jpnurmi marked this conversation as resolved.
Show resolved Hide resolved
: _releases = _firmwareModel.state.getReleases(_device);
final FirmwareModel _firmwareModel;
final FwupdDevice _device;
final List<FwupdRelease>? _releases;

FwupdRelease? _selectedRelease;
FwupdRelease? get selectedRelease => _selectedRelease;
set selectedRelease(FwupdRelease? release) {
_selectedRelease = release;
notifyListeners();
}

FwupdDevice get device => _device;
List<FwupdRelease>? get releases => _releases;

Future<void> verify() => _firmwareModel.verify(_device);
Future<void> install(FwupdRelease release) =>
_firmwareModel.install(_device, release);
bool hasUpgrade() => _firmwareModel.state.hasUpgrade(_device);
}
59 changes: 59 additions & 0 deletions lib/firmware_body_page.dart
@@ -0,0 +1,59 @@
import 'package:flutter/material.dart';
import 'package:fwupd/fwupd.dart';
import 'package:provider/provider.dart';

import 'device_model.dart';
import 'firmware_model.dart';
import 'fwupd_x.dart';
import 'src/widgets/device_body.dart';
import 'src/widgets/release_dialog.dart';

class FirmwareBodyPage extends StatefulWidget {
const FirmwareBodyPage({
super.key,
});

static Widget create(
BuildContext context, {
required FwupdDevice device,
}) {
final firmwareModel = context.read<FirmwareModel>();
return ChangeNotifierProvider<DeviceModel>(
create: (_) => DeviceModel(firmwareModel, device),
child: const FirmwareBodyPage(),
);
}

@override
State<FirmwareBodyPage> createState() => _FirmwareBodyPageState();
}

class _FirmwareBodyPageState extends State<FirmwareBodyPage> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FirmwareBodyPage could be a StatelessWidget?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yes of course.. that was kind of the point 😆

@override
Widget build(BuildContext context) {
final deviceModel = context.watch<DeviceModel>();
return Navigator(
pages: [
MaterialPage(
child: DeviceBody(
device: deviceModel.device,
canVerify: deviceModel.device.canVerify,
onVerify: deviceModel.verify,
releases: deviceModel.releases ?? [],
onInstall: deviceModel.install,
hasUpgrade: deviceModel.hasUpgrade(),
),
),
if (deviceModel.selectedRelease != null)
MaterialPage(
child: ReleaseDialog(
device: deviceModel.device,
releases: deviceModel.releases ?? [],
onInstall: deviceModel.install,
),
)
],
onPopPage: (route, result) => route.didPop(result),
);
}
}
10 changes: 3 additions & 7 deletions lib/firmware_page.dart
Expand Up @@ -4,10 +4,10 @@ import 'package:provider/provider.dart';
import 'package:ubuntu_service/ubuntu_service.dart';
import 'package:yaru_widgets/yaru_widgets.dart';

import 'firmware_body_page.dart';
import 'firmware_model.dart';
import 'fwupd_notifier.dart';
import 'fwupd_service.dart';
import 'fwupd_x.dart';
import 'widgets.dart';

class FirmwarePage extends StatefulWidget {
Expand Down Expand Up @@ -46,13 +46,9 @@ class _FirmwarePageState extends State<FirmwarePage> {
device: device,
hasUpgrade: state.hasUpgrade(device),
),
builder: (context) => DeviceBody(
builder: (context) => FirmwareBodyPage.create(
context,
device: device,
canVerify: device.canVerify,
onVerify: () => model.verify(device),
releases: state.getReleases(device) ?? [],
onInstall: (release) => model.install(device, release),
hasUpgrade: state.hasUpgrade(device),
),
iconData: DeviceIcon.fromName(device.icon.firstOrNull),
))
Expand Down
21 changes: 8 additions & 13 deletions lib/src/widgets/device_body.dart
Expand Up @@ -2,10 +2,11 @@ import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:fwupd/fwupd.dart';
import 'package:provider/provider.dart';

import '../../device_model.dart';
import '../../fwupd_l10n.dart';
import 'device_icon.dart';
import 'release_dialog.dart';

class DeviceBody extends StatelessWidget {
const DeviceBody({
Expand Down Expand Up @@ -118,21 +119,15 @@ class DeviceBody extends StatelessWidget {
if (releases.isNotEmpty)
hasUpgrade
? ElevatedButton(
onPressed: () => showReleaseDialog(
context,
device: device,
releases: releases,
onInstall: onInstall,
),
onPressed: () => context
.read<DeviceModel>()
.selectedRelease = releases.first,
child: Text(l10n.showUpdates),
)
: OutlinedButton(
onPressed: () => showReleaseDialog(
context,
device: device,
releases: releases,
onInstall: onInstall,
),
onPressed: () => context
.read<DeviceModel>()
.selectedRelease = releases.first,
child: Text(l10n.showReleases),
),
],
Expand Down
45 changes: 21 additions & 24 deletions lib/src/widgets/release_dialog.dart
@@ -1,7 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:fwupd/fwupd.dart';
import 'package:provider/provider.dart';

import '../../device_model.dart';
import '../../fwupd_x.dart';
import 'confirmation_dialog.dart';
import 'release_card.dart';
Expand All @@ -22,7 +24,7 @@ Future<void> showReleaseDialog(
);
}

class ReleaseDialog extends StatefulWidget {
class ReleaseDialog extends StatelessWidget {
const ReleaseDialog({
super.key,
required this.device,
Expand All @@ -34,56 +36,51 @@ class ReleaseDialog extends StatefulWidget {
final List<FwupdRelease> releases;
final ValueChanged<FwupdRelease> onInstall;

@override
State<ReleaseDialog> createState() => _ReleaseDialogState();
}

class _ReleaseDialogState extends State<ReleaseDialog> {
FwupdRelease? _selected;

@override
Widget build(BuildContext context) {
final selected = context.watch<DeviceModel>().selectedRelease;
final l10n = AppLocalizations.of(context);
final String action;
final String dialogText;

if (_selected?.isDowngrade == true) {
if (selected?.isDowngrade == true) {
action = l10n.downgrade;
dialogText = l10n.downgradeConfirm(
widget.device.name,
widget.device.version,
_selected?.version,
device.name,
device.version,
selected?.version,
);
} else if (_selected?.isUpgrade == false) {
} else if (selected?.isUpgrade == false) {
action = l10n.reinstall;
dialogText = l10n.reinstallConfirm(
widget.device.name,
widget.device.version,
device.name,
device.version,
);
} else {
action = l10n.upgrade;
dialogText = l10n.upgradeConfirm(
widget.device.name,
widget.device.version,
_selected?.version,
device.name,
device.version,
selected?.version,
);
}

return AlertDialog(
title: Text('${widget.device.name} ${widget.device.version}'),
title: Text('${device.name} ${device.version}'),
titlePadding: const EdgeInsets.fromLTRB(16, 16, 16, 0),
contentPadding: const EdgeInsets.all(4),
content: SingleChildScrollView(
padding: const EdgeInsets.all(12),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: widget.releases.map((release) {
children: releases.map((release) {
return Flexible(
child: ReleaseCard(
release: release,
selected: release == _selected,
onSelected: () => setState(() => _selected = release),
selected: release == selected,
onSelected: () =>
context.read<DeviceModel>().selectedRelease = release,
),
);
}).toList(),
Expand All @@ -93,13 +90,13 @@ class _ReleaseDialogState extends State<ReleaseDialog> {
actionsPadding: const EdgeInsets.fromLTRB(0, 0, 12, 12),
actions: [
ElevatedButton(
onPressed: _selected != null
onPressed: selected != null
? () {
showConfirmationDialog(
context,
text: dialogText,
onConfirm: () {
widget.onInstall(_selected!);
onInstall(selected);
Navigator.of(context).pop();
},
onCancel: () {},
Expand Down
50 changes: 50 additions & 0 deletions test/device_model_test.dart
@@ -0,0 +1,50 @@
import 'package:firmware_updater/device_model.dart';
import 'package:firmware_updater/firmware_model.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:fwupd/fwupd.dart';
import 'package:mockito/mockito.dart';

import 'test_utils.dart';

void main() {
jpnurmi marked this conversation as resolved.
Show resolved Hide resolved
test('fetches releases', () async {
final device = testDevice(id: 'a');

final releases = [
FwupdRelease(version: '1', name: ''),
FwupdRelease(version: '2', name: ''),
];

final service = mockService(devices: [device], releases: {'a': releases});

final firmwareModel = FirmwareModel(service);
await firmwareModel.init();

final deviceModel = DeviceModel(firmwareModel, device);

expect(deviceModel.releases, releases);
});

test('install release', () async {
final device = testDevice(id: '');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be nice to specify a non-empty ID so it wouldn't match any default-constructed device below

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do

final release = FwupdRelease(name: '');

final service = mockService();

final firmwareModel = FirmwareModel(service);
final deviceModel = DeviceModel(firmwareModel, device);
await deviceModel.install(release);
verify(service.install(device, release)).called(1);
});

test('verify', () async {
final device = testDevice(id: '');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, could you set a non-empty id please :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do


final service = mockService();

final firmwareModel = FirmwareModel(service);
final deviceModel = DeviceModel(firmwareModel, device);
await deviceModel.verify();
verify(service.verify(device)).called(1);
});
}