added about page with OSS licenses
This commit is contained in:
parent
b4bc950fb8
commit
6b68004806
7 changed files with 468 additions and 34 deletions
|
@ -27,4 +27,189 @@ class Constants {
|
|||
|
||||
static const svgBackground = Colors.white;
|
||||
static const svgColorFilter = ColorFilter.mode(svgBackground, BlendMode.dstOver);
|
||||
|
||||
static const List<Dependency> packages = [
|
||||
Dependency(
|
||||
name: 'Flutter',
|
||||
license: 'BSD 3-Clause',
|
||||
licenseUrl: 'https://github.com/flutter/flutter/blob/master/LICENSE',
|
||||
sourceUrl: 'https://github.com/flutter/flutter',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Charts',
|
||||
license: 'Apache 2.0',
|
||||
licenseUrl: 'https://github.com/google/charts/blob/master/LICENSE',
|
||||
sourceUrl: 'https://github.com/google/charts',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Collection',
|
||||
license: 'BSD 3-Clause',
|
||||
licenseUrl: 'https://github.com/dart-lang/collection/blob/master/LICENSE',
|
||||
sourceUrl: 'https://github.com/dart-lang/collection',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Draggable Scrollbar',
|
||||
license: 'MIT',
|
||||
licenseUrl: 'https://github.com/fluttercommunity/flutter-draggable-scrollbar/blob/master/LICENSE',
|
||||
sourceUrl: 'https://github.com/fluttercommunity/flutter-draggable-scrollbar',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Event Bus',
|
||||
license: 'MIT',
|
||||
licenseUrl: 'https://github.com/marcojakob/dart-event-bus/blob/master/LICENSE',
|
||||
sourceUrl: 'https://github.com/marcojakob/dart-event-bus',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Expansion Tile Card',
|
||||
license: 'BSD 3-Clause',
|
||||
licenseUrl: 'https://github.com/Skylled/expansion_tile_card/blob/master/LICENSE',
|
||||
sourceUrl: 'https://github.com/Skylled/expansion_tile_card',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Flushbar',
|
||||
license: 'Apache 2.0',
|
||||
licenseUrl: 'https://github.com/AndreHaueisen/flushbar/blob/master/LICENSE',
|
||||
sourceUrl: 'https://github.com/AndreHaueisen/flushbar',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Flutter ijkplayer',
|
||||
license: 'MIT',
|
||||
licenseUrl: 'https://github.com/CaiJingLong/flutter_ijkplayer/blob/master/LICENSE',
|
||||
sourceUrl: 'https://github.com/CaiJingLong/flutter_ijkplayer',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Flutter Native Timezone',
|
||||
license: 'Apache 2.0',
|
||||
licenseUrl: 'https://github.com/pinkfish/flutter_native_timezone/blob/master/LICENSE',
|
||||
sourceUrl: 'https://github.com/pinkfish/flutter_native_timezone',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Flutter SVG',
|
||||
license: 'MIT',
|
||||
licenseUrl: 'https://github.com/dnfield/flutter_svg/blob/master/LICENSE',
|
||||
sourceUrl: 'https://github.com/dnfield/flutter_svg',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Geocoder',
|
||||
license: 'MIT',
|
||||
licenseUrl: 'https://github.com/aloisdeniel/flutter_geocoder/blob/master/LICENSE',
|
||||
sourceUrl: 'https://github.com/aloisdeniel/flutter_geocoder',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Google Maps for Flutter',
|
||||
license: 'BSD 3-Clause',
|
||||
licenseUrl: 'https://github.com/flutter/plugins/blob/master/packages/google_maps_flutter/google_maps_flutter/LICENSE',
|
||||
sourceUrl: 'https://github.com/flutter/plugins/blob/master/packages/google_maps_flutter/google_maps_flutter',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Intl',
|
||||
license: 'BSD 3-Clause',
|
||||
licenseUrl: 'https://github.com/dart-lang/intl/blob/master/LICENSE',
|
||||
sourceUrl: 'https://github.com/dart-lang/intl',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Outline Material Icons',
|
||||
license: 'Apache 2.0',
|
||||
licenseUrl: 'https://github.com/lucaslcode/outline_material_icons/blob/master/LICENSE',
|
||||
sourceUrl: 'https://github.com/lucaslcode/outline_material_icons',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Palette Generator',
|
||||
license: 'BSD 3-Clause',
|
||||
licenseUrl: 'https://github.com/flutter/packages/blob/master/packages/palette_generator/LICENSE',
|
||||
sourceUrl: 'https://github.com/flutter/packages/tree/master/packages/palette_generator',
|
||||
),
|
||||
Dependency(
|
||||
name: 'PDF for Dart and Flutter',
|
||||
license: 'Apache 2.0',
|
||||
licenseUrl: 'https://github.com/DavBfr/dart_pdf/blob/master/LICENSE',
|
||||
sourceUrl: 'https://github.com/DavBfr/dart_pdf',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Pedantic',
|
||||
license: 'BSD 3-Clause',
|
||||
licenseUrl: 'https://github.com/dart-lang/pedantic/blob/master/LICENSE',
|
||||
sourceUrl: 'https://github.com/dart-lang/pedantic',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Percent Indicator',
|
||||
license: 'BSD 2-Clause',
|
||||
licenseUrl: 'https://github.com/diegoveloper/flutter_percent_indicator/blob/master/LICENSE',
|
||||
sourceUrl: 'https://github.com/diegoveloper/flutter_percent_indicator/',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Permission Handler',
|
||||
license: 'MIT',
|
||||
licenseUrl: 'https://github.com/Baseflow/flutter-permission-handler/blob/develop/permission_handler/LICENSE',
|
||||
sourceUrl: 'https://github.com/Baseflow/flutter-permission-handler',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Photo View',
|
||||
license: 'MIT',
|
||||
licenseUrl: 'https://github.com/renancaraujo/photo_view/blob/master/LICENSE',
|
||||
sourceUrl: 'https://github.com/renancaraujo/photo_view',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Printing',
|
||||
license: 'Apache 2.0',
|
||||
licenseUrl: 'https://github.com/DavBfr/dart_pdf/blob/master/LICENSE',
|
||||
sourceUrl: 'https://github.com/DavBfr/dart_pdf',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Provider',
|
||||
license: 'MIT',
|
||||
licenseUrl: 'https://github.com/rrousselGit/provider/blob/master/LICENSE',
|
||||
sourceUrl: 'https://github.com/rrousselGit/provider',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Screen',
|
||||
license: 'MIT',
|
||||
licenseUrl: 'https://github.com/clovisnicolas/flutter_screen/blob/master/LICENSE',
|
||||
sourceUrl: 'https://github.com/clovisnicolas/flutter_screen',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Shared Preferences',
|
||||
license: 'BSD 3-Clause',
|
||||
licenseUrl: 'https://github.com/flutter/plugins/blob/master/packages/shared_preferences/shared_preferences/LICENSE',
|
||||
sourceUrl: 'https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences',
|
||||
),
|
||||
Dependency(
|
||||
name: 'sqflite',
|
||||
license: 'MIT',
|
||||
licenseUrl: 'https://github.com/tekartik/sqflite/blob/master/sqflite/LICENSE',
|
||||
sourceUrl: 'https://github.com/tekartik/sqflite',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Streams Channel',
|
||||
license: 'Apache 2.0',
|
||||
licenseUrl: 'https://github.com/loup-v/streams_channel/blob/master/LICENSE',
|
||||
sourceUrl: 'https://github.com/loup-v/streams_channel',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Tuple',
|
||||
license: 'BSD 2-Clause',
|
||||
licenseUrl: 'https://github.com/dart-lang/tuple/blob/master/LICENSE',
|
||||
sourceUrl: 'https://github.com/dart-lang/tuple',
|
||||
),
|
||||
Dependency(
|
||||
name: 'UUID',
|
||||
license: 'MIT',
|
||||
licenseUrl: 'https://github.com/Daegalus/dart-uuid/blob/master/LICENSE',
|
||||
sourceUrl: 'https://github.com/Daegalus/dart-uuid',
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
class Dependency {
|
||||
final String name;
|
||||
final String license;
|
||||
final String sourceUrl;
|
||||
final String licenseUrl;
|
||||
|
||||
const Dependency({
|
||||
@required this.name,
|
||||
@required this.license,
|
||||
@required this.licenseUrl,
|
||||
@required this.sourceUrl,
|
||||
});
|
||||
}
|
||||
|
|
45
lib/widgets/about/about_page.dart
Normal file
45
lib/widgets/about/about_page.dart
Normal file
|
@ -0,0 +1,45 @@
|
|||
import 'package:aves/widgets/about/licenses.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AboutPage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('About'),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.only(top: 16),
|
||||
sliver: SliverList(
|
||||
delegate: SliverChildListDelegate(
|
||||
[
|
||||
Center(
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
children: [
|
||||
const TextSpan(text: 'Made with ❤️ and '),
|
||||
WidgetSpan(
|
||||
child: FlutterLogo(
|
||||
size: Theme.of(context).textTheme.bodyText2.fontSize * 1.25,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Divider(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Licenses(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
169
lib/widgets/about/licenses.dart
Normal file
169
lib/widgets/about/licenses.dart
Normal file
|
@ -0,0 +1,169 @@
|
|||
import 'package:aves/utils/constants.dart';
|
||||
import 'package:aves/widgets/common/icons.dart';
|
||||
import 'package:aves/widgets/common/menu_row.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class Licenses extends StatefulWidget {
|
||||
@override
|
||||
_LicensesState createState() => _LicensesState();
|
||||
}
|
||||
|
||||
class _LicensesState extends State<Licenses> {
|
||||
LicenseSort _sort = LicenseSort.name;
|
||||
List<Dependency> _packages;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_packages = List.of(Constants.packages);
|
||||
_sortPackages();
|
||||
}
|
||||
|
||||
void _sortPackages() {
|
||||
_packages.sort((a, b) {
|
||||
switch (_sort) {
|
||||
case LicenseSort.license:
|
||||
final c = compareAsciiUpperCase(a.license, b.license);
|
||||
return c != 0 ? c : compareAsciiUpperCase(a.name, b.name);
|
||||
case LicenseSort.name:
|
||||
default:
|
||||
return compareAsciiUpperCase(a.name, b.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverPadding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
sliver: SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index-- == 0) {
|
||||
return _buildHeader();
|
||||
}
|
||||
return LicenseRow(_packages[index]);
|
||||
},
|
||||
childCount: _packages.length + 1,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader() {
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsetsDirectional.only(start: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
'Open-source licenses',
|
||||
style: Theme.of(context).textTheme.headline6.copyWith(fontFamily: 'Concourse Caps'),
|
||||
),
|
||||
const Spacer(),
|
||||
PopupMenuButton<LicenseSort>(
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
value: LicenseSort.name,
|
||||
child: MenuRow(text: 'Sort by name', checked: _sort == LicenseSort.name),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: LicenseSort.license,
|
||||
child: MenuRow(text: 'Sort by license', checked: _sort == LicenseSort.license),
|
||||
),
|
||||
],
|
||||
onSelected: (newSort) {
|
||||
_sort = newSort;
|
||||
_sortPackages();
|
||||
setState(() {});
|
||||
},
|
||||
tooltip: 'Sort',
|
||||
icon: Icon(AIcons.sort),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||
child: Text('The following sets forth attribution notices for third party software that may be contained in this application.'),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LicenseRow extends StatelessWidget {
|
||||
final Dependency package;
|
||||
|
||||
const LicenseRow(this.package);
|
||||
|
||||
static const borderRadius = BorderRadius.all(Radius.circular(8));
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final textTheme = Theme.of(context).textTheme;
|
||||
final bodyTextStyle = textTheme.bodyText2;
|
||||
final subColor = bodyTextStyle.color.withOpacity(.6);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
InkWell(
|
||||
borderRadius: borderRadius,
|
||||
onTap: () => launch(package.sourceUrl),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
package.name,
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Icon(
|
||||
AIcons.openInNew,
|
||||
size: bodyTextStyle.fontSize,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsetsDirectional.only(start: 16),
|
||||
child: InkWell(
|
||||
borderRadius: borderRadius,
|
||||
onTap: () => launch(package.licenseUrl),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
package.license,
|
||||
style: TextStyle(color: subColor),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Icon(
|
||||
AIcons.openInNew,
|
||||
size: bodyTextStyle.fontSize,
|
||||
color: subColor,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum LicenseSort { license, name }
|
|
@ -11,7 +11,7 @@ import 'package:aves/model/filters/tag.dart';
|
|||
import 'package:aves/model/mime_types.dart';
|
||||
import 'package:aves/model/settings.dart';
|
||||
import 'package:aves/utils/android_file_utils.dart';
|
||||
import 'package:aves/utils/color_utils.dart';
|
||||
import 'package:aves/widgets/about/about_page.dart';
|
||||
import 'package:aves/widgets/album/collection_page.dart';
|
||||
import 'package:aves/widgets/common/aves_logo.dart';
|
||||
import 'package:aves/widgets/common/icons.dart';
|
||||
|
@ -87,6 +87,15 @@ class _AppDrawerState extends State<AppDrawer> {
|
|||
title: 'Favourites',
|
||||
filter: FavouriteFilter(),
|
||||
);
|
||||
final aboutEntry = SafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
child: ListTile(
|
||||
leading: const Icon(AIcons.info),
|
||||
title: const Text('About'),
|
||||
onTap: () => _goToAbout(context),
|
||||
),
|
||||
);
|
||||
|
||||
final drawerItems = <Widget>[
|
||||
header,
|
||||
|
@ -97,6 +106,7 @@ class _AppDrawerState extends State<AppDrawer> {
|
|||
_buildRegularAlbumSection(),
|
||||
_buildCountrySection(),
|
||||
_buildTagSection(),
|
||||
aboutEntry,
|
||||
if (kDebugMode) ...[
|
||||
const Divider(),
|
||||
SafeArea(
|
||||
|
@ -150,35 +160,6 @@ class _AppDrawerState extends State<AppDrawer> {
|
|||
);
|
||||
}
|
||||
|
||||
Widget _buildLocationEntry(LocationLevel level, String location) {
|
||||
String title;
|
||||
String flag;
|
||||
if (level == LocationLevel.country) {
|
||||
final split = location.split(';');
|
||||
String countryCode;
|
||||
if (split.isNotEmpty) title = split[0];
|
||||
if (split.length > 1) countryCode = split[1];
|
||||
flag = LocationFilter.countryCodeToFlag(countryCode);
|
||||
} else {
|
||||
title = location;
|
||||
}
|
||||
return _FilteredCollectionNavTile(
|
||||
source: source,
|
||||
leading: flag != null
|
||||
? Text(
|
||||
flag,
|
||||
style: TextStyle(fontSize: IconTheme.of(context).size),
|
||||
)
|
||||
: Icon(
|
||||
AIcons.location,
|
||||
color: stringToColor(title),
|
||||
),
|
||||
title: title,
|
||||
dense: true,
|
||||
filter: LocationFilter(level, location),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSpecialAlbumSection() {
|
||||
return StreamBuilder(
|
||||
stream: source.eventBus.on<AlbumsChangedEvent>(),
|
||||
|
@ -320,6 +301,16 @@ class _AppDrawerState extends State<AppDrawer> {
|
|||
);
|
||||
}
|
||||
|
||||
void _goToAbout(BuildContext context) {
|
||||
Navigator.pop(context);
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => AboutPage(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _goToDebug(BuildContext context) {
|
||||
Navigator.pop(context);
|
||||
Navigator.push(
|
||||
|
|
|
@ -40,6 +40,7 @@ class AIcons {
|
|||
static const IconData search = OMIcons.search;
|
||||
static const IconData select = OMIcons.selectAll;
|
||||
static const IconData share = OMIcons.share;
|
||||
static const IconData sort = OMIcons.sort;
|
||||
static const IconData stats = OMIcons.pieChart;
|
||||
static const IconData zoomIn = OMIcons.add;
|
||||
static const IconData zoomOut = OMIcons.remove;
|
||||
|
|
42
pubspec.lock
42
pubspec.lock
|
@ -303,6 +303,13 @@ packages:
|
|||
relative: true
|
||||
source: path
|
||||
version: "0.9.2"
|
||||
platform_detect:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform_detect
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -324,6 +331,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.1.1"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.4.4"
|
||||
qr:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -469,6 +483,34 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.6"
|
||||
url_launcher:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: url_launcher
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.4.10"
|
||||
url_launcher_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_macos
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.0.1+7"
|
||||
url_launcher_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.7"
|
||||
url_launcher_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.1+6"
|
||||
utf:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -47,19 +47,19 @@ dependencies:
|
|||
path: ../expansion_tile_card
|
||||
# git:
|
||||
# url: git://github.com/deckerst/expansion_tile_card.git
|
||||
flushbar:
|
||||
flutter_ijkplayer:
|
||||
path: ../flutter_ijkplayer
|
||||
# git:
|
||||
# url: git://github.com/deckerst/flutter_ijkplayer.git
|
||||
flushbar:
|
||||
# git:
|
||||
# url: git://github.com/deckerst/flutter_ijkplayer.git
|
||||
flutter_native_timezone:
|
||||
flutter_svg:
|
||||
geocoder:
|
||||
google_maps_flutter:
|
||||
intl:
|
||||
outline_material_icons:
|
||||
pdf:
|
||||
palette_generator:
|
||||
pdf:
|
||||
pedantic:
|
||||
percent_indicator:
|
||||
permission_handler:
|
||||
|
@ -76,6 +76,7 @@ dependencies:
|
|||
sqflite:
|
||||
streams_channel:
|
||||
tuple:
|
||||
url_launcher:
|
||||
uuid:
|
||||
|
||||
dev_dependencies:
|
||||
|
|
Loading…
Reference in a new issue