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

refactor: Migrate away snap model from old provider style #1660

Merged
merged 31 commits into from
Jun 14, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ccf4f4c
refactor: Migrate to built-in providers
spydon Apr 29, 2024
3adb494
refactor: Break out data class
spydon Apr 29, 2024
9888380
fix: Make suer all tests work with new providers
spydon May 3, 2024
de033a9
docs: Add some dartdocs to the snap model providers
spydon May 7, 2024
9a8bf23
fix: Wait for the polling until returning from FutureProviders
spydon May 7, 2024
d048147
test: Fix so that mock emits done by default
spydon May 7, 2024
bdf8653
test: Make sure that abort test aborts after install
spydon May 7, 2024
1280e7a
chore: Bump dependencies
spydon May 27, 2024
ddaba2e
refactor: Move to generated NotifierProvider
spydon May 27, 2024
53ed96b
Fix manage page tests
spydon May 29, 2024
159ce97
Migrate snap page tests
spydon Jun 4, 2024
593b38e
Use freezed for SnapData class
spydon Jun 4, 2024
02ab3bc
Fix last tests and use AsyncValue on the storesnap
spydon Jun 6, 2024
e7551a3
Try with Flutter 3.22.1
spydon Jun 6, 2024
40fbeec
Invalidate state when needed
spydon Jun 6, 2024
8ba396a
Load snap before removing in test
spydon Jun 6, 2024
a5af871
Use asdf flutter version
spydon Jun 6, 2024
e0ae1c8
Remove unnecessary testing methods
spydon Jun 6, 2024
c29a5a4
Fix PR comments
spydon Jun 10, 2024
63f313b
SnapPackageProvider -> SnapModelProvider
spydon Jun 10, 2024
fb98fc7
Fix formatting
spydon Jun 10, 2024
f733846
Listen on storeSnapProvider instead of watching it
spydon Jun 10, 2024
5e1cee6
Simplify store snap loading
spydon Jun 11, 2024
979c18f
Remove unnecessary reads of storeSnapProvider in tests
spydon Jun 11, 2024
8fed937
Add error handling at top level
spydon Jun 12, 2024
06fe6cf
fix: Force correct card color
spydon Jun 13, 2024
79f7dc4
fix: Force correct color of the other cards too
spydon Jun 13, 2024
26b715d
chore: Downgrade to Flutter 3.19.6
spydon Jun 13, 2024
26d5804
Rename snapModel variables to snapData
spydon Jun 14, 2024
a87a41f
Move callback selection to enum
spydon Jun 14, 2024
e8cb42e
Re-generate mocks
spydon Jun 14, 2024
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 .tool-versions
Original file line number Diff line number Diff line change
@@ -1 +1 @@
flutter 3.19.3-stable
flutter 3.19.6-stable
4 changes: 2 additions & 2 deletions melos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ command:
bootstrap:
environment:
sdk: '>=3.1.0 <4.0.0'
flutter: '>=3.19.0'
flutter: '>=3.19.6'

dev_dependencies:
ubuntu_lints: ^0.3.0
ubuntu_lints: ^0.3.1

scripts:
# analyze all packages
Expand Down
22 changes: 19 additions & 3 deletions packages/app_center/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:app_center/l10n.dart';
import 'package:app_center/packagekit.dart';
import 'package:app_center/ratings.dart';
import 'package:app_center/snapd.dart';
import 'package:app_center/src/providers/error_stream_provider.dart';
import 'package:app_center/store.dart';
import 'package:app_center_ratings_client/app_center_ratings_client.dart';
import 'package:flutter/material.dart';
Expand All @@ -21,9 +22,9 @@ import 'package:ubuntu_service/ubuntu_service.dart';
import 'package:xdg_directories/xdg_directories.dart' as xdg;
import 'package:yaru/yaru.dart';

Future<void> main(List<String> args) async {
await YaruWindowTitleBar.ensureInitialized();
final log = Logger('main');

Future<void> main(List<String> args) async {
final binaryName = p.basename(Platform.resolvedExecutable);
Logger.setup(
path: p.join(
Expand Down Expand Up @@ -66,8 +67,23 @@ Future<void> main(List<String> args) async {
registerService(PackageKitClient.new);
registerService(PackageKitService.new,
dispose: (service) => service.dispose());
registerService(
ErrorStreamController.new,
dispose: (controller) => controller.close(),
);

await initDefaultLocale();

runApp(const ProviderScope(child: StoreApp()));
await runZonedGuarded(
() async {
await YaruWindowTitleBar.ensureInitialized();
runApp(const ProviderScope(child: StoreApp()));
},
(error, stackTrace) {
log.error('Error propagated to top-level', error, stackTrace);
if (error is Exception) {
getService<ErrorStreamController>().add(error);
}
},
);
}
2 changes: 2 additions & 0 deletions packages/app_center/lib/src/games/games_page_featured.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ class _CarouselCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
return YaruBanner(
// TODO: Remove color once we have upgraded to a yaru version > 4.1.0
color: Theme.of(context).cardColor,
padding: EdgeInsets.zero,
onTap: () => onTap(snap),
child: ClipRRect(
Expand Down
141 changes: 73 additions & 68 deletions packages/app_center/lib/src/manage/manage_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,9 @@ class _ActionButtons extends ConsumerWidget {
child: updatesModel.activeChangeId != null
? Consumer(
builder: (context, ref, child) {
final change = ref
.watch(changeProvider(updatesModel.activeChangeId))
.whenOrNull(data: (data) => data);
final change = ref.watch(
activeChangeProvider(updatesModel.activeChangeId),
);
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Expand Down Expand Up @@ -340,7 +340,6 @@ class _ManageSnapTile extends ConsumerWidget {

@override
Widget build(BuildContext context, WidgetRef ref) {
final snapLauncher = ref.watch(launchProvider(snap));
final l10n = AppLocalizations.of(context);
final border = BorderSide(color: Theme.of(context).colorScheme.outline);
final dateTimeSinceUpdate = snap.installDate != null
Expand Down Expand Up @@ -470,75 +469,71 @@ class _ManageSnapTile extends ConsumerWidget {
],
),
trailing: showUpdateButton
? buildButtonBarForUpdate(ref, l10n, snapLauncher, context)
: buildButtonBarForOpen(ref, l10n, snapLauncher, context),
? _ButtonBarForUpdate(snap)
: _ButtonBarForOpen(snap),
),
);
}
}

class _ButtonBarForUpdate extends ConsumerWidget {
const _ButtonBarForUpdate(this.snap);

final Snap snap;

@override
Widget build(BuildContext context, WidgetRef ref) {
final snapLauncher = ref.watch(launchProvider(snap));
final l10n = AppLocalizations.of(context);
final snapModel = ref.watch(snapModelProvider(snap.name));
final updatesModel = ref.watch(updatesModelProvider);
final activeChangeId = snapModel.value?.activeChangeId;
final change = activeChangeId != null
? ref.watch(activeChangeProvider(activeChangeId))
: null;

ButtonBar buildButtonBarForUpdate(WidgetRef ref, AppLocalizations l10n,
SnapLauncher snapLauncher, BuildContext context) {
return ButtonBar(
mainAxisSize: MainAxisSize.min,
children: [
Consumer(
builder: (context, ref, child) {
final snapModel = ref.watch(snapModelProvider(snap.name));
final updatesModel = ref.watch(updatesModelProvider);

return PushButton.outlined(
style: const ButtonStyle(
padding: MaterialStatePropertyAll(EdgeInsets.zero)),
onPressed: updatesModel.activeChangeId != null
? null
: () {
ref.read(snapModelProvider(snap.name)).refresh();
},
child: snapModel.activeChangeId != null
? Consumer(
builder: (context, ref, child) {
final change = ref
.watch(changeProvider(snapModel.activeChangeId))
.whenOrNull(data: (data) => data);
return Row(
children: [
SizedBox.square(
dimension: kCircularProgressIndicatorHeight,
child: YaruCircularProgressIndicator(
value: change?.progress,
strokeWidth: 2,
),
),
if (change != null) ...[
const SizedBox(width: 8),
Flexible(
child: Text(
change.localize(l10n) ?? '',
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
]
],
);
},
)
: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(YaruIcons.download),
const SizedBox(width: 8),
Flexible(
child: Text(
l10n.snapActionUpdateLabel,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
PushButton.outlined(
style: const ButtonStyle(
padding: MaterialStatePropertyAll(EdgeInsets.zero),
),
onPressed: updatesModel.activeChangeId != null || !snapModel.hasValue
? null
: ref.read(snapModelProvider(snap.name).notifier).refresh,
child: snapModel.value?.activeChangeId != null
? Row(
children: [
SizedBox.square(
dimension: kCircularProgressIndicatorHeight,
child: YaruCircularProgressIndicator(
value: change?.progress,
strokeWidth: 2,
),
),
if (change != null) ...[
const SizedBox(width: 8),
Text(
change.localize(l10n) ?? '',
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
]
],
)
: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(YaruIcons.download),
const SizedBox(width: 8),
Text(
l10n.snapActionUpdateLabel,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
);
},
],
),
),
MenuAnchor(
menuChildren: [
Expand Down Expand Up @@ -575,9 +570,18 @@ class _ManageSnapTile extends ConsumerWidget {
],
);
}
}

class _ButtonBarForOpen extends ConsumerWidget {
const _ButtonBarForOpen(this.snap);

final Snap snap;

@override
Widget build(BuildContext context, WidgetRef ref) {
final snapLauncher = ref.watch(launchProvider(snap));
final l10n = AppLocalizations.of(context);

ButtonBar buildButtonBarForOpen(WidgetRef ref, AppLocalizations l10n,
SnapLauncher snapLauncher, BuildContext context) {
return ButtonBar(
mainAxisSize: MainAxisSize.min,
children: [
Expand All @@ -588,7 +592,8 @@ class _ManageSnapTile extends ConsumerWidget {
visible: snapLauncher.isLaunchable,
child: OutlinedButton(
style: const ButtonStyle(
padding: MaterialStatePropertyAll(EdgeInsets.zero)),
padding: MaterialStatePropertyAll(EdgeInsets.zero),
),
onPressed: snapLauncher.open,
child: Text(
l10n.snapActionOpenLabel,
Expand Down
11 changes: 11 additions & 0 deletions packages/app_center/lib/src/providers/error_stream_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import 'dart:async';

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:ubuntu_service/ubuntu_service.dart';

/// Used to listen to incoming errors to show them to the user.
final errorStreamProvider = StreamProvider<Exception>(
(ref) => getService<ErrorStreamController>().stream,
);

typedef ErrorStreamController = StreamController<Exception>;
15 changes: 15 additions & 0 deletions packages/app_center/lib/src/providers/file_system_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import 'dart:io';

import 'package:file/file.dart';
import 'package:file/local.dart';
import 'package:file/memory.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

/// Provides the correct file system to use for the environment.
/// The file system is a [MemoryFileSystem] when running widget tests and a
/// [LocalFileSystem] otherwise.
final fileSystemProvider = Provider<FileSystem>(
(_) => Platform.environment.containsKey('FLUTTER_TEST')
? MemoryFileSystem()
: const LocalFileSystem(),
);
Loading
Loading