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

fix: Reorganize subscription status #1017

Merged
merged 6 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
4 changes: 2 additions & 2 deletions gui/packages/ubuntupro/end_to_end/end_to_end_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Future<void> testOrganizationProvidedToken(WidgetTester tester) async {

// asserts that we transitioned to the organization-managed status page.
final l10n = tester.l10n<SubscriptionStatusPage>();
expect(find.text(l10n.orgManaged), findsOneWidget);
expect(find.text(l10n.manageUbuntuPro), findsNothing);
}

Future<void> testManualTokenInput(WidgetTester tester) async {
Expand Down Expand Up @@ -99,5 +99,5 @@ Future<void> testPurchase(WidgetTester tester) async {

// asserts that we transitioned to the store-managed status page.
l10n = tester.l10n<SubscriptionStatusPage>();
expect(find.text(l10n.storeManaged), findsOneWidget);
expect(find.text(l10n.manageUbuntuPro), findsOneWidget);
}
15 changes: 3 additions & 12 deletions gui/packages/ubuntupro/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,9 @@
"getUbuntuPro": "Get Ubuntu Pro",
"learnMore": "Learn more",

"subscriptionIsActive": "Your subscription is active!",
"storeManaged": "This subscription is managed through Microsoft Store.",
"manageSubscription": "Manage your subscription",
"orgManaged": "This subscription is owned and managed by your organization.",
"manuallyManaged": "Visit {proDashboardLink} to manage your subscription.",
"@manuallyManaged": {
"placeholders": {
"proDashboardLink": {
"type": "String"
}
}
},
"ubuntuProEnabled": "Ubuntu Pro is enabled on this machine.",
"ubuntuProEnabledInfo": "All Ubuntu WSL instances have access to Ubuntu Pro security features.",
"manageUbuntuPro": "Manage Ubuntu Pro subscription",
"detachPro": "Detach Ubuntu Pro",

"updatingSubscriptionInfo": "Updating the subscription information.",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import 'package:agentapi/agentapi.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:provider/provider.dart';
import 'package:ubuntu_service/ubuntu_service.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:wizard_router/wizard_router.dart';
import 'package:yaru/yaru.dart';

Expand All @@ -24,19 +26,17 @@ class SubscriptionStatusPage extends StatelessWidget {
duration: const Duration(milliseconds: 700),
child: switch (model) {
StoreSubscriptionStatusModel() => SubscriptionStatus(
caption: lang.storeManaged,
actionButtons: [
if (model.canConfigureLandscape) _landscapeButton(context),
OutlinedButton(
onPressed: model.launchManagementWebPage,
child: Text(lang.manageSubscription),
],
footerLinks: [
MarkdownBody(
data: '[${lang.manageUbuntuPro}]()',
onTapLink: (_, href, __) => model.launchManagementWebPage(),
),
],
),
UserSubscriptionStatusModel() => SubscriptionStatus(
caption: lang.manuallyManaged(
'[ubuntu.com/pro/dashboard](https://ubuntu.com/pro/dashboard)',
),
actionButtons: [
if (model.canConfigureLandscape) _landscapeButton(context),
ElevatedButton(
Expand All @@ -59,9 +59,15 @@ class SubscriptionStatusPage extends StatelessWidget {
child: Text(lang.detachPro),
),
],
footerLinks: [
MarkdownBody(
data: '[${lang.manageUbuntuPro}]()',
onTapLink: (_, href, __) =>
launchUrlString('https://ubuntu.com/pro/dashboard'),
),
],
),
OrgSubscriptionStatusModel() => SubscriptionStatus(
caption: lang.orgManaged,
actionButtons: model.canConfigureLandscape
? [_landscapeButton(context)]
: null,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,56 +1,38 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:yaru/yaru.dart';
import '../widgets/page_widgets.dart';
import '/pages/widgets/page_widgets.dart';

/// A page content widget built on top of the Dark styled landing page showing the current user active subscription
/// feedback and an optional action button in a column layout.
class SubscriptionStatus extends StatelessWidget {
const SubscriptionStatus({
super.key,
required this.caption,
this.actionButtons,
this.footerLinks,
});

/// The caption to render below the active subscription subtitle.
final String caption;

/// The optional action button matching the capabilities of the current subscription type.
final List<Widget>? actionButtons;

final List<Widget>? footerLinks;

@override
Widget build(BuildContext context) {
final lang = AppLocalizations.of(context);

final theme = Theme.of(context);
final linkStyle = MarkdownStyleSheet.fromTheme(
theme.copyWith(
textTheme: theme.textTheme.copyWith(
bodyMedium: theme.textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w100,
),
),
),
).copyWith(
a: TextStyle(
decoration: TextDecoration.underline,
color: theme.colorScheme.onSurface,
),
);

return LandingPage(
centered: true,
return CenteredPage(
footer: footerLinks != null
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: footerLinks!,
)
: null,
children: [
const SizedBox(height: 16.0),
YaruInfoBox(
title: Text(lang.subscriptionIsActive),
subtitle: MarkdownBody(
data: caption,
onTapLink: (_, href, __) => launchUrlString(href!),
styleSheet: linkStyle,
),
title: Text(lang.ubuntuProEnabled),
subtitle: Text(lang.ubuntuProEnabledInfo),
yaruInfoType: YaruInfoType.success,
),
if (actionButtons != null)
Expand Down
67 changes: 67 additions & 0 deletions gui/packages/ubuntupro/lib/pages/widgets/page_widgets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,73 @@ class _PageContent extends StatelessWidget {
}
}

class CenteredPage extends StatelessWidget {
const CenteredPage({
super.key,
required this.children,
this.svgAsset = 'assets/Ubuntu-tag.svg',
this.title = 'Ubuntu Pro',
this.footer,
});

final List<Widget> children;
final Widget? footer;
final String svgAsset;
final String title;

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);

return Pro4WSLPage(
body: Padding(
padding: const EdgeInsets.fromLTRB(32.0, 24.0, 32.0, 32.0),
child: Column(
children: [
Expanded(
child: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 540.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RichText(
text: TextSpan(
children: [
WidgetSpan(
child: SvgPicture.asset(
svgAsset,
height: 70,
),
),
const WidgetSpan(
child: SizedBox(
width: 8,
),
),
TextSpan(
text: title,
style: theme.textTheme.displaySmall
?.copyWith(fontWeight: FontWeight.w100),
),
],
),
),
const SizedBox(height: 16),
...children,
],
),
),
),
),
if (footer != null) footer!,
],
),
),
);
}
}

/// Two-column, vertically centered page. The left column always contains the
/// svg image and title, with the left children below it. Both columns are equal
/// in width. Optionally, a [NavigationRow] may be provided that will span the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,59 @@ import 'package:ubuntu_service/ubuntu_service.dart';
import 'package:ubuntupro/core/agent_api_client.dart';
import 'package:ubuntupro/pages/subscription_status/subscription_status_model.dart';
import 'package:ubuntupro/pages/subscription_status/subscription_status_page.dart';
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
import 'package:wizard_router/wizard_router.dart';

import '../../utils/build_multiprovider_app.dart';
import '../../utils/url_launcher_mock.dart';

void main() {
group('subscription info:', () {
final client = FakeAgentApiClient();
registerServiceInstance<AgentApiClient>(client);
final info = SubscriptionInfo();

group('footer links:', () {
final landscape = LandscapeSource()..ensureOrganization();
testWidgets('user', (tester) async {
CarlosNihelton marked this conversation as resolved.
Show resolved Hide resolved
final launcher = FakeUrlLauncher();
UrlLauncherPlatform.instance = launcher;

info.ensureUser();
final app = buildApp(info, landscape, client);

await tester.pumpWidget(app);

final context = tester.element(find.byType(SubscriptionStatusPage));
final lang = AppLocalizations.of(context);

expect(launcher.launched, isFalse);
await tester
.tapOnText(find.textRange.ofSubstring(lang.manageUbuntuPro));
await tester.pump();
expect(launcher.launched, isTrue);
});

testWidgets('store', (tester) async {
final launcher = FakeUrlLauncher();
UrlLauncherPlatform.instance = launcher;

info.ensureMicrosoftStore();
final app = buildApp(info, landscape, client);

await tester.pumpWidget(app);

final context = tester.element(find.byType(SubscriptionStatusPage));
final lang = AppLocalizations.of(context);

expect(launcher.launched, isFalse);
await tester
.tapOnText(find.textRange.ofSubstring(lang.manageUbuntuPro));
await tester.pump();
expect(launcher.launched, isTrue);
});
});

group('org landscape:', () {
final landscape = LandscapeSource()..ensureOrganization();
testWidgets('user', (tester) async {
Expand All @@ -27,6 +71,7 @@ void main() {
final context = tester.element(find.byType(SubscriptionStatusPage));
final lang = AppLocalizations.of(context);

expect(find.text(lang.manageUbuntuPro), findsOneWidget);
expect(find.text(lang.detachPro), findsOneWidget);
expect(find.text(lang.landscapeConfigureButton), findsNothing);
});
Expand All @@ -40,7 +85,7 @@ void main() {
final context = tester.element(find.byType(SubscriptionStatusPage));
final lang = AppLocalizations.of(context);

expect(find.text(lang.manageSubscription), findsOneWidget);
expect(find.text(lang.manageUbuntuPro), findsOneWidget);
expect(find.text(lang.landscapeConfigureButton), findsNothing);
});

Expand All @@ -53,10 +98,11 @@ void main() {
final context = tester.element(find.byType(SubscriptionStatusPage));
final lang = AppLocalizations.of(context);

expect(find.text(lang.orgManaged), findsOneWidget);
expect(find.text(lang.manageUbuntuPro), findsNothing);
expect(find.text(lang.landscapeConfigureButton), findsNothing);
});
});

group('landscape:', () {
testWidgets('user', (tester) async {
final landscape = LandscapeSource()..ensureNone();
Expand All @@ -68,6 +114,7 @@ void main() {
final context = tester.element(find.byType(SubscriptionStatusPage));
final lang = AppLocalizations.of(context);

expect(find.text(lang.manageUbuntuPro), findsOneWidget);
expect(find.text(lang.detachPro), findsOneWidget);
expect(find.text(lang.landscapeConfigureButton), findsOneWidget);
});
Expand All @@ -82,7 +129,7 @@ void main() {
final context = tester.element(find.byType(SubscriptionStatusPage));
final lang = AppLocalizations.of(context);

expect(find.text(lang.manageSubscription), findsOneWidget);
expect(find.text(lang.manageUbuntuPro), findsOneWidget);
expect(find.text(lang.landscapeConfigureButton), findsOneWidget);
});

Expand All @@ -96,7 +143,7 @@ void main() {
final context = tester.element(find.byType(SubscriptionStatusPage));
final lang = AppLocalizations.of(context);

expect(find.text(lang.orgManaged), findsOneWidget);
expect(find.text(lang.manageUbuntuPro), findsNothing);
expect(find.text(lang.landscapeConfigureButton), findsOneWidget);
});
});
Expand All @@ -117,6 +164,7 @@ void main() {
final context = tester.element(find.byType(SubscriptionStatusPage));
final lang = AppLocalizations.of(context);

expect(find.text(lang.manageUbuntuPro), findsOneWidget);
expect(find.text(lang.detachPro), findsOneWidget);
expect(find.text(lang.landscapeConfigureButton), findsNothing);
});
Expand All @@ -136,7 +184,7 @@ void main() {
final context = tester.element(find.byType(SubscriptionStatusPage));
final lang = AppLocalizations.of(context);

expect(find.text(lang.manageSubscription), findsOneWidget);
expect(find.text(lang.manageUbuntuPro), findsOneWidget);
expect(find.text(lang.landscapeConfigureButton), findsNothing);
});

Expand All @@ -155,7 +203,7 @@ void main() {
final context = tester.element(find.byType(SubscriptionStatusPage));
final lang = AppLocalizations.of(context);

expect(find.text(lang.orgManaged), findsOneWidget);
expect(find.text(lang.manageUbuntuPro), findsNothing);
expect(find.text(lang.landscapeConfigureButton), findsNothing);
});
});
Expand Down
Loading
Loading