Skip to content

Commit 88ba6e9

Browse files
SERDUNclaude
andcommitted
fix(settings): add optimistic toggle with spinner for register status switch
Replaces the blocking register status toggle with an optimistic update pattern — the switch flips immediately, shows a spinner, disables during the API call, and reverts on failure. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent f3c53a1 commit 88ba6e9

3 files changed

Lines changed: 28 additions & 15 deletions

File tree

lib/features/register_status/cubit/register_status_cubit.dart

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,19 @@ import 'package:webtrit_phone/repositories/repositories.dart';
88

99
final _logger = Logger('RegisterStatusCubit');
1010

11+
class RegisterStatus {
12+
const RegisterStatus({required this.value, this.isUpdating = false});
13+
14+
final bool value;
15+
final bool isUpdating;
16+
17+
RegisterStatus copyWith({bool? value, bool? isUpdating}) =>
18+
RegisterStatus(value: value ?? this.value, isUpdating: isUpdating ?? this.isUpdating);
19+
}
20+
1121
class RegisterStatusCubit extends Cubit<RegisterStatus> {
1222
RegisterStatusCubit(this.appRepository, this.registerStatusRepository, {this.handleError})
13-
: super(registerStatusRepository.getRegisterStatus()) {
23+
: super(RegisterStatus(value: registerStatusRepository.getRegisterStatus())) {
1424
fetchStatus();
1525
_connectivitySub = Connectivity().onConnectivityChanged.listen(_handleConnectivity);
1626
}
@@ -29,20 +39,22 @@ class RegisterStatusCubit extends Cubit<RegisterStatus> {
2939
try {
3040
final status = await appRepository.getRegisterStatus();
3141
registerStatusRepository.setRegisterStatus(status);
32-
emit(status);
42+
emit(RegisterStatus(value: status));
3343
} catch (e, s) {
3444
_logger.warning('Failed to get register status', e, s);
3545
handleError?.call(e, s);
3646
}
3747
}
3848

3949
Future<void> setStatus(bool value) async {
50+
emit(RegisterStatus(value: value, isUpdating: true));
4051
try {
4152
await appRepository.setRegisterStatus(value);
4253
await registerStatusRepository.setRegisterStatus(value);
43-
emit(value);
54+
emit(RegisterStatus(value: value, isUpdating: false));
4455
} catch (e, stackTrace) {
4556
_logger.warning('_onRegisterStatusChanged', e, stackTrace);
57+
emit(RegisterStatus(value: !value, isUpdating: false));
4658
handleError?.call(e, stackTrace);
4759
}
4860
}
@@ -53,5 +65,3 @@ class RegisterStatusCubit extends Cubit<RegisterStatus> {
5365
return super.close();
5466
}
5567
}
56-
57-
typedef RegisterStatus = bool;

lib/features/settings/view/settings_screen.dart

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,16 @@ class SettingsScreen extends StatelessWidget {
8686
context.l10n.settings_ListViewTileTitle_registered,
8787
style: effectiveStyle?.itemTextStyle,
8888
),
89-
value: registerState,
90-
onChanged: (value) => _onRegisterStatusChanged(context, value),
91-
secondary: Icon(
92-
Icons.account_circle_outlined,
93-
color: effectiveStyle?.userIconColor ?? effectiveStyle?.leadingIconsColor,
94-
),
89+
value: registerState.value,
90+
onChanged: registerState.isUpdating
91+
? null
92+
: (value) => _onRegisterStatusChanged(context, value),
93+
secondary: registerState.isUpdating
94+
? const SizedCircularProgressIndicator(size: 24, strokeWidth: 2)
95+
: Icon(
96+
Icons.account_circle_outlined,
97+
color: effectiveStyle?.userIconColor ?? effectiveStyle?.leadingIconsColor,
98+
),
9599
),
96100
),
97101
if (showSeparators) const ListTileSeparator(),

screenshots/lib/mocks/mock_register_status_cubit.dart

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,15 @@ import 'package:bloc_test/bloc_test.dart';
22

33
import 'package:webtrit_phone/features/features.dart';
44

5-
class MockRegisterStatusCubit extends MockCubit<RegisterStatus>
6-
implements RegisterStatusCubit {
5+
class MockRegisterStatusCubit extends MockCubit<RegisterStatus> implements RegisterStatusCubit {
76
MockRegisterStatusCubit();
87

9-
factory MockRegisterStatusCubit.initial(RegisterStatus initialStatus) {
8+
factory MockRegisterStatusCubit.initial(bool initialValue) {
109
final mock = MockRegisterStatusCubit();
1110
whenListen(
1211
mock,
1312
const Stream<RegisterStatus>.empty(),
14-
initialState: initialStatus,
13+
initialState: RegisterStatus(value: initialValue),
1514
);
1615
return mock;
1716
}

0 commit comments

Comments
 (0)