mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-10-19 14:07:26 +02:00
fix(network): discard stale folder browsing responses
This commit is contained in:
1 parent
7057b0c349
commit
92e3fa94ec
2 files changed
+87
-4
No files matched your search
@@ -46,6 +46,7 @@ class _NetworkStorageScreenState extends ConsumerState<NetworkStorageScreen> {
|
||||
bool _adding = false;
|
||||
bool _added = false;
|
||||
bool _selectingDestination = false;
|
||||
int _loadGeneration = 0;
|
||||
|
||||
Future<void> _useForDownloads() async {
|
||||
final connection = widget.connection;
|
||||
@@ -123,7 +124,20 @@ class _NetworkStorageScreenState extends ConsumerState<NetworkStorageScreen> {
|
||||
_load();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant NetworkStorageScreen oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.connection != widget.connection ||
|
||||
oldWidget.path != widget.path ||
|
||||
oldWidget.service != widget.service) {
|
||||
_added = false;
|
||||
_load();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _load() async {
|
||||
final generation = ++_loadGeneration;
|
||||
bool isCurrent() => mounted && generation == _loadGeneration;
|
||||
setState(() {
|
||||
_loading = true;
|
||||
_failed = false;
|
||||
@@ -132,18 +146,18 @@ class _NetworkStorageScreenState extends ConsumerState<NetworkStorageScreen> {
|
||||
final connection = widget.connection;
|
||||
if (connection == null) {
|
||||
final connections = await service.connections();
|
||||
if (mounted) _connections = connections;
|
||||
if (isCurrent()) _connections = connections;
|
||||
} else {
|
||||
final entries = await service.list(connection, widget.path);
|
||||
if (mounted) _entries = entries;
|
||||
if (isCurrent()) _entries = entries;
|
||||
}
|
||||
} catch (error) {
|
||||
if (mounted) {
|
||||
if (isCurrent()) {
|
||||
_failed = true;
|
||||
_failure = classifyNetworkStorageError(error);
|
||||
}
|
||||
} finally {
|
||||
if (mounted) setState(() => _loading = false);
|
||||
if (isCurrent()) setState(() => _loading = false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
@@ -12,6 +13,59 @@ import 'package:spotiflac_android/theme/mornye_theme.dart';
|
||||
import 'package:spotiflac_android/widgets/app_action_button.dart';
|
||||
|
||||
void main() {
|
||||
for (final olderFails in [false, true]) {
|
||||
testWidgets('latest folder refresh wins (older fails: $olderFails)', (
|
||||
tester,
|
||||
) async {
|
||||
final service = _DelayedListings();
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
child: MaterialApp(
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
home: NetworkStorageScreen(
|
||||
service: service,
|
||||
connection: const NetworkConnection(
|
||||
id: 'nas',
|
||||
name: 'NAS',
|
||||
protocol: NetworkProtocol.webdav,
|
||||
address: 'https://nas.test/music/',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
service.requests.single.complete([
|
||||
const NetworkEntry('Initial.flac', 'Initial.flac'),
|
||||
]);
|
||||
await tester.pumpAndSettle();
|
||||
final refresh = find.byTooltip('Refresh');
|
||||
// Two taps before the disabled state is painted can overlap requests.
|
||||
await tester.tap(refresh);
|
||||
await tester.tap(refresh);
|
||||
expect(service.requests, hasLength(3));
|
||||
service.requests.last.complete([
|
||||
const NetworkEntry('Latest.flac', 'Latest.flac'),
|
||||
]);
|
||||
await tester.pumpAndSettle();
|
||||
if (olderFails) {
|
||||
service.requests[1].completeError(const SocketException('offline'));
|
||||
} else {
|
||||
service.requests[1].complete([
|
||||
const NetworkEntry('Stale.flac', 'Stale.flac'),
|
||||
]);
|
||||
}
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Latest.flac'), findsOneWidget);
|
||||
expect(find.text('Stale.flac'), findsNothing);
|
||||
expect(
|
||||
find.textContaining('The server could not be reached.'),
|
||||
findsNothing,
|
||||
);
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
}
|
||||
|
||||
testWidgets('failed sign-in stays on the form with a specific error', (
|
||||
tester,
|
||||
) async {
|
||||
@@ -243,3 +297,18 @@ class _NetworkFixture extends NetworkStorageService {
|
||||
: [const NetworkEntry('Album/Song.flac', 'Song.flac')];
|
||||
}
|
||||
}
|
||||
|
||||
class _DelayedListings extends NetworkStorageService {
|
||||
_DelayedListings() : super(read: () async => null, write: (_) async {});
|
||||
final requests = <Completer<List<NetworkEntry>>>[];
|
||||
|
||||
@override
|
||||
Future<List<NetworkEntry>> list(
|
||||
NetworkConnection connection, [
|
||||
String path = '',
|
||||
]) {
|
||||
final request = Completer<List<NetworkEntry>>();
|
||||
requests.add(request);
|
||||
return request.future;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user