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

feat(gui): Hide subscribe button udeng 1552 #339

Merged
merged 4 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ void main() {
// Use a random place inside the build tree as the `LOCALAPPDATA` env variable for all test cases below.
tmp = await msixRootDir().createTemp('test-');
Environment(
overrides: {'LOCALAPPDATA': tmp!.path},
overrides: {
'LOCALAPPDATA': tmp!.path,
'UP4W_ALLOW_STORE_PURCHASE': '1',
},
);
});

Expand Down
1 change: 1 addition & 0 deletions gui/packages/ubuntupro/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

"proHeading": "The most comprehensive subscription\nfor open-source software security\nnow available on WSL",
"subscribeNow": "Subscribe Now",
"subscribeNowTooltipDisabled": "Coming soon",
"learnMore": "Learn More",

"subscriptionIsActive": "Your subscription is active!",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,34 +27,41 @@ class SubscribeNowPage extends StatelessWidget {
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
ElevatedButton(
onPressed: () async {
final subs = await model.purchaseSubscription();
Tooltip(
message: model.purchaseAllowed()
? ''
: lang.subscribeNowTooltipDisabled,
child: ElevatedButton(
onPressed: model.purchaseAllowed()
? () async {
final subs = await model.purchaseSubscription();

// Using anything attached to the BuildContext after a suspension point might be tricky.
// Better check if it's still mounted in the widget tree.
if (!context.mounted) return;
// Using anything attached to the BuildContext after a suspension point might be tricky.
// Better check if it's still mounted in the widget tree.
if (!context.mounted) return;

subs.fold(
ifLeft: (status) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Center(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 2.0,
horizontal: 16.0,
),
child: Text(status.localize(lang)),
),
),
),
);
},
ifRight: onSubscriptionUpdate,
);
},
child: Text(lang.subscribeNow),
subs.fold(
ifLeft: (status) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Center(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 2.0,
horizontal: 16.0,
),
child: Text(status.localize(lang)),
),
),
),
);
},
ifRight: onSubscriptionUpdate,
);
}
: null,
CarlosNihelton marked this conversation as resolved.
Show resolved Hide resolved
child: Text(lang.subscribeNow),
),
),
const Padding(padding: EdgeInsets.only(right: 8.0)),
FilledButton.tonal(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:url_launcher/url_launcher.dart';

import '/core/agent_api_client.dart';
import '/core/pro_token.dart';
import '../../core/environment.dart';

/// A base class for the view-models that may represent different types of subscriptions and the optional actions they allow.
sealed class SubscriptionStatusModel {
Expand Down Expand Up @@ -69,6 +70,7 @@ class OrgSubscriptionStatusModel extends SubscriptionStatusModel {

class SubscribeNowModel extends SubscriptionStatusModel {
final AgentApiClient client;
bool? _isPurchaseAllowed;
SubscribeNowModel(this.client) : super._();

Future<SubscriptionInfo> applyProToken(ProToken token) async {
Expand Down Expand Up @@ -99,4 +101,12 @@ class SubscribeNowModel extends SubscriptionStatusModel {
return PurchaseStatus.unknown.left();
}
}

/// Returns true if the environment variable 'UP4W_ALLOW_STORE_PURCHASE' has been set.
/// Since this reading won't change during the app lifetime, even if the user changes
/// it's value from outside, the value is cached so we don't check the environment more than once.
bool purchaseAllowed() {
return _isPurchaseAllowed ??= ['true', '1', 'on']
.contains(Environment()['UP4W_ALLOW_STORE_PURCHASE']?.toLowerCase());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import 'token_samples.dart' as tks;
void main() {
testWidgets('launch web page', (tester) async {
final model = MockSubscribeNowModel();
when(model.purchaseAllowed()).thenReturn(true);
var called = false;
when(model.launchProWebPage()).thenAnswer((_) async {
called = true;
Expand All @@ -32,9 +33,44 @@ void main() {
await tester.pump();
expect(called, isTrue);
});
group('purchase button enabled by model', () {
testWidgets('disabled', (tester) async {
final model = MockSubscribeNowModel();
when(model.purchaseAllowed()).thenReturn(false);
final app = buildApp(model, (_) {});
await tester.pumpWidget(app);
final context = tester.element(find.byType(SubscribeNowPage));
final lang = AppLocalizations.of(context);

final button = find.byType(ElevatedButton);
// check that's the right button
expect(
find.descendant(of: button, matching: find.text(lang.subscribeNow)),
findsOneWidget,
);
expect(tester.widget<ElevatedButton>(button).enabled, isFalse);
});
testWidgets('enabled', (tester) async {
final model = MockSubscribeNowModel();
when(model.purchaseAllowed()).thenReturn(true);
final app = buildApp(model, (_) {});
await tester.pumpWidget(app);
final context = tester.element(find.byType(SubscribeNowPage));
final lang = AppLocalizations.of(context);

final button = find.byType(ElevatedButton);
// check that's the right button
expect(
find.descendant(of: button, matching: find.text(lang.subscribeNow)),
findsOneWidget,
);
expect(tester.widget<ElevatedButton>(button).enabled, isTrue);
});
});
group('subscribe', () {
testWidgets('calls back on success', (tester) async {
final model = MockSubscribeNowModel();
when(model.purchaseAllowed()).thenReturn(true);
var called = false;
when(model.purchaseSubscription()).thenAnswer((_) async {
final info = SubscriptionInfo()..ensureMicrosoftStore();
Expand All @@ -57,6 +93,7 @@ void main() {
testWidgets('feedback on error', (tester) async {
const purchaseError = PurchaseStatus.networkError;
final model = MockSubscribeNowModel();
when(model.purchaseAllowed()).thenReturn(true);
var called = false;
when(model.purchaseSubscription()).thenAnswer((_) async {
return purchaseError.left();
Expand All @@ -79,6 +116,7 @@ void main() {
});
testWidgets('feedback when applying token', (tester) async {
final model = MockSubscribeNowModel();
when(model.purchaseAllowed()).thenReturn(true);
when(model.applyProToken(any)).thenAnswer((_) async {
return SubscriptionInfo()..ensureUser();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,12 @@ class MockSubscribeNowModel extends _i1.Mock implements _i5.SubscribeNowModel {
)),
) as _i6
.Future<_i4.Either<_i8.PurchaseStatus, _i3.SubscriptionInfo>>);
@override
bool purchaseAllowed() => (super.noSuchMethod(
Invocation.method(
#purchaseAllowed,
[],
),
returnValue: false,
) as bool);
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ void main() {

final client = MockAgentApiClient();

test('disabled by default', () {
final model = SubscribeNowModel(client);
expect(model.purchaseAllowed(), isFalse);
});

test('expected failure', () async {
const expectedError = Left(PurchaseStatus.userGaveUp);
pluginMessenger.setMockMethodCallHandler(pluginChannel, (_) async {
Expand Down
Loading