diff --git a/analysis_options.yaml b/analysis_options.yaml index 287915f..4af011f 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -26,6 +26,7 @@ linter: - curly_braces_in_flow_control_structures # doc comments - slash_for_doc_comments + - package_api_docs # The lint rules applied to this project can be customized in the # section below to disable rules from the `package:flutter_lints/flutter.yaml` # included above or to enable additional rules. A list of all available lints diff --git a/lib/API/Classes/ShortURL/short_url.dart b/lib/API/Classes/ShortURL/short_url.dart index 7825840..82d0f01 100644 --- a/lib/API/Classes/ShortURL/short_url.dart +++ b/lib/API/Classes/ShortURL/short_url.dart @@ -23,7 +23,7 @@ class ShortURL { VisitsSummary visitsSummary; /// List of tags assigned to this short URL - List tags; + List tags; /// Metadata ShortURLMeta meta; @@ -58,7 +58,7 @@ class ShortURL { deviceLongUrls = DeviceLongUrls.fromJson(json["deviceLongUrls"]), dateCreated = DateTime.parse(json["dateCreated"]), visitsSummary = VisitsSummary.fromJson(json["visitsSummary"]), - tags = json["tags"], + tags = (json["tags"] as List).map((e) => e.toString()).toList(), meta = ShortURLMeta.fromJson(json["meta"]), domain = json["domain"], title = json["title"], diff --git a/lib/API/server_manager.dart b/lib/API/server_manager.dart index cdaeaf9..9dd39a0 100644 --- a/lib/API/server_manager.dart +++ b/lib/API/server_manager.dart @@ -32,7 +32,8 @@ class ServerManager { return apiVersion; } - /// Checks whether the user provided information about the server (url and apikey) + /// Checks whether the user provided information about the server + /// (url and apikey) Future checkLogin() async { await _loadCredentials(); return (serverUrl != null); diff --git a/lib/util/string_to_color.dart b/lib/util/string_to_color.dart new file mode 100644 index 0000000..c4e4ad5 --- /dev/null +++ b/lib/util/string_to_color.dart @@ -0,0 +1,19 @@ +import 'dart:ui'; + +import 'package:flutter/widgets.dart'; + +Color stringToColor(String string) { + int hash = 0; + string.split('').forEach((char) { + hash = char.codeUnitAt(0) + ((hash << 5) - hash); + }); + var rgb = []; + for (int i = 0; i < 3; i++) { + var value = (hash >> (i * 8)) & 0xff; + rgb.add(int.parse(value.toRadixString(16).padLeft(2, '0'), radix: 16)); + } + if (rgb.length != 3) { + return const Color(0xff000000); + } + return Color.fromARGB(1, rgb[0], rgb[1], rgb[2]); +} \ No newline at end of file diff --git a/lib/views/url_detail_view.dart b/lib/views/url_detail_view.dart index cc060fa..7efcc4d 100644 --- a/lib/views/url_detail_view.dart +++ b/lib/views/url_detail_view.dart @@ -1,8 +1,8 @@ -import 'package:dynamic_color/dynamic_color.dart'; import 'package:flutter/material.dart'; import 'package:shlink_app/API/Classes/ShortURL/short_url.dart'; import 'package:intl/intl.dart'; import 'package:shlink_app/API/server_manager.dart'; +import 'package:shlink_app/widgets/url_tags_list_widget.dart'; import '../globals.dart' as globals; class URLDetailView extends StatefulWidget { @@ -100,30 +100,7 @@ class _URLDetailViewState extends State { SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.only(left: 16.0, right: 16.0), - child: Wrap( - children: widget.shortURL.tags.map((tag) { - var randomColor = ([...Colors.primaries]..shuffle()) - .first - .harmonizeWith(Theme.of(context).colorScheme.primary); - return Padding( - padding: const EdgeInsets.only(right: 4, top: 4), - child: Container( - padding: const EdgeInsets.only( - top: 4, bottom: 4, left: 12, right: 12), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4), - color: randomColor, - ), - child: Text( - tag, - style: TextStyle( - color: randomColor.computeLuminance() < 0.5 - ? Colors.white - : Colors.black), - ), - ), - ); - }).toList()), + child: UrlTagsListWidget(tags: widget.shortURL.tags) ), ), _ListCell(title: "Short Code", content: widget.shortURL.shortCode), diff --git a/lib/views/url_list_view.dart b/lib/views/url_list_view.dart index c9a3744..5ddd597 100644 --- a/lib/views/url_list_view.dart +++ b/lib/views/url_list_view.dart @@ -1,10 +1,10 @@ -import 'package:dynamic_color/dynamic_color.dart'; import 'package:flutter/material.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:shlink_app/API/Classes/ShortURL/short_url.dart'; import 'package:shlink_app/API/server_manager.dart'; import 'package:shlink_app/views/short_url_edit_view.dart'; import 'package:shlink_app/views/url_detail_view.dart'; +import 'package:shlink_app/widgets/url_tags_list_widget.dart'; import '../globals.dart' as globals; import 'package:flutter/services.dart'; @@ -235,31 +235,7 @@ class _ShortURLCellState extends State { style: TextStyle(color: Colors.grey[600]), ), // List tags in a row - Wrap( - children: widget.shortURL.tags.map((tag) { - var randomColor = ([...Colors.primaries]..shuffle()) - .first - .harmonizeWith( - Theme.of(context).colorScheme.primary); - return Padding( - padding: const EdgeInsets.only(right: 4, top: 4), - child: Container( - padding: const EdgeInsets.only( - top: 4, bottom: 4, left: 12, right: 12), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4), - color: randomColor, - ), - child: Text( - tag, - style: TextStyle( - color: randomColor.computeLuminance() < 0.5 - ? Colors.white - : Colors.black), - ), - ), - ); - }).toList()) + UrlTagsListWidget(tags: widget.shortURL.tags) ], ), ), diff --git a/lib/widgets/url_tags_list_widget.dart b/lib/widgets/url_tags_list_widget.dart new file mode 100644 index 0000000..8ba51af --- /dev/null +++ b/lib/widgets/url_tags_list_widget.dart @@ -0,0 +1,42 @@ +import 'package:dynamic_color/dynamic_color.dart'; +import 'package:flutter/material.dart'; +import 'package:shlink_app/util/string_to_color.dart'; + +class UrlTagsListWidget extends StatefulWidget { + const UrlTagsListWidget({super.key, required this.tags}); + + final List tags; + + @override + State createState() => _UrlTagsListWidgetState(); +} + +class _UrlTagsListWidgetState extends State { + @override + Widget build(BuildContext context) { + return Wrap( + children: widget.tags.map((tag) { + var boxColor = stringToColor(tag) + .harmonizeWith( + Theme.of(context).colorScheme.primary); + return Padding( + padding: const EdgeInsets.only(right: 4, top: 4), + child: Container( + padding: const EdgeInsets.only( + top: 4, bottom: 4, left: 12, right: 12), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + color: boxColor, + ), + child: Text( + tag, + style: TextStyle( + color: boxColor.computeLuminance() < 0.5 + ? Colors.white + : Colors.black), + ), + ), + ); + }).toList()); + } +}