fix(mobile): temporarily disable age gating (#7708)

Disable the mobile launch age gate to restore access for users locked
out after updating. The production provider now allows access
immediately without calling the platform age API, while preserving the
existing push snapshot restoration path.

This is a temporary disable of age enforcement on both mobile platforms.
The original gate implementation remains available for a separate
follow-up.

Signed-off-by: Tom Brow <tomb@block.xyz>
This commit is contained in:
Tom Brow
2026-09-17 09:30:20 -07:00
committed by GitHub
parent 4c2086c084
commit d1cae93c1a
3 changed files with 94 additions and 71 deletions

View File

@@ -252,5 +252,15 @@ class AgeSignalNotifier extends Notifier<AgeSignalState> {
}
final ageSignalProvider = NotifierProvider<AgeSignalNotifier, AgeSignalState>(
AgeSignalNotifier.new,
_DisabledAgeSignalNotifier.new,
);
// Temporarily disable age blocking: platform age checks can lock users out of
// the app. Keep the allowed transition so push snapshots are restored normally.
class _DisabledAgeSignalNotifier extends AgeSignalNotifier {
@override
AgeSignalState build() => AgeSignalState.allowed;
@override
Future<void> request() async {}
}

View File

@@ -1,5 +1,3 @@
import 'dart:async';
import 'package:buzz/app.dart';
import 'package:buzz/features/age_gate/age_restriction_page.dart';
import 'package:buzz/features/age_gate/age_signal_push_bootstrap.dart';
@@ -98,75 +96,65 @@ void main() {
expect(badgeCounts.last, 0);
});
testWidgets('keeps app content unmounted until the signal resolves', (
tester,
) async {
final response = Completer<Object?>();
final relaySession = _CountingRelaySessionNotifier();
var requests = 0;
var snapshotSuspensions = 0;
var snapshotRestorations = 0;
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(ageSignalChannel, (call) {
requests += 1;
return response.future;
});
SharedPreferences.setMockInitialValues({});
final prefs = await SharedPreferences.getInstance();
testWidgets(
'opens app and restores push snapshots without a native age check',
(tester) async {
final relaySession = _CountingRelaySessionNotifier();
var requests = 0;
var snapshotSuspensions = 0;
var snapshotRestorations = 0;
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(ageSignalChannel, (call) {
requests += 1;
throw PlatformException(code: 'unavailable');
});
SharedPreferences.setMockInitialValues({});
final prefs = await SharedPreferences.getInstance();
await tester.pumpWidget(
ProviderScope(
overrides: [
authProvider.overrideWith(() => _AuthenticatedAuthNotifier()),
relaySessionProvider.overrideWith(() => relaySession),
suspendCommunitySnapshotForAgeCheckProvider.overrideWithValue(
() async {
snapshotSuspensions += 1;
if (snapshotSuspensions == 1) {
throw StateError('injected suspension failure');
}
},
),
resumeCommunitySnapshotAfterAgeCheckProvider.overrideWithValue(
() async {
snapshotRestorations += 1;
if (snapshotRestorations == 1) {
throw StateError('injected restoration failure');
}
},
),
ageSignalPushSnapshotRetryWaitProvider.overrideWithValue(
(_) async {},
),
savedPrefsProvider.overrideWithValue(prefs),
],
child: const AgeSignalPushBootstrap(child: App()),
),
);
await tester.pumpWidget(
ProviderScope(
overrides: [
authProvider.overrideWith(() => _AuthenticatedAuthNotifier()),
relaySessionProvider.overrideWith(() => relaySession),
suspendCommunitySnapshotForAgeCheckProvider.overrideWithValue(
() async {
snapshotSuspensions += 1;
if (snapshotSuspensions == 1) {
throw StateError('injected suspension failure');
}
},
),
resumeCommunitySnapshotAfterAgeCheckProvider.overrideWithValue(
() async {
snapshotRestorations += 1;
if (snapshotRestorations == 1) {
throw StateError('injected restoration failure');
}
},
),
ageSignalPushSnapshotRetryWaitProvider.overrideWithValue(
(_) async {},
),
savedPrefsProvider.overrideWithValue(prefs),
],
child: const AgeSignalPushBootstrap(child: App()),
),
);
expect(requests, 1);
expect(find.bySemanticsLabel('Checking age eligibility'), findsOneWidget);
expect(find.byType(HomePage), findsNothing);
expect(find.byType(Navigator), findsNothing);
expect(relaySession.builds, 0);
await tester.pump();
await tester.pump();
expect(snapshotSuspensions, 2);
expect(snapshotRestorations, 0);
await tester.pump();
await tester.pump();
expect(snapshotSuspensions, 0);
response.complete({'status': 'noSignal', 'ageUpper': null});
await tester.pump();
await tester.pump();
expect(find.bySemanticsLabel('Checking age eligibility'), findsNothing);
expect(find.byType(HomePage), findsOneWidget);
expect(find.byType(Navigator), findsOneWidget);
expect(relaySession.builds, 1);
expect(requests, 1);
await tester.pump();
await tester.pump();
expect(snapshotRestorations, 2);
});
expect(find.bySemanticsLabel('Checking age eligibility'), findsNothing);
expect(find.byType(HomePage), findsOneWidget);
expect(find.byType(Navigator), findsOneWidget);
expect(relaySession.builds, 1);
expect(requests, 0);
await tester.pump();
await tester.pump();
expect(snapshotRestorations, 2);
},
);
testWidgets('offers a retry after the native age check fails', (
tester,

View File

@@ -13,6 +13,27 @@ void main() {
.setMockMethodCallHandler(ageSignalChannel, null);
});
test(
'production launch allows access without requesting a native age signal',
() async {
var nativeCalls = 0;
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(ageSignalChannel, (_) async {
nativeCalls += 1;
throw PlatformException(code: 'unavailable');
});
final container = ProviderContainer();
addTearDown(container.dispose);
expect(container.read(ageSignalProvider), AgeSignalState.allowed);
await container.read(ageSignalProvider.notifier).request();
await container.read(ageSignalProvider.notifier).request();
expect(container.read(ageSignalProvider), AgeSignalState.allowed);
expect(nativeCalls, 0);
},
);
Future<AgeSignalState> requestWithResponse(Object? response) async {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(ageSignalChannel, (call) async {
@@ -20,7 +41,9 @@ void main() {
expect(call.arguments, isNull);
return response;
});
final container = ProviderContainer();
final container = ProviderContainer(
overrides: [ageSignalProvider.overrideWith(AgeSignalNotifier.new)],
);
addTearDown(container.dispose);
await container.read(ageSignalProvider.notifier).request();
@@ -506,7 +529,9 @@ void main() {
requests += 1;
return {'status': 'noSignal', 'ageUpper': null};
});
final container = ProviderContainer();
final container = ProviderContainer(
overrides: [ageSignalProvider.overrideWith(AgeSignalNotifier.new)],
);
addTearDown(container.dispose);
final notifier = container.read(ageSignalProvider.notifier);