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

Update Deep link validation UI #6544

Merged
merged 55 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
87ed955
1
hangyujin Mar 10, 2023
354462a
Merge branch 'master' of https://github.com/flutter/devtools into dee…
hangyujin Apr 27, 2023
139148e
http request
hangyujin May 2, 2023
5a8db98
Merge branch 'master' of https://github.com/flutter/devtools into dee…
hangyujin Jun 29, 2023
703b180
1
hangyujin Jul 27, 2023
3269474
Merge branch 'master' of https://github.com/flutter/devtools into dee…
hangyujin Jul 27, 2023
80160f1
1
hangyujin Aug 3, 2023
9d9f5f9
2
hangyujin Aug 3, 2023
5b64149
Update deep_links_screen.dart
hangyujin Aug 3, 2023
de2ecc6
2
hangyujin Aug 10, 2023
9d2892b
3
hangyujin Aug 10, 2023
d849f14
update datatable
hangyujin Aug 10, 2023
cfea32d
Update deep_links_model.dart
hangyujin Sep 21, 2023
63f9a2d
Update deep_links_screen.dart
hangyujin Sep 21, 2023
d3b4ea8
update
hangyujin Sep 21, 2023
808e07b
green
hangyujin Sep 21, 2023
ad82412
Update deep_links_screen.dart
hangyujin Sep 21, 2023
ce1dcc4
update
hangyujin Sep 27, 2023
d887fee
Update deep_links_screen.dart
hangyujin Sep 27, 2023
76daac3
1
hangyujin Sep 27, 2023
ea119c6
1
hangyujin Sep 28, 2023
4cbf046
Merge branch 'master' of https://github.com/flutter/devtools into dee…
hangyujin Sep 29, 2023
95c2633
update deep link validation tool ui
hangyujin Oct 17, 2023
ed245ee
Merge branch 'master' of https://github.com/flutter/devtools into dee…
hangyujin Oct 17, 2023
d95318c
more ui
hangyujin Oct 24, 2023
4064cc2
Merge branch 'master' of https://github.com/flutter/devtools into dee…
hangyujin Oct 24, 2023
8c26270
2
hangyujin Oct 25, 2023
94325b5
Update app.dart
hangyujin Oct 25, 2023
4b2591d
Update deep_links_screen.dart
hangyujin Oct 25, 2023
2917aae
small fix
hangyujin Oct 25, 2023
ef2f30c
Update deep_link_list_view.dart
hangyujin Oct 25, 2023
eda157f
fix
hangyujin Oct 25, 2023
4cca4e3
lint
hangyujin Oct 25, 2023
d2f1a73
ui updates
hangyujin Nov 1, 2023
3ad6511
Merge branch 'master' of https://github.com/flutter/devtools into dee…
hangyujin Nov 1, 2023
3d53bcb
update more UI
hangyujin Nov 7, 2023
2b13844
1
hangyujin Nov 7, 2023
5fc40b5
rename
hangyujin Nov 7, 2023
f4d545a
Update validation_details_view.dart
hangyujin Nov 7, 2023
4c83571
Update deep_links_controller.dart
hangyujin Nov 8, 2023
58f732c
resolve comments
hangyujin Nov 8, 2023
7f5c6d7
resolve comments
hangyujin Nov 8, 2023
ef479b1
Update deep_link_list_view.dart
hangyujin Nov 8, 2023
bb23643
Merge branch 'master' of https://github.com/flutter/devtools into dee…
hangyujin Nov 9, 2023
0821254
lint
hangyujin Nov 9, 2023
47540b6
Update deep_links_controller.dart
hangyujin Nov 9, 2023
bf911bf
Update deep_links_controller.dart
hangyujin Nov 9, 2023
9e0f937
Update deep_links_screen_test.dart
hangyujin Nov 9, 2023
ae0c59b
update tests
hangyujin Nov 9, 2023
e43d150
Update deep_links_screen_test.dart
hangyujin Nov 9, 2023
68bf8b9
lint
hangyujin Nov 9, 2023
8b86de4
add todo
hangyujin Nov 9, 2023
e52ce27
lint
hangyujin Nov 9, 2023
c199bf9
lint
hangyujin Nov 9, 2023
634d489
lint
hangyujin Nov 9, 2023
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

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,6 @@ import 'deep_links_controller.dart';
import 'deep_links_model.dart';
import 'select_project_view.dart';

enum TableViewType {
domainView,
pathView,
singleUrlView,
}

class DeepLinksScreen extends Screen {
DeepLinksScreen() : super.fromMetaData(ScreenMetaData.deepLinks);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ final allLinkDatas = <LinkData>[
os: [PlatformOS.android, PlatformOS.ios],
domain: 'm.shopping.com',
path: path,
domainError: true,
domainErrors: [DomainError.existence],
pathError: path.contains('shoe'),
),
for (var path in paths)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,370 @@
// Copyright 2023 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:devtools_app_shared/ui.dart';
import 'package:flutter/material.dart';

import '../../shared/common_widgets.dart';
import '../../shared/table/table.dart';
import '../../shared/ui/colors.dart';
import 'deep_link_list_view.dart';
import 'deep_links_controller.dart';
import 'deep_links_model.dart';

class ValidationDetailView extends StatelessWidget {
const ValidationDetailView({
super.key,
required this.linkData,
required this.viewType,
required this.controller,
});

final LinkData linkData;
final TableViewType viewType;
final DeepLinksController controller;

@override
Widget build(BuildContext context) {
hangyujin marked this conversation as resolved.
Show resolved Hide resolved
return ListView(
children: [
ValidationDetailHeader(viewType: viewType, controller: controller),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: largeSpacing,
vertical: defaultSpacing,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'This tool assistants helps you diagnose Universal Links, App Links,'
' and Custom Schemes in your app. Web check are done for the web association'
' file on your website. App checks are done for the intent filters in'
' the manifest and info.plist file, routing issues, URL format, etc.',
style: Theme.of(context).subtleTextStyle,
),
if (viewType == TableViewType.domainView ||
viewType == TableViewType.singleUrlView)
_DomainCheckTable(
controller: controller,
),
if (viewType == TableViewType.pathView ||
viewType == TableViewType.singleUrlView)
_PathCheckTable(),
const SizedBox(height: largeSpacing),
Align(
alignment: Alignment.bottomRight,
child: FilledButton(
onPressed: () {
controller.validateLinks();
},
child: const Text('Recheck all'),
),
),
if (viewType == TableViewType.domainView)
_DomainAssociatedLinksPanel(controller: controller),
],
),
),
],
);
}
}

class ValidationDetailHeader extends StatelessWidget {
const ValidationDetailHeader({
super.key,
required this.viewType,
required this.controller,
});

final TableViewType viewType;
final DeepLinksController controller;

@override
Widget build(BuildContext context) {
return OutlineDecoration(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: defaultSpacing),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
viewType == TableViewType.domainView
? 'Selected domain validation details'
: 'Selected Deep link validation details',
style: Theme.of(context).textTheme.titleSmall,
),
IconButton(
onPressed: () =>
controller.updateDisplayOptions(showSplitScreen: false),
icon: const Icon(Icons.close),
),
],
),
),
);
}
}

class _DomainCheckTable extends StatelessWidget {
const _DomainCheckTable({
required this.controller,
});

final DeepLinksController controller;

@override
Widget build(BuildContext context) {
hangyujin marked this conversation as resolved.
Show resolved Hide resolved
final linkData = controller.selectedLink.value!;
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: intermediateSpacing),
Text('Domain check', style: Theme.of(context).textTheme.titleSmall),
const SizedBox(height: denseSpacing),
DataTable(
headingRowColor: MaterialStateProperty.all(
Theme.of(context).colorScheme.deeplinkTableHeaderColor,
),
dataRowColor: MaterialStateProperty.all(
Theme.of(context).colorScheme.alternatingBackgroundColor2,
),
columns: const [
DataColumn(label: Text('OS')),
DataColumn(label: Text('Issue type')),
DataColumn(label: Text('Status')),
],
dataRowMinHeight: defaultRowHeight,
dataRowMaxHeight: defaultRowHeight,
rows: [
if (linkData.os.contains(PlatformOS.android))
DataRow(
cells: [
const DataCell(Text('Android')),
const DataCell(Text('Digital assets link file')),
DataCell(
linkData.domainErrors.isNotEmpty
? Text(
'Check failed',
style: TextStyle(
color: Theme.of(context).colorScheme.error,
),
)
: Text(
'No issues found',
style: TextStyle(
color: Theme.of(context).colorScheme.green,
),
),
),
],
),
if (linkData.os.contains(PlatformOS.ios))
DataRow(
cells: [
const DataCell(Text('iOS')),
const DataCell(Text('Apple-App-Site-Association file')),
DataCell(
Text(
'No issues found',
style:
TextStyle(color: Theme.of(context).colorScheme.green),
),
),
],
),
],
),
if (linkData.domainErrors.isNotEmpty)
_DomainFixPanel(controller: controller),
],
);
}
}

class _DomainFixPanel extends StatelessWidget {
const _DomainFixPanel({
required this.controller,
});

final DeepLinksController controller;

@override
Widget build(BuildContext context) {
final linkData = controller.selectedLink.value!;
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text('How to fix'),
Text(
'Add the new recommended Digital Asset Links JSON file to the failed website domain at the correct location.',
style: Theme.of(context).subtleTextStyle,
),
Text(
'Update and publish recommend Digital Asset Links JSON file below to this location: ',
style: Theme.of(context).subtleTextStyle,
),
Align(
alignment: Alignment.centerLeft,
child: Card(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(4.0)),
),
color: Theme.of(context).colorScheme.outline,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: denseSpacing),
child: SelectionArea(
child: Text(
'https://${linkData.domain}/.well-known/assetlinks.json',
style: Theme.of(context).regularTextStyle.copyWith(
color: Colors.black,
fontWeight: FontWeight.w500,
),
),
),
),
),
),
Card(
child: ValueListenableBuilder(
valueListenable: controller.generatedAssetLinksForSelectedLink,
builder: (_, String? generatedAssetLinks, __) =>
generatedAssetLinks != null
? Align(
alignment: Alignment.centerLeft,
child: SelectionArea(
child: Text(generatedAssetLinks),
),
)
: const CenteredCircularProgressIndicator(),
),
),
],
);
}
}

class _DomainAssociatedLinksPanel extends StatelessWidget {
const _DomainAssociatedLinksPanel({
required this.controller,
});

final DeepLinksController controller;

@override
Widget build(BuildContext context) {
final linkData = controller.selectedLink.value!;
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'Associated deep link URL',
style: Theme.of(context).textTheme.titleSmall,
),
Card(
color: Theme.of(context).colorScheme.surface,
shape: const RoundedRectangleBorder(),
child: Padding(
padding: const EdgeInsets.all(denseSpacing),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: linkData.associatedPath
.map(
(path) => Padding(
padding: const EdgeInsets.symmetric(
vertical: denseRowSpacing,
),
child: Row(
children: <Widget>[
if (linkData.domainErrors.isNotEmpty)
Icon(
Icons.error,
color: Theme.of(context).colorScheme.error,
size: defaultIconSize,
),
const SizedBox(width: denseSpacing),
Text(path),
],
),
),
)
.toList(),
),
),
),
],
);
}
}

class _PathCheckTable extends StatelessWidget {
@override
Widget build(BuildContext context) {
final notAvailableCell = DataCell(
Text(
'Not available',
style: TextStyle(
color: Theme.of(context).colorScheme.deeplinkUnavailableColor,
),
),
);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: intermediateSpacing),
Text(
'Path check (coming soon)',
style: Theme.of(context).textTheme.titleSmall,
),
Opacity(
opacity: 0.5,
child: DataTable(
headingRowColor: MaterialStateProperty.all(
Theme.of(context).colorScheme.deeplinkTableHeaderColor,
),
dataRowColor: MaterialStateProperty.all(
Theme.of(context).colorScheme.alternatingBackgroundColor2,
),
columns: const [
DataColumn(label: Text('OS')),
DataColumn(label: Text('Issue type')),
DataColumn(label: Text('Status')),
],
rows: [
DataRow(
cells: [
const DataCell(Text('Android')),
const DataCell(Text('Intent filter')),
notAvailableCell,
],
),
DataRow(
cells: [
const DataCell(Text('iOS')),
const DataCell(Text('Associated domain')),
notAvailableCell,
],
),
DataRow(
cells: [
const DataCell(Text('Android, iOS')),
const DataCell(Text('URL format')),
notAvailableCell,
],
),
DataRow(
cells: [
const DataCell(Text('Android, iOS')),
const DataCell(Text('Routing')),
notAvailableCell,
],
),
],
),
),
],
);
}
}
Loading
Loading