Skip to content

Commit

Permalink
Add Landscape skip page
Browse files Browse the repository at this point in the history
This is quite a large commit since there are a lot of moving pieces. The existing Landscape page needs its skip functionality removed, the skip page uses an updated version of copy, the skip page uses an entirely new layout, etc.

I plan on expanding the use of reusable navigation and other layout widgets since the new designs will be much more consistent across pages.
  • Loading branch information
ashuntu committed Dec 5, 2024
1 parent 9aca9a1 commit 4d40517
Show file tree
Hide file tree
Showing 10 changed files with 243 additions and 26 deletions.
3 changes: 3 additions & 0 deletions gui/packages/ubuntupro/lib/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'core/agent_connection.dart';
import 'core/agent_monitor.dart';
import 'core/settings.dart';
import 'pages/landscape/landscape_page.dart';
import 'pages/landscape_skip/landscape_skip_page.dart';
import 'pages/startup/startup_page.dart';
import 'pages/subscribe_now/subscribe_now_page.dart';
import 'pages/subscription_status/subscription_status_page.dart';
Expand Down Expand Up @@ -76,6 +77,8 @@ class Pro4WSLApp extends StatelessWidget {
},
),
if (settings.isLandscapeConfigurationEnabled) ...{
Routes.skipLandscape:
WizardRoute(builder: (_) => const LandscapeSkipPage()),
Routes.configureLandscape:
const WizardRoute(builder: LandscapePage.create),
Routes.subscriptionStatus: WizardRoute(
Expand Down
7 changes: 6 additions & 1 deletion gui/packages/ubuntupro/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,12 @@
"purchaseStatusServer":"Something went wrong with Microsoft Store. Please try again later.",
"purchaseStatusUnknown": "Unknown error when trying to purchase the subscription. Consider restarting this app.",

"landscapeHeading": "Configure the connection to {landscapeLink} to manage your Ubuntu WSL instances remotely.",
"landscapeSkip": "Skip for now",
"landscapeSkipDescription": "You can always configure Landscape later",
"landscapeSkipRegister": "Register with Landscape",

"landscapeTitle": "Landscape",
"landscapeHeading": "Configure the connection to Landscape to manage your Ubuntu WSL instances remotely. {landscapeLink}",
"@landscapeHeading": {
"placeholders": {
"landscapeLink": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class LandscapeModel extends ChangeNotifier {
LandscapeModel(this.client);

/// The URL to be shown in the UI.
final landscapeURI = Uri.https('ubuntu.com', '/landscape');
static final landscapeURI = Uri.https('ubuntu.com', '/landscape');

/// Whether the current form is complete (ready to be submitted).
bool get isComplete => _active.isComplete;
Expand Down
26 changes: 3 additions & 23 deletions gui/packages/ubuntupro/lib/pages/landscape/landscape_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ class LandscapePage extends StatelessWidget {
super.key,
required this.onApplyConfig,
required this.onBack,
required this.onSkip,
});

/// Callable invoked when this page successfully applies the configuration.
Expand All @@ -34,9 +33,6 @@ class LandscapePage extends StatelessWidget {
/// Callable invoked when the user navigates back.
final void Function() onBack;

/// Callable invoked when the user skips this page.
final void Function() onSkip;

@override
Widget build(BuildContext context) {
final lang = AppLocalizations.of(context);
Expand All @@ -49,21 +45,17 @@ class LandscapePage extends StatelessWidget {
),
),
),
).copyWith(
a: const TextStyle(
decoration: TextDecoration.underline,
),
);

return LandingPage(
svgAsset: 'assets/Landscape-tag.svg',
title: 'Landscape',
title: lang.landscapeTitle,
children: [
// Only rebuilds if the value of model.landscapeURI changes (never in production)
Selector<LandscapeModel, Uri>(
selector: (_, model) => model.landscapeURI,
selector: (_, model) => LandscapeModel.landscapeURI,
builder: (context, uri, _) => MarkdownBody(
data: lang.landscapeHeading('[Landscape]($uri)'),
data: lang.landscapeHeading('[${lang.learnMore}]($uri)'),
onTapLink: (_, href, __) => launchUrl(uri),
styleSheet: linkStyle,
),
Expand All @@ -79,7 +71,6 @@ class LandscapePage extends StatelessWidget {
selector: (_, model) => model.isComplete,
builder: (context, isComplete, _) => _NavigationButtonRow(
onBack: onBack,
onSkip: onSkip,
onNext: isComplete ? () => _tryApplyConfig(context) : null,
),
),
Expand Down Expand Up @@ -114,13 +105,11 @@ class LandscapePage extends StatelessWidget {
landscapePage = LandscapePage(
onApplyConfig: Wizard.of(context).back,
onBack: Wizard.of(context).back,
onSkip: Wizard.of(context).back,
);
} else {
landscapePage = LandscapePage(
onApplyConfig: Wizard.of(context).next,
onBack: Wizard.of(context).back,
onSkip: Wizard.of(context).next,
);
}

Expand Down Expand Up @@ -242,12 +231,10 @@ class LandscapeConfigForm extends StatelessWidget {
class _NavigationButtonRow extends StatelessWidget {
const _NavigationButtonRow({
this.onBack,
this.onSkip,
this.onNext,
});

final void Function()? onBack;
final void Function()? onSkip;
final void Function()? onNext;

@override
Expand All @@ -261,13 +248,6 @@ class _NavigationButtonRow extends StatelessWidget {
child: Text(lang.buttonBack),
),
const Spacer(),
FilledButton(
onPressed: onSkip,
child: Text(lang.buttonSkip),
),
const SizedBox(
width: 16.0,
),
ElevatedButton(
onPressed: onNext,
child: Text(lang.buttonNext),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
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.dart';
import 'package:wizard_router/wizard_router.dart';

import '../../routes.dart';
import '../landscape/landscape_model.dart';
import '../widgets/navigation_row.dart';
import '../widgets/page_widgets.dart';
import '../widgets/radio_tile.dart';

enum _SkipEnum {
skip,
register,
}

class LandscapeSkipPage extends StatefulWidget {
const LandscapeSkipPage({super.key});

@override
State<LandscapeSkipPage> createState() => _LandscapeSkipPageState();
}

class _LandscapeSkipPageState extends State<LandscapeSkipPage> {
_SkipEnum groupValue = _SkipEnum.skip;

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

return ColumnPage(
svgAsset: 'assets/Landscape-tag.svg',
title: lang.landscapeTitle,
left: [
MarkdownBody(
data: lang.landscapeHeading(
'[${lang.learnMore}](${LandscapeModel.landscapeURI})',
),
onTapLink: (_, href, __) => launchUrl(LandscapeModel.landscapeURI),
),
],
right: [
RadioTile(
value: _SkipEnum.skip,
title: lang.landscapeSkip,
subtitle: lang.landscapeSkipDescription,
groupValue: groupValue,
onChanged: (v) => setState(() {
groupValue = v!;
}),
),
RadioTile(
value: _SkipEnum.register,
title: lang.landscapeSkipRegister,
groupValue: groupValue,
onChanged: (v) => setState(() {
groupValue = v!;
}),
),
],
navigationRow: NavigationRow(
onBack: wizard.back,
onNext: () {
switch (groupValue) {
case _SkipEnum.skip:
wizard.jump(Routes.subscriptionStatus);
case _SkipEnum.register:
wizard.next();
}
},
),
);
}
}
27 changes: 27 additions & 0 deletions gui/packages/ubuntupro/lib/pages/widgets/navigation_row.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import 'package:flutter/material.dart';

class NavigationRow extends StatelessWidget {
const NavigationRow({
required this.onBack,
required this.onNext,
this.backText,
this.nextText,
super.key,
});

final void Function()? onBack;
final String? backText;
final void Function()? onNext;
final String? nextText;

@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
OutlinedButton(onPressed: onBack, child: Text(backText ?? 'Back')),
FilledButton(onPressed: onNext, child: Text(nextText ?? 'Next')),
],
);
}
}
89 changes: 89 additions & 0 deletions gui/packages/ubuntupro/lib/pages/widgets/page_widgets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:yaru/yaru.dart';

import 'navigation_row.dart';
import 'status_bar.dart';

/// The simplest material page that covers most of the use cases in this app,
Expand Down Expand Up @@ -168,3 +169,91 @@ class _PageContent extends StatelessWidget {
);
}
}

/// 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
/// width below both columns.
class ColumnPage extends StatelessWidget {
const ColumnPage({
required this.left,
required this.right,
this.svgAsset = 'assets/Ubuntu-tag.svg',
this.title = 'Ubuntu Pro',
this.navigationRow,
super.key,
});

final List<Widget> left;
final List<Widget> right;
final String svgAsset;
final String title;
final NavigationRow? navigationRow;

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

return Pro4WSLPage(
body: Padding(
padding: const EdgeInsets.fromLTRB(32.0, 32.0, 32.0, 32.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// Left column
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
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: 24),
...left,
],
),
),
// Spacer
const SizedBox(width: 32),
// Right column
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: right,
),
),
],
),
),
if (navigationRow != null) navigationRow!,
],
),
),
);
}
}
37 changes: 37 additions & 0 deletions gui/packages/ubuntupro/lib/pages/widgets/radio_tile.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import 'package:flutter/material.dart';
import 'package:yaru/yaru.dart';

class RadioTile<T> extends StatelessWidget {
const RadioTile({
required this.value,
required this.title,
required this.groupValue,
required this.onChanged,
this.subtitle,
super.key,
});

final String title;
final String? subtitle;
final T value, groupValue;
final Function(T?)? onChanged;

@override
Widget build(BuildContext context) {
// Adds a nice visual clue that the tile is selected.
return YaruSelectableContainer(
selected: groupValue == value,
selectionColor: Theme.of(context).colorScheme.tertiaryContainer,
child: YaruRadioListTile(
contentPadding: EdgeInsets.zero,
visualDensity: VisualDensity.comfortable,
dense: true,
title: Text(title),
subtitle: subtitle != null ? Text(subtitle!) : null,
value: value,
groupValue: groupValue,
onChanged: onChanged,
),
);
}
}
1 change: 1 addition & 0 deletions gui/packages/ubuntupro/lib/routes.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
abstract class Routes {
static const startup = '/startup';
static const subscribeNow = '/subscribe-now';
static const skipLandscape = '/skip-landscape';
static const configureLandscape = '/configure-landscape';
static const subscriptionStatus = '/subscription-status';
static const configureLandscapeLate = '/configure-landscape-late';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,6 @@ Widget buildApp(
return buildSingleRouteMultiProviderApp(
child: LandscapePage(
onApplyConfig: () {},
onSkip: () {},
onBack: () {},
),
providers: [ChangeNotifierProvider<LandscapeModel>.value(value: model)],
Expand Down

0 comments on commit 4d40517

Please sign in to comment.