From 7b16683d10ed1ef14b70fd17c2268a98070fb314 Mon Sep 17 00:00:00 2001 From: Adrian Baumgart Date: Sat, 27 Jan 2024 23:07:06 +0100 Subject: [PATCH] a ton of refactoring and commenting --- lib/API/Classes/ShlinkStats/ShlinkStats.dart | 10 --- .../ShlinkStats/ShlinkStats_Visits.dart | 12 --- lib/API/Classes/ShlinkStats/shlink_stats.dart | 15 ++++ .../ShlinkStats/shlink_stats_visits.dart | 17 ++++ lib/API/Classes/ShortURL/ShortURL.dart | 33 -------- .../ShortURL/ShortURL_DeviceLongUrls.dart | 18 ----- lib/API/Classes/ShortURL/ShortURL_Meta.dart | 12 --- .../ShortURL/ShortURL_VisitsSummary.dart | 12 --- .../Classes/ShortURL/device_long_urls.dart | 24 ++++++ lib/API/Classes/ShortURL/short_url.dart | 46 +++++++++++ lib/API/Classes/ShortURL/short_url_meta.dart | 17 ++++ lib/API/Classes/ShortURL/visits_summary.dart | 17 ++++ ...mission.dart => short_url_submission.dart} | 19 ++++- lib/API/Methods/connect.dart | 9 ++- ...eteShortUrl.dart => delete_short_url.dart} | 9 ++- ...rtUrls.dart => get_recent_short_urls.dart} | 11 +-- ...rverHealth.dart => get_server_health.dart} | 9 ++- ...ShlinkStats.dart => get_shlink_stats.dart} | 63 ++++++--------- ...{getShortUrls.dart => get_short_urls.dart} | 34 ++++---- ...mitShortUrl.dart => submit_short_url.dart} | 13 ++-- ...ServerManager.dart => server_manager.dart} | 78 +++++++++++-------- lib/globals.dart | 2 +- lib/main.dart | 32 ++------ lib/util/license.dart | 34 ++++---- lib/{HomeView.dart => views/home_view.dart} | 49 ++++++------ lib/{LoginView.dart => views/login_view.dart} | 32 ++++---- .../navigationbar_view.dart} | 10 +-- .../opensource_licenses_view.dart} | 22 +++--- .../settings_view.dart} | 66 ++++++++-------- .../short_url_edit_view.dart} | 64 +++++++-------- .../url_detail_view.dart} | 34 ++++---- .../url_list_view.dart} | 42 +++++----- pubspec.yaml | 2 +- 33 files changed, 457 insertions(+), 410 deletions(-) delete mode 100644 lib/API/Classes/ShlinkStats/ShlinkStats.dart delete mode 100644 lib/API/Classes/ShlinkStats/ShlinkStats_Visits.dart create mode 100644 lib/API/Classes/ShlinkStats/shlink_stats.dart create mode 100644 lib/API/Classes/ShlinkStats/shlink_stats_visits.dart delete mode 100644 lib/API/Classes/ShortURL/ShortURL.dart delete mode 100644 lib/API/Classes/ShortURL/ShortURL_DeviceLongUrls.dart delete mode 100644 lib/API/Classes/ShortURL/ShortURL_Meta.dart delete mode 100644 lib/API/Classes/ShortURL/ShortURL_VisitsSummary.dart create mode 100644 lib/API/Classes/ShortURL/device_long_urls.dart create mode 100644 lib/API/Classes/ShortURL/short_url.dart create mode 100644 lib/API/Classes/ShortURL/short_url_meta.dart create mode 100644 lib/API/Classes/ShortURL/visits_summary.dart rename lib/API/Classes/ShortURLSubmission/{ShortURLSubmission.dart => short_url_submission.dart} (56%) rename lib/API/Methods/{deleteShortUrl.dart => delete_short_url.dart} (64%) rename lib/API/Methods/{getRecentShortUrls.dart => get_recent_short_urls.dart} (64%) rename lib/API/Methods/{getServerHealth.dart => get_server_health.dart} (68%) rename lib/API/Methods/{getShlinkStats.dart => get_shlink_stats.dart} (55%) rename lib/API/Methods/{getShortUrls.dart => get_short_urls.dart} (58%) rename lib/API/Methods/{submitShortUrl.dart => submit_short_url.dart} (59%) rename lib/API/{ServerManager.dart => server_manager.dart} (51%) rename lib/{HomeView.dart => views/home_view.dart} (84%) rename lib/{LoginView.dart => views/login_view.dart} (83%) rename lib/{NavigationBarView.dart => views/navigationbar_view.dart} (76%) rename lib/{OpenSourceLicensesView.dart => views/opensource_licenses_view.dart} (74%) rename lib/{SettingsView.dart => views/settings_view.dart} (80%) rename lib/{ShortURLEditView.dart => views/short_url_edit_view.dart} (82%) rename lib/{URLDetailView.dart => views/url_detail_view.dart} (84%) rename lib/{URLListView.dart => views/url_list_view.dart} (81%) diff --git a/lib/API/Classes/ShlinkStats/ShlinkStats.dart b/lib/API/Classes/ShlinkStats/ShlinkStats.dart deleted file mode 100644 index 232d000..0000000 --- a/lib/API/Classes/ShlinkStats/ShlinkStats.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:shlink_app/API/Classes/ShlinkStats/ShlinkStats_Visits.dart'; - -class ShlinkStats { - ShlinkStats_Visits nonOrphanVisits; - ShlinkStats_Visits orphanVisits; - int shortUrlsCount; - int tagsCount; - - ShlinkStats(this.nonOrphanVisits, this.orphanVisits, this.shortUrlsCount, this.tagsCount); -} \ No newline at end of file diff --git a/lib/API/Classes/ShlinkStats/ShlinkStats_Visits.dart b/lib/API/Classes/ShlinkStats/ShlinkStats_Visits.dart deleted file mode 100644 index d43872f..0000000 --- a/lib/API/Classes/ShlinkStats/ShlinkStats_Visits.dart +++ /dev/null @@ -1,12 +0,0 @@ -class ShlinkStats_Visits { - int total; - int nonBots; - int bots; - - ShlinkStats_Visits(this.total, this.nonBots, this.bots); - - ShlinkStats_Visits.fromJson(Map json) - : total = json["total"], - nonBots = json["nonBots"], - bots = json["bots"]; -} \ No newline at end of file diff --git a/lib/API/Classes/ShlinkStats/shlink_stats.dart b/lib/API/Classes/ShlinkStats/shlink_stats.dart new file mode 100644 index 0000000..490d0a8 --- /dev/null +++ b/lib/API/Classes/ShlinkStats/shlink_stats.dart @@ -0,0 +1,15 @@ +import 'package:shlink_app/API/Classes/ShortURL/visits_summary.dart'; + +/// Includes data about the statistics of a Shlink instance +class ShlinkStats { + /// Data about non-orphan visits + VisitsSummary nonOrphanVisits; + /// Data about orphan visits (without any valid slug assigned) + VisitsSummary orphanVisits; + /// Total count of all short URLs + int shortUrlsCount; + /// Total count all all tags + int tagsCount; + + ShlinkStats(this.nonOrphanVisits, this.orphanVisits, this.shortUrlsCount, this.tagsCount); +} \ No newline at end of file diff --git a/lib/API/Classes/ShlinkStats/shlink_stats_visits.dart b/lib/API/Classes/ShlinkStats/shlink_stats_visits.dart new file mode 100644 index 0000000..f92cb0b --- /dev/null +++ b/lib/API/Classes/ShlinkStats/shlink_stats_visits.dart @@ -0,0 +1,17 @@ +/// Visitor data +class ShlinkStatsVisits { + /// Count of URL visits + int total; + /// Count of URL visits from humans + int nonBots; + /// Count of URL visits from bots/crawlers + int bots; + + ShlinkStatsVisits(this.total, this.nonBots, this.bots); + + /// Converts the JSON data from the API to an instance of [ShlinkStatsVisits] + ShlinkStatsVisits.fromJson(Map json) + : total = json["total"], + nonBots = json["nonBots"], + bots = json["bots"]; +} \ No newline at end of file diff --git a/lib/API/Classes/ShortURL/ShortURL.dart b/lib/API/Classes/ShortURL/ShortURL.dart deleted file mode 100644 index 89359f4..0000000 --- a/lib/API/Classes/ShortURL/ShortURL.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:shlink_app/API/Classes/ShortURL/ShortURL_DeviceLongUrls.dart'; -import 'package:shlink_app/API/Classes/ShortURL/ShortURL_Meta.dart'; -import 'package:shlink_app/API/Classes/ShortURL/ShortURL_VisitsSummary.dart'; - -class ShortURL { - String shortCode; - String shortUrl; - String longUrl; - ShortURL_DeviceLongUrls deviceLongUrls; - DateTime dateCreated; - ShortURL_VisitsSummary visitsSummary; - List tags; - ShortURL_Meta meta; - String? domain; - String? title; - bool crawlable; - - ShortURL(this.shortCode, this.shortUrl, this.longUrl, this.deviceLongUrls, this.dateCreated, this.visitsSummary, this.tags, this.meta, this.domain, this.title, this.crawlable); - - ShortURL.fromJson(Map json): - shortCode = json["shortCode"], - shortUrl = json["shortUrl"], - longUrl = json["longUrl"], - deviceLongUrls = ShortURL_DeviceLongUrls.fromJson(json["deviceLongUrls"]), - dateCreated = DateTime.parse(json["dateCreated"]), - visitsSummary = ShortURL_VisitsSummary.fromJson(json["visitsSummary"]), - tags = json["tags"], - meta = ShortURL_Meta.fromJson(json["meta"]), - domain = json["domain"], - title = json["title"], - crawlable = json["crawlable"]; - -} \ No newline at end of file diff --git a/lib/API/Classes/ShortURL/ShortURL_DeviceLongUrls.dart b/lib/API/Classes/ShortURL/ShortURL_DeviceLongUrls.dart deleted file mode 100644 index 48b786f..0000000 --- a/lib/API/Classes/ShortURL/ShortURL_DeviceLongUrls.dart +++ /dev/null @@ -1,18 +0,0 @@ -class ShortURL_DeviceLongUrls { - final String? android; - final String? ios; - final String? desktop; - - ShortURL_DeviceLongUrls(this.android, this.ios, this.desktop); - - ShortURL_DeviceLongUrls.fromJson(Map json) - : android = json["android"], - ios = json["ios"], - desktop = json["desktop"]; - - Map toJson() => { - "android": android, - "ios": ios, - "desktop": desktop - }; -} \ No newline at end of file diff --git a/lib/API/Classes/ShortURL/ShortURL_Meta.dart b/lib/API/Classes/ShortURL/ShortURL_Meta.dart deleted file mode 100644 index 58d203f..0000000 --- a/lib/API/Classes/ShortURL/ShortURL_Meta.dart +++ /dev/null @@ -1,12 +0,0 @@ -class ShortURL_Meta { - DateTime? validSince; - DateTime? validUntil; - int? maxVisits; - - ShortURL_Meta(this.validSince, this.validUntil, this.maxVisits); - - ShortURL_Meta.fromJson(Map json): - validSince = json["validSince"] != null ? DateTime.parse(json["validSince"]) : null, - validUntil = json["validUntil"] != null ? DateTime.parse(json["validUntil"]) : null, - maxVisits = json["maxVisits"]; -} \ No newline at end of file diff --git a/lib/API/Classes/ShortURL/ShortURL_VisitsSummary.dart b/lib/API/Classes/ShortURL/ShortURL_VisitsSummary.dart deleted file mode 100644 index 32fac26..0000000 --- a/lib/API/Classes/ShortURL/ShortURL_VisitsSummary.dart +++ /dev/null @@ -1,12 +0,0 @@ -class ShortURL_VisitsSummary { - int total; - int nonBots; - int bots; - - ShortURL_VisitsSummary(this.total, this.nonBots, this.bots); - - ShortURL_VisitsSummary.fromJson(Map json): - total = json["total"] as int, - nonBots = json["nonBots"] as int, - bots = json["bots"] as int; -} \ No newline at end of file diff --git a/lib/API/Classes/ShortURL/device_long_urls.dart b/lib/API/Classes/ShortURL/device_long_urls.dart new file mode 100644 index 0000000..ad6b96b --- /dev/null +++ b/lib/API/Classes/ShortURL/device_long_urls.dart @@ -0,0 +1,24 @@ +/// Data about device-specific long URLs for one short URL +class DeviceLongUrls { + /// Custom URL for Android devices + final String? android; + /// Custom URL for iOS devices + final String? ios; + /// Custom URL for desktop + final String? desktop; + + DeviceLongUrls(this.android, this.ios, this.desktop); + + /// Converts JSON data from the API to an instance of [DeviceLongUrls] + DeviceLongUrls.fromJson(Map json) + : android = json["android"], + ios = json["ios"], + desktop = json["desktop"]; + + /// Converts data from this class to an JSON object of type + Map toJson() => { + "android": android, + "ios": ios, + "desktop": desktop + }; +} \ No newline at end of file diff --git a/lib/API/Classes/ShortURL/short_url.dart b/lib/API/Classes/ShortURL/short_url.dart new file mode 100644 index 0000000..fd70ee3 --- /dev/null +++ b/lib/API/Classes/ShortURL/short_url.dart @@ -0,0 +1,46 @@ +import 'package:shlink_app/API/Classes/ShortURL/device_long_urls.dart'; +import 'package:shlink_app/API/Classes/ShortURL/short_url_meta.dart'; +import 'package:shlink_app/API/Classes/ShortURL/visits_summary.dart'; + +/// Data about a short URL +class ShortURL { + /// Slug of the short URL used in the URL + String shortCode; + /// Entire short URL + String shortUrl; + /// Long URL where the user gets redirected to + String longUrl; + /// Device-specific long URLs + DeviceLongUrls deviceLongUrls; + /// Creation date of the short URL + DateTime dateCreated; + /// Visitor data + VisitsSummary visitsSummary; + /// List of tags assigned to this short URL + List tags; + /// Metadata + ShortURLMeta meta; + /// Associated domain + String? domain; + /// Optional title + String? title; + /// Whether the short URL is crawlable by a web crawler + bool crawlable; + + ShortURL(this.shortCode, this.shortUrl, this.longUrl, this.deviceLongUrls, this.dateCreated, this.visitsSummary, this.tags, this.meta, this.domain, this.title, this.crawlable); + + /// Converts the JSON data from the API to an instance of [ShortURL] + ShortURL.fromJson(Map json): + shortCode = json["shortCode"], + shortUrl = json["shortUrl"], + longUrl = json["longUrl"], + deviceLongUrls = DeviceLongUrls.fromJson(json["deviceLongUrls"]), + dateCreated = DateTime.parse(json["dateCreated"]), + visitsSummary = VisitsSummary.fromJson(json["visitsSummary"]), + tags = json["tags"], + meta = ShortURLMeta.fromJson(json["meta"]), + domain = json["domain"], + title = json["title"], + crawlable = json["crawlable"]; + +} \ No newline at end of file diff --git a/lib/API/Classes/ShortURL/short_url_meta.dart b/lib/API/Classes/ShortURL/short_url_meta.dart new file mode 100644 index 0000000..d0d01c4 --- /dev/null +++ b/lib/API/Classes/ShortURL/short_url_meta.dart @@ -0,0 +1,17 @@ +/// Metadata for a short URL +class ShortURLMeta { + /// The date since when this short URL has been valid + DateTime? validSince; + /// The data when this short URL expires + DateTime? validUntil; + /// Amount of maximum visits allowed to this short URL + int? maxVisits; + + ShortURLMeta(this.validSince, this.validUntil, this.maxVisits); + + /// Converts JSON data from the API to an instance of [ShortURLMeta] + ShortURLMeta.fromJson(Map json): + validSince = json["validSince"] != null ? DateTime.parse(json["validSince"]) : null, + validUntil = json["validUntil"] != null ? DateTime.parse(json["validUntil"]) : null, + maxVisits = json["maxVisits"]; +} \ No newline at end of file diff --git a/lib/API/Classes/ShortURL/visits_summary.dart b/lib/API/Classes/ShortURL/visits_summary.dart new file mode 100644 index 0000000..9936796 --- /dev/null +++ b/lib/API/Classes/ShortURL/visits_summary.dart @@ -0,0 +1,17 @@ +/// Visitor data +class VisitsSummary { + /// Count of total visits + int total; + /// Count of visits from humans + int nonBots; + /// Count of visits from bots/crawlers + int bots; + + VisitsSummary(this.total, this.nonBots, this.bots); + + /// Converts JSON data from the API to an instance of [VisitsSummary] + VisitsSummary.fromJson(Map json): + total = json["total"] as int, + nonBots = json["nonBots"] as int, + bots = json["bots"] as int; +} \ No newline at end of file diff --git a/lib/API/Classes/ShortURLSubmission/ShortURLSubmission.dart b/lib/API/Classes/ShortURLSubmission/short_url_submission.dart similarity index 56% rename from lib/API/Classes/ShortURLSubmission/ShortURLSubmission.dart rename to lib/API/Classes/ShortURLSubmission/short_url_submission.dart index a2cc76f..9b41771 100644 --- a/lib/API/Classes/ShortURLSubmission/ShortURLSubmission.dart +++ b/lib/API/Classes/ShortURLSubmission/short_url_submission.dart @@ -1,22 +1,37 @@ -import '../ShortURL/ShortURL_DeviceLongUrls.dart'; +import '../ShortURL/device_long_urls.dart'; +/// Data for a short URL which can be submitted to the server class ShortURLSubmission { + /// Long URL to redirect to String longUrl; - ShortURL_DeviceLongUrls? deviceLongUrls; + /// Device-specific long URLs + DeviceLongUrls? deviceLongUrls; + /// Date since when this short URL is valid in ISO8601 format String? validSince; + /// Date until when this short URL is valid in ISO8601 format String? validUntil; + /// Amount of maximum visits allowed to this short URLs int? maxVisits; + /// List of tags assigned to this short URL List tags; + /// Title of the page String? title; + /// Whether the short URL is crawlable by web crawlers bool crawlable; + /// Whether to forward query parameters bool forwardQuery; + /// Custom slug (if not provided a random one will be generated) String? customSlug; + /// Whether to use an existing short URL if the slug matches bool findIfExists; + /// Domain to use String? domain; + /// Length of the slug if a custom one is not provided int? shortCodeLength; ShortURLSubmission({required this.longUrl, required this.deviceLongUrls, this.validSince, this.validUntil, this.maxVisits, required this.tags, this.title, required this.crawlable, required this.forwardQuery, this.customSlug, required this.findIfExists, this.domain, this.shortCodeLength}); + /// Converts class data to a JSON object Map toJson() { return { "longUrl": longUrl, diff --git a/lib/API/Methods/connect.dart b/lib/API/Methods/connect.dart index 77dbbb2..f5ea29b 100644 --- a/lib/API/Methods/connect.dart +++ b/lib/API/Methods/connect.dart @@ -2,12 +2,13 @@ import 'dart:async'; import 'dart:convert'; import 'package:dartz/dartz.dart'; import 'package:http/http.dart' as http; -import '../ServerManager.dart'; +import '../server_manager.dart'; -FutureOr> API_connect(String? api_key, String? server_url, String apiVersion) async { +/// Tries to connect to the Shlink server +FutureOr> apiConnect(String? apiKey, String? serverUrl, String apiVersion) async { try { - final response = await http.get(Uri.parse("${server_url}/rest/v${apiVersion}/short-urls"), headers: { - "X-Api-Key": api_key ?? "", + final response = await http.get(Uri.parse("$serverUrl/rest/v$apiVersion/short-urls"), headers: { + "X-Api-Key": apiKey ?? "", }); if (response.statusCode == 200) { return left(""); diff --git a/lib/API/Methods/deleteShortUrl.dart b/lib/API/Methods/delete_short_url.dart similarity index 64% rename from lib/API/Methods/deleteShortUrl.dart rename to lib/API/Methods/delete_short_url.dart index a983ec2..9085cbf 100644 --- a/lib/API/Methods/deleteShortUrl.dart +++ b/lib/API/Methods/delete_short_url.dart @@ -2,12 +2,13 @@ import 'dart:async'; import 'dart:convert'; import 'package:dartz/dartz.dart'; import 'package:http/http.dart' as http; -import '../ServerManager.dart'; +import '../server_manager.dart'; -FutureOr> API_deleteShortUrl(String shortCode, String? api_key, String? server_url, String apiVersion) async { +/// Deletes a short URL from the server +FutureOr> apiDeleteShortUrl(String shortCode, String? apiKey, String? serverUrl, String apiVersion) async { try { - final response = await http.delete(Uri.parse("${server_url}/rest/v${apiVersion}/short-urls/${shortCode}"), headers: { - "X-Api-Key": api_key ?? "", + final response = await http.delete(Uri.parse("$serverUrl/rest/v$apiVersion/short-urls/$shortCode"), headers: { + "X-Api-Key": apiKey ?? "", }); if (response.statusCode == 204) { // get returned short url diff --git a/lib/API/Methods/getRecentShortUrls.dart b/lib/API/Methods/get_recent_short_urls.dart similarity index 64% rename from lib/API/Methods/getRecentShortUrls.dart rename to lib/API/Methods/get_recent_short_urls.dart index 63d8420..3633ee1 100644 --- a/lib/API/Methods/getRecentShortUrls.dart +++ b/lib/API/Methods/get_recent_short_urls.dart @@ -2,13 +2,14 @@ import 'dart:async'; import 'dart:convert'; import 'package:dartz/dartz.dart'; import 'package:http/http.dart' as http; -import 'package:shlink_app/API/Classes/ShortURL/ShortURL.dart'; -import '../ServerManager.dart'; +import 'package:shlink_app/API/Classes/ShortURL/short_url.dart'; +import '../server_manager.dart'; -FutureOr, Failure>> API_getRecentShortUrls(String? api_key, String? server_url, String apiVersion) async { +/// Gets recently created short URLs from the server +FutureOr, Failure>> apiGetRecentShortUrls(String? apiKey, String? serverUrl, String apiVersion) async { try { - final response = await http.get(Uri.parse("${server_url}/rest/v${apiVersion}/short-urls?itemsPerPage=5&orderBy=dateCreated-DESC"), headers: { - "X-Api-Key": api_key ?? "", + final response = await http.get(Uri.parse("$serverUrl/rest/v$apiVersion/short-urls?itemsPerPage=5&orderBy=dateCreated-DESC"), headers: { + "X-Api-Key": apiKey ?? "", }); if (response.statusCode == 200) { var jsonResponse = jsonDecode(response.body); diff --git a/lib/API/Methods/getServerHealth.dart b/lib/API/Methods/get_server_health.dart similarity index 68% rename from lib/API/Methods/getServerHealth.dart rename to lib/API/Methods/get_server_health.dart index cba2c60..8a6b619 100644 --- a/lib/API/Methods/getServerHealth.dart +++ b/lib/API/Methods/get_server_health.dart @@ -2,12 +2,13 @@ import 'dart:async'; import 'dart:convert'; import 'package:dartz/dartz.dart'; import 'package:http/http.dart' as http; -import '../ServerManager.dart'; +import '../server_manager.dart'; -FutureOr> API_getServerHealth(String? api_key, String? server_url, String apiVersion) async { +/// Gets the status of the server and health information +FutureOr> apiGetServerHealth(String? apiKey, String? serverUrl, String apiVersion) async { try { - final response = await http.get(Uri.parse("${server_url}/rest/v${apiVersion}/health"), headers: { - "X-Api-Key": api_key ?? "", + final response = await http.get(Uri.parse("$serverUrl/rest/v$apiVersion/health"), headers: { + "X-Api-Key": apiKey ?? "", }); if (response.statusCode == 200) { var jsonData = jsonDecode(response.body); diff --git a/lib/API/Methods/getShlinkStats.dart b/lib/API/Methods/get_shlink_stats.dart similarity index 55% rename from lib/API/Methods/getShlinkStats.dart rename to lib/API/Methods/get_shlink_stats.dart index b6a7b4f..5164f6c 100644 --- a/lib/API/Methods/getShlinkStats.dart +++ b/lib/API/Methods/get_shlink_stats.dart @@ -2,11 +2,12 @@ import 'dart:async'; import 'dart:convert'; import 'package:dartz/dartz.dart'; import 'package:http/http.dart' as http; -import 'package:shlink_app/API/Classes/ShlinkStats/ShlinkStats_Visits.dart'; -import '../Classes/ShlinkStats/ShlinkStats.dart'; -import '../ServerManager.dart'; +import 'package:shlink_app/API/Classes/ShortURL/visits_summary.dart'; +import '../Classes/ShlinkStats/shlink_stats.dart'; +import '../server_manager.dart'; -FutureOr> API_getShlinkStats(String? api_key, String? server_url, String apiVersion) async { +/// Gets statistics about the Shlink server +FutureOr> apiGetShlinkStats(String? apiKey, String? serverUrl, String apiVersion) async { var nonOrphanVisits; var orphanVisits; @@ -14,7 +15,7 @@ FutureOr> API_getShlinkStats(String? api_key, Strin var tagsCount; var failure; - var visitStatsResponse = await _getVisitStats(api_key, server_url, apiVersion); + var visitStatsResponse = await _getVisitStats(apiKey, serverUrl, apiVersion); visitStatsResponse.fold((l) { nonOrphanVisits = l.nonOrphanVisits; orphanVisits = l.orphanVisits; @@ -23,7 +24,7 @@ FutureOr> API_getShlinkStats(String? api_key, Strin return right(r); }); - var shortUrlsCountResponse = await _getShortUrlsCount(api_key, server_url, apiVersion); + var shortUrlsCountResponse = await _getShortUrlsCount(apiKey, serverUrl, apiVersion); shortUrlsCountResponse.fold((l) { shortUrlsCount = l; }, (r) { @@ -31,7 +32,7 @@ FutureOr> API_getShlinkStats(String? api_key, Strin return right(r); }); - var tagsCountResponse = await _getTagsCount(api_key, server_url, apiVersion); + var tagsCountResponse = await _getTagsCount(apiKey, serverUrl, apiVersion); tagsCountResponse.fold((l) { tagsCount = l; }, (r) { @@ -40,7 +41,7 @@ FutureOr> API_getShlinkStats(String? api_key, Strin }); while(failure == null && (nonOrphanVisits == null || orphanVisits == null || shortUrlsCount == null || tagsCount == null)) { - await Future.delayed(Duration(milliseconds: 100)); + await Future.delayed(const Duration(milliseconds: 100)); } if (failure != null) { @@ -49,37 +50,23 @@ FutureOr> API_getShlinkStats(String? api_key, Strin return left(ShlinkStats(nonOrphanVisits, orphanVisits, shortUrlsCount, tagsCount)); } -/*Future>, FutureOr>, FutureOr>>> waiterFunction(String? api_key, String? server_url, String apiVersion) async { - late FutureOr> visits; - late FutureOr> shortUrlsCount; - late FutureOr> tagsCount; - - await Future.wait([ - _getVisitStats(api_key, server_url, apiVersion).then((value) => visits = value), - _getShortUrlsCount(api_key, server_url, apiVersion).then((value) => shortUrlsCount = value), - _getTagsCount(api_key, server_url, apiVersion).then((value) => tagsCount = value), - ]); - - return Future.value(tuple.Tuple3(visits, shortUrlsCount, tagsCount)); - -}*/ - class _ShlinkVisitStats { - ShlinkStats_Visits nonOrphanVisits; - ShlinkStats_Visits orphanVisits; + VisitsSummary nonOrphanVisits; + VisitsSummary orphanVisits; _ShlinkVisitStats(this.nonOrphanVisits, this.orphanVisits); } -FutureOr> _getVisitStats(String? api_key, String? server_url, String apiVersion) async { +/// Gets visitor statistics about the entire server +FutureOr> _getVisitStats(String? apiKey, String? serverUrl, String apiVersion) async { try { - final response = await http.get(Uri.parse("${server_url}/rest/v${apiVersion}/visits"), headers: { - "X-Api-Key": api_key ?? "", + final response = await http.get(Uri.parse("$serverUrl/rest/v$apiVersion/visits"), headers: { + "X-Api-Key": apiKey ?? "", }); if (response.statusCode == 200) { var jsonResponse = jsonDecode(response.body); - var nonOrphanVisits = ShlinkStats_Visits.fromJson(jsonResponse["visits"]["nonOrphanVisits"]); - var orphanVisits = ShlinkStats_Visits.fromJson(jsonResponse["visits"]["orphanVisits"]); + var nonOrphanVisits = VisitsSummary.fromJson(jsonResponse["visits"]["nonOrphanVisits"]); + var orphanVisits = VisitsSummary.fromJson(jsonResponse["visits"]["orphanVisits"]); return left(_ShlinkVisitStats(nonOrphanVisits, orphanVisits)); } @@ -98,11 +85,11 @@ FutureOr> _getVisitStats(String? api_key, Str } } -// get short urls count -FutureOr> _getShortUrlsCount(String? api_key, String? server_url, String apiVersion) async { +/// Gets amount of short URLs +FutureOr> _getShortUrlsCount(String? apiKey, String? serverUrl, String apiVersion) async { try { - final response = await http.get(Uri.parse("${server_url}/rest/v${apiVersion}/short-urls"), headers: { - "X-Api-Key": api_key ?? "", + final response = await http.get(Uri.parse("$serverUrl/rest/v$apiVersion/short-urls"), headers: { + "X-Api-Key": apiKey ?? "", }); if (response.statusCode == 200) { var jsonResponse = jsonDecode(response.body); @@ -123,11 +110,11 @@ FutureOr> _getShortUrlsCount(String? api_key, String? serve } } -// get tags count -FutureOr> _getTagsCount(String? api_key, String? server_url, String apiVersion) async { +/// Gets amount of tags +FutureOr> _getTagsCount(String? apiKey, String? serverUrl, String apiVersion) async { try { - final response = await http.get(Uri.parse("${server_url}/rest/v${apiVersion}/tags"), headers: { - "X-Api-Key": api_key ?? "", + final response = await http.get(Uri.parse("$serverUrl/rest/v$apiVersion/tags"), headers: { + "X-Api-Key": apiKey ?? "", }); if (response.statusCode == 200) { var jsonResponse = jsonDecode(response.body); diff --git a/lib/API/Methods/getShortUrls.dart b/lib/API/Methods/get_short_urls.dart similarity index 58% rename from lib/API/Methods/getShortUrls.dart rename to lib/API/Methods/get_short_urls.dart index 252ee25..d66241a 100644 --- a/lib/API/Methods/getShortUrls.dart +++ b/lib/API/Methods/get_short_urls.dart @@ -2,39 +2,41 @@ import 'dart:async'; import 'dart:convert'; import 'package:dartz/dartz.dart'; import 'package:http/http.dart' as http; -import 'package:shlink_app/API/Classes/ShortURL/ShortURL.dart'; -import '../ServerManager.dart'; +import 'package:shlink_app/API/Classes/ShortURL/short_url.dart'; +import '../server_manager.dart'; -FutureOr, Failure>> API_getShortUrls(String? api_key, String? server_url, String apiVersion) async { - var _currentPage = 1; - var _maxPages = 2; - List _allUrls = []; +/// Gets all short URLs +FutureOr, Failure>> apiGetShortUrls(String? apiKey, String? serverUrl, String apiVersion) async { + var currentPage = 1; + var maxPages = 2; + List allUrls = []; Failure? error; - while (_currentPage <= _maxPages) { - final response = await _getShortUrlPage(_currentPage, api_key, server_url, apiVersion); + while (currentPage <= maxPages) { + final response = await _getShortUrlPage(currentPage, apiKey, serverUrl, apiVersion); response.fold((l) { - _allUrls.addAll(l.urls); - _maxPages = l.totalPages; - _currentPage++; + allUrls.addAll(l.urls); + maxPages = l.totalPages; + currentPage++; }, (r) { - _maxPages = 0; + maxPages = 0; error = r; }); } if (error == null) { - return left(_allUrls); + return left(allUrls); } else { return right(error!); } } -FutureOr> _getShortUrlPage(int page, String? api_key, String? server_url, String apiVersion) async { +/// Gets all short URLs from a specific page +FutureOr> _getShortUrlPage(int page, String? apiKey, String? serverUrl, String apiVersion) async { try { - final response = await http.get(Uri.parse("${server_url}/rest/v${apiVersion}/short-urls?page=${page}"), headers: { - "X-Api-Key": api_key ?? "", + final response = await http.get(Uri.parse("$serverUrl/rest/v$apiVersion/short-urls?page=$page"), headers: { + "X-Api-Key": apiKey ?? "", }); if (response.statusCode == 200) { var jsonResponse = jsonDecode(response.body); diff --git a/lib/API/Methods/submitShortUrl.dart b/lib/API/Methods/submit_short_url.dart similarity index 59% rename from lib/API/Methods/submitShortUrl.dart rename to lib/API/Methods/submit_short_url.dart index bf339cd..4aa6f1c 100644 --- a/lib/API/Methods/submitShortUrl.dart +++ b/lib/API/Methods/submit_short_url.dart @@ -2,13 +2,14 @@ import 'dart:async'; import 'dart:convert'; import 'package:dartz/dartz.dart'; import 'package:http/http.dart' as http; -import 'package:shlink_app/API/Classes/ShortURLSubmission/ShortURLSubmission.dart'; -import '../ServerManager.dart'; +import 'package:shlink_app/API/Classes/ShortURLSubmission/short_url_submission.dart'; +import '../server_manager.dart'; -FutureOr> API_submitShortUrl(ShortURLSubmission shortUrl, String? api_key, String? server_url, String apiVersion) async { +/// Submits a short URL to a server for it to be added +FutureOr> apiSubmitShortUrl(ShortURLSubmission shortUrl, String? apiKey, String? serverUrl, String apiVersion) async { try { - final response = await http.post(Uri.parse("${server_url}/rest/v${apiVersion}/short-urls"), headers: { - "X-Api-Key": api_key ?? "", + final response = await http.post(Uri.parse("$serverUrl/rest/v$apiVersion/short-urls"), headers: { + "X-Api-Key": apiKey ?? "", }, body: jsonEncode(shortUrl.toJson())); if (response.statusCode == 200) { // get returned short url @@ -18,7 +19,7 @@ FutureOr> API_submitShortUrl(ShortURLSubmission shortUrl else { try { var jsonBody = jsonDecode(response.body); - return right(ApiFailure(type: jsonBody["type"], detail: jsonBody["detail"], title: jsonBody["title"], status: jsonBody["status"], invalidElements: jsonBody["invalidElements"] ?? null)); + return right(ApiFailure(type: jsonBody["type"], detail: jsonBody["detail"], title: jsonBody["title"], status: jsonBody["status"], invalidElements: jsonBody["invalidElements"])); } catch(resErr) { return right(RequestFailure(response.statusCode, resErr.toString())); diff --git a/lib/API/ServerManager.dart b/lib/API/server_manager.dart similarity index 51% rename from lib/API/ServerManager.dart rename to lib/API/server_manager.dart index 97b92bc..892efc8 100644 --- a/lib/API/ServerManager.dart +++ b/lib/API/server_manager.dart @@ -2,113 +2,125 @@ import 'dart:async'; import 'package:dartz/dartz.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'package:shlink_app/API/Classes/ShlinkStats/ShlinkStats.dart'; -import 'package:shlink_app/API/Classes/ShortURL/ShortURL.dart'; -import 'package:shlink_app/API/Classes/ShortURLSubmission/ShortURLSubmission.dart'; +import 'package:shlink_app/API/Classes/ShlinkStats/shlink_stats.dart'; +import 'package:shlink_app/API/Classes/ShortURL/short_url.dart'; +import 'package:shlink_app/API/Classes/ShortURLSubmission/short_url_submission.dart'; import 'package:shlink_app/API/Methods/connect.dart'; -import 'package:shlink_app/API/Methods/getRecentShortUrls.dart'; -import 'package:shlink_app/API/Methods/getServerHealth.dart'; -import 'package:shlink_app/API/Methods/getShlinkStats.dart'; -import 'package:shlink_app/API/Methods/getShortUrls.dart'; +import 'package:shlink_app/API/Methods/get_recent_short_urls.dart'; +import 'package:shlink_app/API/Methods/get_server_health.dart'; +import 'package:shlink_app/API/Methods/get_shlink_stats.dart'; +import 'package:shlink_app/API/Methods/get_short_urls.dart'; -import 'Methods/deleteShortUrl.dart'; -import 'Methods/submitShortUrl.dart'; +import 'Methods/delete_short_url.dart'; +import 'Methods/submit_short_url.dart'; class ServerManager { - String? _server_url; - String? _api_key; + /// The URL of the Shlink server + String? serverUrl; + /// The API key to access the server + String? apiKey; + + /// Current Shlink API Version used by the app static String apiVersion = "3"; String getServerUrl() { - return _server_url ?? ""; + return serverUrl ?? ""; } String getApiVersion() { return apiVersion; } + /// Checks whether the user provided information about the server (url and apikey) Future checkLogin() async { await _loadCredentials(); - return (_server_url != null); + return (serverUrl != null); } + /// Logs out the user and removes data about the Shlink server Future logOut() async { const storage = FlutterSecureStorage(); await storage.delete(key: "shlink_url"); await storage.delete(key: "shlink_apikey"); } + /// Loads the server credentials from [FlutterSecureStorage] Future _loadCredentials() async { const storage = FlutterSecureStorage(); final prefs = await SharedPreferences.getInstance(); if (prefs.getBool('first_run') ?? true) { - FlutterSecureStorage storage = FlutterSecureStorage(); + FlutterSecureStorage storage = const FlutterSecureStorage(); await storage.deleteAll(); prefs.setBool('first_run', false); } - _server_url = await storage.read(key: "shlink_url"); - _api_key = await storage.read(key: "shlink_apikey"); + serverUrl = await storage.read(key: "shlink_url"); + apiKey = await storage.read(key: "shlink_apikey"); } + /// Saves the provided server credentials to [FlutterSecureStorage] void _saveCredentials(String url, String apiKey) async { const storage = FlutterSecureStorage(); storage.write(key: "shlink_url", value: url); storage.write(key: "shlink_apikey", value: apiKey); } - void _removeCredentials() async { - const storage = FlutterSecureStorage(); - storage.delete(key: "shlink_url"); - storage.delete(key: "shlink_apikey"); - } + /// Saves provided server credentials and tries to establish a connection FutureOr> initAndConnect(String url, String apiKey) async { // TODO: convert url to correct format - _server_url = url; - _api_key = apiKey; + serverUrl = url; + this.apiKey = apiKey; _saveCredentials(url, apiKey); final result = await connect(); result.fold((l) => null, (r) { - _removeCredentials(); + logOut(); }); return result; } + /// Establishes a connection to the server FutureOr> connect() async { _loadCredentials(); - return API_connect(_api_key, _server_url, apiVersion); + return apiConnect(apiKey, serverUrl, apiVersion); } + /// Gets all short URLs from the server FutureOr, Failure>> getShortUrls() async { - return API_getShortUrls(_api_key, _server_url, apiVersion); + return apiGetShortUrls(apiKey, serverUrl, apiVersion); } + /// Gets statistics about the Shlink instance FutureOr> getShlinkStats() async { - return API_getShlinkStats(_api_key, _server_url, apiVersion); + return apiGetShlinkStats(apiKey, serverUrl, apiVersion); } + /// Saves a new short URL to the server FutureOr> submitShortUrl(ShortURLSubmission shortUrl) async { - return API_submitShortUrl(shortUrl, _api_key, _server_url, apiVersion); + return apiSubmitShortUrl(shortUrl, apiKey, serverUrl, apiVersion); } + /// Deletes a short URL from the server, identified by its slug FutureOr> deleteShortUrl(String shortCode) async { - return API_deleteShortUrl(shortCode, _api_key, _server_url, apiVersion); + return apiDeleteShortUrl(shortCode, apiKey, serverUrl, apiVersion); } + /// Gets health data about the server FutureOr> getServerHealth() async { - return API_getServerHealth(_api_key, _server_url, apiVersion); + return apiGetServerHealth(apiKey, serverUrl, apiVersion); } + /// Gets recently created/used short URLs from the server FutureOr, Failure>> getRecentShortUrls() async { - return API_getRecentShortUrls(_api_key, _server_url, apiVersion); + return apiGetRecentShortUrls(apiKey, serverUrl, apiVersion); } } +/// Server response data type about a page of short URLs from the server class ShortURLPageResponse { List urls; int totalPages; @@ -116,6 +128,7 @@ class ShortURLPageResponse { ShortURLPageResponse(this.urls, this.totalPages); } +/// Server response data type about the health status of the server class ServerHealthResponse { String status; String version; @@ -123,8 +136,10 @@ class ServerHealthResponse { ServerHealthResponse({required this.status, required this.version}); } +/// Failure class, used for the API abstract class Failure {} +/// Used when a request to a server fails (due to networking issues or an unexpected response) class RequestFailure extends Failure { int statusCode; String description; @@ -132,6 +147,7 @@ class RequestFailure extends Failure { RequestFailure(this.statusCode, this.description); } +/// Contains information about an error returned by the Shlink API class ApiFailure extends Failure { String type; String detail; diff --git a/lib/globals.dart b/lib/globals.dart index cce656f..a75c20a 100644 --- a/lib/globals.dart +++ b/lib/globals.dart @@ -1,4 +1,4 @@ library dev.abmgrt.shlink_app.globals; -import 'package:shlink_app/API/ServerManager.dart'; +import 'package:shlink_app/API/server_manager.dart'; ServerManager serverManager = ServerManager(); \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 1b95e38..a16fc50 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:shlink_app/LoginView.dart'; -import 'package:shlink_app/NavigationBarView.dart'; +import 'package:shlink_app/views/login_view.dart'; +import 'package:shlink_app/views/navigationbar_view.dart'; import 'globals.dart' as globals; import 'package:dynamic_color/dynamic_color.dart'; @@ -11,7 +11,7 @@ void main() { class MyApp extends StatelessWidget { const MyApp({super.key}); - static final _defaultLightColorScheme = + static const _defaultLightColorScheme = ColorScheme.light();//.fromSwatch(primarySwatch: Colors.blue, backgroundColor: Colors.white); static final _defaultDarkColorScheme = ColorScheme.fromSwatch( @@ -25,14 +25,14 @@ class MyApp extends StatelessWidget { title: 'Shlink', debugShowCheckedModeBanner: false, theme: ThemeData( - appBarTheme: AppBarTheme( + appBarTheme: const AppBarTheme( backgroundColor: Color(0xfffafafa), ), colorScheme: lightColorScheme ?? _defaultLightColorScheme, useMaterial3: true ), darkTheme: ThemeData( - appBarTheme: AppBarTheme( + appBarTheme: const AppBarTheme( backgroundColor: Color(0xff0d0d0d), foregroundColor: Colors.white, elevation: 0, @@ -40,26 +40,8 @@ class MyApp extends StatelessWidget { colorScheme: darkColorScheme?.copyWith(background: Colors.black) ?? _defaultDarkColorScheme, useMaterial3: true, ), - /*theme: ThemeData( - primarySwatch: Colors.blue, - brightness: Brightness.light, - useMaterial3: true, - colorScheme: ColorScheme.fromSwatch().copyWith( - secondary: Colors.orange - ) - ),*/ - /*darkTheme: ThemeData( - primarySwatch: Colors.blue, - brightness: Brightness.dark, - useMaterial3: true, - colorScheme: ColorScheme.dark( - background: Colors.black, - surface: Color(0xff0d0d0d), - secondaryContainer: Colors.grey[300] - ) - ),*/ themeMode: ThemeMode.system, - home: InitialPage() + home: const InitialPage() ); }); } @@ -97,7 +79,7 @@ class _InitialPageState extends State { @override Widget build(BuildContext context) { - return Scaffold( + return const Scaffold( body: Center( child: Text("") ), diff --git a/lib/util/license.dart b/lib/util/license.dart index 4ce9b31..237205a 100644 --- a/lib/util/license.dart +++ b/lib/util/license.dart @@ -26,7 +26,7 @@ class LicenseUtil { static List getLicenses() { return [ - License( + const License( name: r'cupertino_icons', license: r'''The MIT License (MIT) @@ -52,7 +52,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', homepage: null, repository: r'https://github.com/flutter/packages/tree/main/third_party/packages/cupertino_icons', ), - License( + const License( name: r'dartz', license: r'''The MIT License (MIT) @@ -80,7 +80,7 @@ SOFTWARE. homepage: r'https://github.com/spebbe/dartz', repository: null, ), - License( + const License( name: r'dynamic_color', license: r''' Apache License Version 2.0, January 2004 @@ -288,7 +288,7 @@ SOFTWARE. homepage: null, repository: r'https://github.com/material-foundation/flutter-packages/tree/main/packages/dynamic_color', ), - License( + const License( name: r'flutter', license: r'''Copyright 2014 The Flutter Authors. All rights reserved. @@ -320,7 +320,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. homepage: r'https://flutter.dev/', repository: r'https://github.com/flutter/flutter', ), - License( + const License( name: r'flutter_launcher_icons', license: r'''MIT License @@ -348,7 +348,7 @@ SOFTWARE. homepage: r'https://github.com/fluttercommunity/flutter_launcher_icons', repository: r'https://github.com/fluttercommunity/flutter_launcher_icons/', ), - License( + const License( name: r'flutter_lints', license: r'''Copyright 2013 The Flutter Authors. All rights reserved. @@ -380,7 +380,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. homepage: null, repository: r'https://github.com/flutter/packages/tree/main/packages/flutter_lints', ), - License( + const License( name: r'flutter_process_text', license: r'''BSD 3-Clause License @@ -413,7 +413,7 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', homepage: null, repository: r'https://github.com/DevsOnFlutter/flutter_process_text', ), - License( + const License( name: r'flutter_secure_storage', license: r'''BSD 3-Clause License @@ -448,7 +448,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', homepage: null, repository: r'https://github.com/mogol/flutter_secure_storage/tree/develop/flutter_secure_storage', ), - License( + const License( name: r'flutter_test', license: r'''Copyright 2014 The Flutter Authors. All rights reserved. @@ -480,7 +480,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. homepage: r'https://flutter.dev/', repository: r'https://github.com/flutter/flutter', ), - License( + const License( name: r'http', license: r'''Copyright 2014, the Dart project authors. @@ -514,7 +514,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. homepage: null, repository: r'https://github.com/dart-lang/http/tree/master/pkgs/http', ), - License( + const License( name: r'intl', license: r'''Copyright 2013, the Dart project authors. @@ -548,7 +548,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. homepage: null, repository: r'https://github.com/dart-lang/i18n/tree/main/pkgs/intl', ), - License( + const License( name: r'license_generator', license: r'''MIT License @@ -576,7 +576,7 @@ SOFTWARE. homepage: r'https://github.com/icapps/flutter-icapps-license', repository: null, ), - License( + const License( name: r'package_info_plus', license: r'''Copyright 2017 The Chromium Authors. All rights reserved. @@ -610,7 +610,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. homepage: r'https://plus.fluttercommunity.dev/', repository: r'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/', ), - License( + const License( name: r'qr_flutter', license: r'''BSD 3-Clause License @@ -646,7 +646,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. homepage: r'https://github.com/theyakka/qr.flutter', repository: null, ), - License( + const License( name: r'shared_preferences', license: r'''Copyright 2013 The Flutter Authors. All rights reserved. @@ -678,7 +678,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. homepage: null, repository: r'https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences', ), - License( + const License( name: r'tuple', license: r'''Copyright (c) 2014, the tuple project authors. All rights reserved. @@ -706,7 +706,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', homepage: null, repository: r'https://github.com/google/tuple.dart', ), - License( + const License( name: r'url_launcher', license: r'''Copyright 2013 The Flutter Authors. All rights reserved. diff --git a/lib/HomeView.dart b/lib/views/home_view.dart similarity index 84% rename from lib/HomeView.dart rename to lib/views/home_view.dart index f0f9d11..3e31b7d 100644 --- a/lib/HomeView.dart +++ b/lib/views/home_view.dart @@ -1,12 +1,11 @@ import 'package:flutter/material.dart'; import 'package:qr_flutter/qr_flutter.dart'; -import 'package:shlink_app/API/Classes/ShlinkStats/ShlinkStats.dart'; -import 'package:shlink_app/API/ServerManager.dart'; -import 'package:shlink_app/LoginView.dart'; -import 'package:shlink_app/ShortURLEditView.dart'; -import 'package:shlink_app/URLListView.dart'; -import 'API/Classes/ShortURL/ShortURL.dart'; -import 'globals.dart' as globals; +import 'package:shlink_app/API/Classes/ShlinkStats/shlink_stats.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_list_view.dart'; +import '../API/Classes/ShortURL/short_url.dart'; +import '../globals.dart' as globals; class HomeView extends StatefulWidget { const HomeView({Key? key}) : super(key: key); @@ -35,8 +34,8 @@ class _HomeViewState extends State { } Future loadAllData() async { - var resultStats = await loadShlinkStats(); - var resultShortUrls = await loadRecentShortUrls(); + await loadShlinkStats(); + await loadRecentShortUrls(); return; } @@ -99,7 +98,7 @@ class _HomeViewState extends State { title: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("Shlink", style: TextStyle(fontWeight: FontWeight.bold)), + const Text("Shlink", style: TextStyle(fontWeight: FontWeight.bold)), Text(globals.serverManager.getServerUrl(), style: TextStyle(fontSize: 16, color: Colors.grey[600])) ], ) @@ -119,12 +118,12 @@ class _HomeViewState extends State { SliverToBoxAdapter( child: Center( child: Padding( - padding: EdgeInsets.only(top: 50), + padding: const EdgeInsets.only(top: 50), child: Column( children: [ - Text("No Short URLs", style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),), + const Text("No Short URLs", style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),), Padding( - padding: EdgeInsets.only(top: 8), + padding: const EdgeInsets.only(top: 8), child: Text('Create one by tapping the "+" button below', style: TextStyle(fontSize: 16, color: Colors.grey[600]),), ) ], @@ -134,9 +133,9 @@ class _HomeViewState extends State { ) else SliverList(delegate: SliverChildBuilderDelegate( - (BuildContext _context, int index) { + (BuildContext context, int index) { if (index == 0) { - return Padding( + return const Padding( padding: EdgeInsets.only(top: 16, left: 12, right: 12), child: Text("Recent Short URLs", style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)), ); @@ -200,10 +199,10 @@ class _HomeViewState extends State { ), floatingActionButton: FloatingActionButton( onPressed: () async { - final result = await Navigator.of(context).push(MaterialPageRoute(builder: (context) => const ShortURLEditView())); + await Navigator.of(context).push(MaterialPageRoute(builder: (context) => const ShortURLEditView())); loadRecentShortUrls(); }, - child: Icon(Icons.add), + child: const Icon(Icons.add), ) ); } @@ -211,11 +210,11 @@ class _HomeViewState extends State { // stats card widget class _ShlinkStatsCardWidget extends StatefulWidget { - const _ShlinkStatsCardWidget({this.text, this.icon, this.borderColor}); + const _ShlinkStatsCardWidget({required this.text, required this.icon, this.borderColor}); - final icon; - final borderColor; - final text; + final IconData icon; + final Color? borderColor; + final String text; @override State<_ShlinkStatsCardWidget> createState() => _ShlinkStatsCardWidgetState(); @@ -226,9 +225,9 @@ class _ShlinkStatsCardWidgetState extends State<_ShlinkStatsCardWidget> { Widget build(BuildContext context) { var randomColor = ([...Colors.primaries]..shuffle()).first; return Padding( - padding: EdgeInsets.all(4), + padding: const EdgeInsets.all(4), child: Container( - padding: EdgeInsets.all(12), + padding: const EdgeInsets.all(12), decoration: BoxDecoration( border: Border.all(color: widget.borderColor ?? randomColor), borderRadius: BorderRadius.circular(8) @@ -238,8 +237,8 @@ class _ShlinkStatsCardWidgetState extends State<_ShlinkStatsCardWidget> { children: [ Icon(widget.icon), Padding( - padding: EdgeInsets.only(left: 4), - child: Text(widget.text, style: TextStyle(fontWeight: FontWeight.bold)), + padding: const EdgeInsets.only(left: 4), + child: Text(widget.text, style: const TextStyle(fontWeight: FontWeight.bold)), ) ], ), diff --git a/lib/LoginView.dart b/lib/views/login_view.dart similarity index 83% rename from lib/LoginView.dart rename to lib/views/login_view.dart index 8299ed9..ad10db6 100644 --- a/lib/LoginView.dart +++ b/lib/views/login_view.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:shlink_app/API/ServerManager.dart'; +import 'package:shlink_app/API/server_manager.dart'; import 'package:shlink_app/main.dart'; -import 'globals.dart' as globals; +import '../globals.dart' as globals; class LoginView extends StatefulWidget { const LoginView({Key? key}) : super(key: key); @@ -11,8 +11,8 @@ class LoginView extends StatefulWidget { } class _LoginViewState extends State { - late TextEditingController _server_url_controller; - late TextEditingController _apikey_controller; + late TextEditingController _serverUrlController; + late TextEditingController _apiKeyController; bool _isLoggingIn = false; String _errorMessage = ""; @@ -21,8 +21,8 @@ class _LoginViewState extends State { void initState() { // TODO: implement initState super.initState(); - _server_url_controller = TextEditingController(); - _apikey_controller = TextEditingController(); + _serverUrlController = TextEditingController(); + _apiKeyController = TextEditingController(); } void _connect() async { @@ -30,7 +30,7 @@ class _LoginViewState extends State { _isLoggingIn = true; _errorMessage = ""; }); - final connectResult = await globals.serverManager.initAndConnect(_server_url_controller.text, _apikey_controller.text); + final connectResult = await globals.serverManager.initAndConnect(_serverUrlController.text, _apiKeyController.text); connectResult.fold((l) { Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (context) => const InitialPage()) @@ -61,8 +61,8 @@ class _LoginViewState extends State { extendBody: true, body: CustomScrollView( slivers: [ - SliverAppBar.medium( - title: const Text("Add server", style: TextStyle(fontWeight: FontWeight.bold)) + const SliverAppBar.medium( + title: Text("Add server", style: TextStyle(fontWeight: FontWeight.bold)) ), SliverFillRemaining( child: Padding( @@ -75,10 +75,10 @@ class _LoginViewState extends State { child: Text("Server URL", style: TextStyle(fontWeight: FontWeight.bold),)), Row( children: [ - Icon(Icons.dns_outlined), - SizedBox(width: 8), + const Icon(Icons.dns_outlined), + const SizedBox(width: 8), Expanded(child: TextField( - controller: _server_url_controller, + controller: _serverUrlController, keyboardType: TextInputType.url, decoration: const InputDecoration( border: OutlineInputBorder(), @@ -93,10 +93,10 @@ class _LoginViewState extends State { ), Row( children: [ - Icon(Icons.key), - SizedBox(width: 8), + const Icon(Icons.key), + const SizedBox(width: 8), Expanded(child: TextField( - controller: _apikey_controller, + controller: _apiKeyController, keyboardType: TextInputType.text, obscureText: true, decoration: const InputDecoration( @@ -130,7 +130,7 @@ class _LoginViewState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Flexible(child: Text(_errorMessage, style: TextStyle(color: Colors.red), textAlign: TextAlign.center)) + Flexible(child: Text(_errorMessage, style: const TextStyle(color: Colors.red), textAlign: TextAlign.center)) ], ), ) diff --git a/lib/NavigationBarView.dart b/lib/views/navigationbar_view.dart similarity index 76% rename from lib/NavigationBarView.dart rename to lib/views/navigationbar_view.dart index 13eeff7..65bd9e6 100644 --- a/lib/NavigationBarView.dart +++ b/lib/views/navigationbar_view.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:shlink_app/SettingsView.dart'; -import 'package:shlink_app/HomeView.dart'; -import 'package:shlink_app/URLListView.dart'; +import 'package:shlink_app/views/settings_view.dart'; +import 'package:shlink_app/views/home_view.dart'; +import 'package:shlink_app/views/url_list_view.dart'; class NavigationBarView extends StatefulWidget { const NavigationBarView({Key? key}) : super(key: key); @@ -12,7 +12,7 @@ class NavigationBarView extends StatefulWidget { class _NavigationBarViewState extends State { - final List views = [HomeView(), URLListView(), SettingsView()]; + final List views = [const HomeView(), const URLListView(), const SettingsView()]; int _selectedView = 0; @override @@ -20,7 +20,7 @@ class _NavigationBarViewState extends State { return Scaffold( body: views.elementAt(_selectedView), bottomNavigationBar: NavigationBar( - destinations: [ + destinations: const [ NavigationDestination(icon: Icon(Icons.home), label: "Home"), NavigationDestination(icon: Icon(Icons.link), label: "Short URLs"), NavigationDestination(icon: Icon(Icons.settings), label: "Settings") diff --git a/lib/OpenSourceLicensesView.dart b/lib/views/opensource_licenses_view.dart similarity index 74% rename from lib/OpenSourceLicensesView.dart rename to lib/views/opensource_licenses_view.dart index 9d1c275..968219b 100644 --- a/lib/OpenSourceLicensesView.dart +++ b/lib/views/opensource_licenses_view.dart @@ -15,9 +15,9 @@ class _OpenSourceLicensesViewState extends State { return Scaffold( body: CustomScrollView( slivers: [ - SliverAppBar.medium( + const SliverAppBar.medium( expandedHeight: 120, - title: const Text("Open Source Licenses", style: TextStyle(fontWeight: FontWeight.bold),) + title: Text("Open Source Licenses", style: TextStyle(fontWeight: FontWeight.bold),) ), SliverList( delegate: SliverChildBuilderDelegate( @@ -32,23 +32,23 @@ class _OpenSourceLicensesViewState extends State { } }, child: Padding( - padding: EdgeInsets.all(12), + padding: const EdgeInsets.all(12), child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), color: Theme.of(context).brightness == Brightness.light ? Colors.grey[100] : Colors.grey[900], ), child: Padding( - padding: EdgeInsets.only(left: 12, right: 12, top: 20, bottom: 20), + padding: const EdgeInsets.only(left: 12, right: 12, top: 20, bottom: 20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("${currentLicense.name}", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)), - Text("Version: ${currentLicense.version ?? "N/A"}", style: TextStyle(color: Colors.grey)), - SizedBox(height: 8), - Divider(), - SizedBox(height: 8), - Text("${currentLicense.license}", textAlign: TextAlign.justify, style: TextStyle(color: Colors.grey)), + Text(currentLicense.name, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 18)), + Text("Version: ${currentLicense.version ?? "N/A"}", style: const TextStyle(color: Colors.grey)), + const SizedBox(height: 8), + const Divider(), + const SizedBox(height: 8), + Text(currentLicense.license, textAlign: TextAlign.justify, style: const TextStyle(color: Colors.grey)), ], ), ), @@ -59,7 +59,7 @@ class _OpenSourceLicensesViewState extends State { childCount: LicenseUtil.getLicenses().length ), ), - SliverToBoxAdapter( + const SliverToBoxAdapter( child: Padding( padding: EdgeInsets.only(top: 8, bottom: 20), child: Text("Thank you to all maintainers of these repositories 💝", style: TextStyle(color: Colors.grey), textAlign: TextAlign.center,), diff --git a/lib/SettingsView.dart b/lib/views/settings_view.dart similarity index 80% rename from lib/SettingsView.dart rename to lib/views/settings_view.dart index 8703e06..092a29f 100644 --- a/lib/SettingsView.dart +++ b/lib/views/settings_view.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:package_info_plus/package_info_plus.dart'; -import 'package:shlink_app/API/ServerManager.dart'; -import 'package:shlink_app/LoginView.dart'; -import 'package:shlink_app/OpenSourceLicensesView.dart'; +import 'package:shlink_app/API/server_manager.dart'; +import 'package:shlink_app/views/login_view.dart'; +import 'package:shlink_app/views/opensource_licenses_view.dart'; import 'package:url_launcher/url_launcher.dart'; -import 'globals.dart' as globals; +import '../globals.dart' as globals; class SettingsView extends StatefulWidget { const SettingsView({super.key}); @@ -21,9 +21,9 @@ enum ServerStatus { class _SettingsViewState extends State { - var _server_version = "---"; - ServerStatus _server_status = ServerStatus.connecting; - var packageInfo = null; + var _serverVersion = "---"; + ServerStatus _serverStatus = ServerStatus.connecting; + PackageInfo packageInfo = PackageInfo(appName: "", packageName: "", version: "", buildNumber: ""); @override void initState() { @@ -34,19 +34,19 @@ class _SettingsViewState extends State { } void getServerHealth() async { - var _packageInfo = await PackageInfo.fromPlatform(); + var packageInfo = await PackageInfo.fromPlatform(); setState(() { - packageInfo = _packageInfo; + packageInfo = packageInfo; }); final response = await globals.serverManager.getServerHealth(); response.fold((l) { setState(() { - _server_version = l.version; - _server_status = ServerStatus.connected; + _serverVersion = l.version; + _serverStatus = ServerStatus.connected; }); }, (r) { setState(() { - _server_status = ServerStatus.disconnected; + _serverStatus = ServerStatus.disconnected; }); var text = ""; @@ -74,7 +74,7 @@ class _SettingsViewState extends State { PopupMenuButton( itemBuilder: (context) { return [ - PopupMenuItem( + const PopupMenuItem( value: 0, child: Text("Log out...", style: TextStyle(color: Colors.red)), ) @@ -105,7 +105,7 @@ class _SettingsViewState extends State { child: Row( children: [ Icon(Icons.dns_outlined, color: (() { - switch (_server_status) { + switch (_serverStatus) { case ServerStatus.connected: return Colors.green; case ServerStatus.connecting: @@ -114,19 +114,19 @@ class _SettingsViewState extends State { return Colors.red; } }())), - SizedBox(width: 8), + const SizedBox(width: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("Connected to", style: TextStyle(color: Colors.grey)), - Text(globals.serverManager.getServerUrl(), style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)), + const Text("Connected to", style: TextStyle(color: Colors.grey)), + Text(globals.serverManager.getServerUrl(), style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16)), Row( children: [ - Text("API Version: ", style: TextStyle(color: Colors.grey, fontWeight: FontWeight.w600)), - Text("${globals.serverManager.getApiVersion()}", style: TextStyle(color: Colors.grey)), - SizedBox(width: 16), - Text("Server Version: ", style: TextStyle(color: Colors.grey, fontWeight: FontWeight.w600)), - Text("${_server_version}", style: TextStyle(color: Colors.grey)) + const Text("API Version: ", style: TextStyle(color: Colors.grey, fontWeight: FontWeight.w600)), + Text(globals.serverManager.getApiVersion(), style: const TextStyle(color: Colors.grey)), + const SizedBox(width: 16), + const Text("Server Version: ", style: TextStyle(color: Colors.grey, fontWeight: FontWeight.w600)), + Text(_serverVersion, style: const TextStyle(color: Colors.grey)) ], ), ], @@ -135,9 +135,9 @@ class _SettingsViewState extends State { ), ), ), - SizedBox(height: 8), - Divider(), - SizedBox(height: 8), + const SizedBox(height: 8), + const Divider(), + const SizedBox(height: 8), GestureDetector( onTap: () { Navigator.of(context).push( @@ -149,7 +149,7 @@ class _SettingsViewState extends State { borderRadius: BorderRadius.circular(8), color: Theme.of(context).brightness == Brightness.light ? Colors.grey[100] : Colors.grey[900], ), - child: Padding( + child: const Padding( padding: EdgeInsets.only(left: 12, right: 12, top: 20, bottom: 20), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -167,7 +167,7 @@ class _SettingsViewState extends State { ), ), ), - SizedBox(height: 16), + const SizedBox(height: 16), GestureDetector( onTap: () async { var url = Uri.parse("https://github.com/rainloreley/shlink-mobile-app"); @@ -180,7 +180,7 @@ class _SettingsViewState extends State { borderRadius: BorderRadius.circular(8), color: Theme.of(context).brightness == Brightness.light ? Colors.grey[100] : Colors.grey[900], ), - child: Padding( + child: const Padding( padding: EdgeInsets.only(left: 12, right: 12, top: 20, bottom: 20), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -198,7 +198,7 @@ class _SettingsViewState extends State { ), ), ), - SizedBox(height: 16), + const SizedBox(height: 16), GestureDetector( onTap: () async { var url = Uri.parse("https://abmgrt.dev/shlink-manager/privacy"); @@ -211,7 +211,7 @@ class _SettingsViewState extends State { borderRadius: BorderRadius.circular(8), color: Theme.of(context).brightness == Brightness.light ? Colors.grey[100] : Colors.grey[900], ), - child: Padding( + child: const Padding( padding: EdgeInsets.only(left: 12, right: 12, top: 20, bottom: 20), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -229,12 +229,12 @@ class _SettingsViewState extends State { ), ), ), - SizedBox(height: 16), - if (packageInfo != null) + const SizedBox(height: 16), + if (packageInfo.appName != "") Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - Text("${packageInfo.appName}, v${packageInfo.version} (${packageInfo.buildNumber})", style: TextStyle(color: Colors.grey),),], + Text("${packageInfo.appName}, v${packageInfo.version} (${packageInfo.buildNumber})", style: const TextStyle(color: Colors.grey),),], ) ], ) diff --git a/lib/ShortURLEditView.dart b/lib/views/short_url_edit_view.dart similarity index 82% rename from lib/ShortURLEditView.dart rename to lib/views/short_url_edit_view.dart index ddc4f9d..7623a40 100644 --- a/lib/ShortURLEditView.dart +++ b/lib/views/short_url_edit_view.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:shlink_app/API/Classes/ShortURLSubmission/ShortURLSubmission.dart'; -import 'package:shlink_app/API/ServerManager.dart'; -import 'globals.dart' as globals; +import 'package:shlink_app/API/Classes/ShortURLSubmission/short_url_submission.dart'; +import 'package:shlink_app/API/server_manager.dart'; +import '../globals.dart' as globals; class ShortURLEditView extends StatefulWidget { const ShortURLEditView({super.key}); @@ -34,7 +34,7 @@ class _ShortURLEditViewState extends State with SingleTickerPr void initState() { _customSlugDiceAnimationController = AnimationController( vsync: this, - duration: Duration(milliseconds: 500), + duration: const Duration(milliseconds: 500), ); super.initState(); } @@ -67,11 +67,11 @@ class _ShortURLEditViewState extends State with SingleTickerPr if (copyToClipboard) { await Clipboard.setData(ClipboardData(text: l)); - final snackBar = SnackBar(content: Text("Copied to clipboard!"), backgroundColor: Colors.green[400], behavior: SnackBarBehavior.floating); + final snackBar = SnackBar(content: const Text("Copied to clipboard!"), backgroundColor: Colors.green[400], behavior: SnackBarBehavior.floating); ScaffoldMessenger.of(context).showSnackBar(snackBar); } else { - final snackBar = SnackBar(content: Text("Short URL created!"), backgroundColor: Colors.green[400], behavior: SnackBarBehavior.floating); + final snackBar = SnackBar(content: const Text("Short URL created!"), backgroundColor: Colors.green[400], behavior: SnackBarBehavior.floating); ScaffoldMessenger.of(context).showSnackBar(snackBar); } Navigator.pop(context); @@ -89,8 +89,8 @@ class _ShortURLEditViewState extends State with SingleTickerPr } else { text = (r as ApiFailure).detail; - if ((r as ApiFailure).invalidElements != null) { - text = text + ": " + (r as ApiFailure).invalidElements.toString(); + if ((r).invalidElements != null) { + text = "$text: ${(r).invalidElements}"; } } @@ -106,12 +106,12 @@ class _ShortURLEditViewState extends State with SingleTickerPr return Scaffold( body: CustomScrollView( slivers: [ - SliverAppBar.medium( + const SliverAppBar.medium( title: Text("New Short URL", style: TextStyle(fontWeight: FontWeight.bold)), ), SliverToBoxAdapter( child: Padding( - padding: EdgeInsets.only(left: 16, right: 16, top: 16), + padding: const EdgeInsets.only(left: 16, right: 16, top: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -119,8 +119,8 @@ class _ShortURLEditViewState extends State with SingleTickerPr controller: longUrlController, decoration: InputDecoration( errorText: longUrlError != "" ? longUrlError : null, - border: OutlineInputBorder(), - label: Row( + border: const OutlineInputBorder(), + label: const Row( children: [ Icon(Icons.public), SizedBox(width: 8), @@ -129,7 +129,7 @@ class _ShortURLEditViewState extends State with SingleTickerPr ) ), ), - SizedBox(height: 16), + const SizedBox(height: 16), Row( children: [ Expanded( @@ -137,23 +137,25 @@ class _ShortURLEditViewState extends State with SingleTickerPr controller: customSlugController, style: TextStyle(color: randomSlug ? Colors.grey : Theme.of(context).brightness == Brightness.light ? Colors.black : Colors.white), onChanged: (_) { - if (randomSlug) setState(() { + if (randomSlug) { + setState(() { randomSlug = false; }); + } }, decoration: InputDecoration( - border: OutlineInputBorder(), + border: const OutlineInputBorder(), label: Row( children: [ - Icon(Icons.link), - SizedBox(width: 8), + const Icon(Icons.link), + const SizedBox(width: 8), Text("${randomSlug ? "Random" : "Custom"} slug", style: TextStyle(fontStyle: randomSlug ? FontStyle.italic : FontStyle.normal),) ], ) ), ), ), - SizedBox(width: 8), + const SizedBox(width: 8), RotationTransition( turns: Tween(begin: 0.0, end: 3.0).animate(CurvedAnimation(parent: _customSlugDiceAnimationController, curve: Curves.easeInOutExpo)), child: IconButton( @@ -175,13 +177,13 @@ class _ShortURLEditViewState extends State with SingleTickerPr ], ), if (randomSlug) - SizedBox(height: 16), + const SizedBox(height: 16), if (randomSlug) Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text("Random slug length"), + const Text("Random slug length"), SizedBox( width: 100, child: TextField( @@ -189,8 +191,8 @@ class _ShortURLEditViewState extends State with SingleTickerPr keyboardType: TextInputType.number, decoration: InputDecoration( errorText: randomSlugLengthError != "" ? "" : null, - border: OutlineInputBorder(), - label: Row( + border: const OutlineInputBorder(), + label: const Row( children: [ Icon(Icons.tag), SizedBox(width: 8), @@ -201,10 +203,10 @@ class _ShortURLEditViewState extends State with SingleTickerPr )) ], ), - SizedBox(height: 16), + const SizedBox(height: 16), TextField( controller: titleController, - decoration: InputDecoration( + decoration: const InputDecoration( border: OutlineInputBorder(), label: Row( children: [ @@ -215,11 +217,11 @@ class _ShortURLEditViewState extends State with SingleTickerPr ) ), ), - SizedBox(height: 16), + const SizedBox(height: 16), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text("Crawlable"), + const Text("Crawlable"), Switch( value: isCrawlable, onChanged: (_) { @@ -230,11 +232,11 @@ class _ShortURLEditViewState extends State with SingleTickerPr ) ], ), - SizedBox(height: 16), + const SizedBox(height: 16), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text("Forward query params"), + const Text("Forward query params"), Switch( value: forwardQuery, onChanged: (_) { @@ -245,11 +247,11 @@ class _ShortURLEditViewState extends State with SingleTickerPr ) ], ), - SizedBox(height: 16), + const SizedBox(height: 16), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text("Copy to clipboard"), + const Text("Copy to clipboard"), Switch( value: copyToClipboard, onChanged: (_) { @@ -293,7 +295,7 @@ class _ShortURLEditViewState extends State with SingleTickerPr } } }, - child: isSaving ? Padding(padding: EdgeInsets.all(16), child: CircularProgressIndicator(strokeWidth: 3)) : Icon(Icons.save) + child: isSaving ? const Padding(padding: EdgeInsets.all(16), child: CircularProgressIndicator(strokeWidth: 3)) : const Icon(Icons.save) ), ); } diff --git a/lib/URLDetailView.dart b/lib/views/url_detail_view.dart similarity index 84% rename from lib/URLDetailView.dart rename to lib/views/url_detail_view.dart index 3f6af98..298180f 100644 --- a/lib/URLDetailView.dart +++ b/lib/views/url_detail_view.dart @@ -1,9 +1,9 @@ import 'package:dynamic_color/dynamic_color.dart'; import 'package:flutter/material.dart'; -import 'package:shlink_app/API/Classes/ShortURL/ShortURL.dart'; +import 'package:shlink_app/API/Classes/ShortURL/short_url.dart'; import 'package:intl/intl.dart'; -import 'package:shlink_app/API/ServerManager.dart'; -import 'globals.dart' as globals; +import 'package:shlink_app/API/server_manager.dart'; +import '../globals.dart' as globals; class URLDetailView extends StatefulWidget { const URLDetailView({super.key, required this.shortURL}); @@ -26,9 +26,9 @@ class _URLDetailViewState extends State { child: ListBody( children: [ const Text("You're about to delete"), - SizedBox(height: 4), - Text("${widget.shortURL.title ?? widget.shortURL.shortCode}", style: TextStyle(fontStyle: FontStyle.italic),), - SizedBox(height: 4), + const SizedBox(height: 4), + Text(widget.shortURL.title ?? widget.shortURL.shortCode, style: const TextStyle(fontStyle: FontStyle.italic),), + const SizedBox(height: 4), const Text("It'll be gone forever! (a very long time)") ], ), @@ -43,7 +43,7 @@ class _URLDetailViewState extends State { Navigator.pop(context); Navigator.pop(context, "reload"); - final snackBar = SnackBar(content: Text("Short URL deleted!"), backgroundColor: Colors.green[400], behavior: SnackBarBehavior.floating); + final snackBar = SnackBar(content: const Text("Short URL deleted!"), backgroundColor: Colors.green[400], behavior: SnackBarBehavior.floating); ScaffoldMessenger.of(context).showSnackBar(snackBar); return true; }, (r) { @@ -60,7 +60,7 @@ class _URLDetailViewState extends State { return false; }); }, - child: Text("Delete", style: TextStyle(color: Colors.red)), + child: const Text("Delete", style: TextStyle(color: Colors.red)), ) ], ); @@ -74,11 +74,11 @@ class _URLDetailViewState extends State { body: CustomScrollView( slivers: [ SliverAppBar.medium( - title: Text(widget.shortURL.title ?? widget.shortURL.shortCode, style: TextStyle(fontWeight: FontWeight.bold)), + title: Text(widget.shortURL.title ?? widget.shortURL.shortCode, style: const TextStyle(fontWeight: FontWeight.bold)), actions: [ IconButton(onPressed: () { showDeletionConfirmation(); - }, icon: Icon(Icons.delete, color: Colors.red,)) + }, icon: const Icon(Icons.delete, color: Colors.red,)) ], ), SliverToBoxAdapter( @@ -88,9 +88,9 @@ class _URLDetailViewState extends State { children: widget.shortURL.tags.map((tag) { var randomColor = ([...Colors.primaries]..shuffle()).first.harmonizeWith(Theme.of(context).colorScheme.primary); return Padding( - padding: EdgeInsets.only(right: 4, top: 4), + padding: const EdgeInsets.only(right: 4, top: 4), child: Container( - padding: EdgeInsets.only(top: 4, bottom: 4, left: 12, right: 12), + padding: const EdgeInsets.only(top: 4, bottom: 4, left: 12, right: 12), decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), color: randomColor, @@ -109,11 +109,11 @@ class _URLDetailViewState extends State { _ListCell(title: "Android", content: widget.shortURL.deviceLongUrls.android, sub: true), _ListCell(title: "Desktop", content: widget.shortURL.deviceLongUrls.desktop, sub: true), _ListCell(title: "Creation Date", content: widget.shortURL.dateCreated), - _ListCell(title: "Visits", content: ""), + const _ListCell(title: "Visits", content: ""), _ListCell(title: "Total", content: widget.shortURL.visitsSummary.total, sub: true), _ListCell(title: "Non-Bots", content: widget.shortURL.visitsSummary.nonBots, sub: true), _ListCell(title: "Bots", content: widget.shortURL.visitsSummary.bots, sub: true), - _ListCell(title: "Meta", content: ""), + const _ListCell(title: "Meta", content: ""), _ListCell(title: "Valid Since", content: widget.shortURL.meta.validSince, sub: true), _ListCell(title: "Valid Until", content: widget.shortURL.meta.validUntil, sub: true), _ListCell(title: "Max Visits", content: widget.shortURL.meta.maxVisits, sub: true), @@ -146,7 +146,7 @@ class _ListCellState extends State<_ListCell> { child: Padding( padding: EdgeInsets.only(top: 16, bottom: widget.last ? 30 : 0), child: Container( - padding: EdgeInsets.only(top: 16, left: 8, right: 8), + padding: const EdgeInsets.only(top: 16, left: 8, right: 8), decoration: BoxDecoration( border: Border(top: BorderSide(width: 1, color: MediaQuery.of(context).platformBrightness == Brightness.dark ? Colors.grey[800]! : Colors.grey[300]!)), ), @@ -157,7 +157,7 @@ class _ListCellState extends State<_ListCell> { children: [ if (widget.sub) Padding( - padding: EdgeInsets.only(right: 4), + padding: const EdgeInsets.only(right: 4), child: SizedBox( width: 20, height: 6, @@ -169,7 +169,7 @@ class _ListCellState extends State<_ListCell> { ), ), ), - Text(widget.title, style: TextStyle(fontWeight: FontWeight.bold),)], + Text(widget.title, style: const TextStyle(fontWeight: FontWeight.bold),)], ), if (widget.content is bool) Icon(widget.content ? Icons.check : Icons.close, color: widget.content ? Colors.green : Colors.red) diff --git a/lib/URLListView.dart b/lib/views/url_list_view.dart similarity index 81% rename from lib/URLListView.dart rename to lib/views/url_list_view.dart index 93b20d8..8e92d27 100644 --- a/lib/URLListView.dart +++ b/lib/views/url_list_view.dart @@ -1,11 +1,11 @@ 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/ShortURL.dart'; -import 'package:shlink_app/API/ServerManager.dart'; -import 'package:shlink_app/ShortURLEditView.dart'; -import 'package:shlink_app/URLDetailView.dart'; -import 'globals.dart' as globals; +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 '../globals.dart' as globals; import 'package:flutter/services.dart'; class URLListView extends StatefulWidget { @@ -59,10 +59,10 @@ class _URLListViewState extends State { return Scaffold( floatingActionButton: FloatingActionButton( onPressed: () async { - final result = await Navigator.of(context).push(MaterialPageRoute(builder: (context) => ShortURLEditView())); + await Navigator.of(context).push(MaterialPageRoute(builder: (context) => const ShortURLEditView())); loadAllShortUrls(); }, - child: Icon(Icons.add), + child: const Icon(Icons.add), ), body: Stack( children: [ @@ -74,19 +74,19 @@ class _URLListViewState extends State { }, child: CustomScrollView( slivers: [ - SliverAppBar.medium( + const SliverAppBar.medium( title: Text("Short URLs", style: TextStyle(fontWeight: FontWeight.bold)) ), - if (shortUrlsLoaded && shortUrls.length == 0) + if (shortUrlsLoaded && shortUrls.isEmpty) SliverToBoxAdapter( child: Center( child: Padding( - padding: EdgeInsets.only(top: 50), + padding: const EdgeInsets.only(top: 50), child: Column( children: [ - Text("No Short URLs", style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),), + const Text("No Short URLs", style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),), Padding( - padding: EdgeInsets.only(top: 8), + padding: const EdgeInsets.only(top: 8), child: Text('Create one by tapping the "+" button below', style: TextStyle(fontSize: 16, color: Colors.grey[600]),), ) ], @@ -96,7 +96,7 @@ class _URLListViewState extends State { ) else SliverList(delegate: SliverChildBuilderDelegate( - (BuildContext _context, int index) { + (BuildContext context, int index) { final shortURL = shortUrls[index]; return ShortURLCell(shortURL: shortURL, reload: () { loadAllShortUrls(); @@ -181,7 +181,7 @@ class _ShortURLCellState extends State { child: Padding( padding: EdgeInsets.only(left: 8, right: 8, bottom: widget.isLast ? 90 : 0), child: Container( - padding: EdgeInsets.only(left: 8, right: 8, bottom: 16, top: 16), + padding: const EdgeInsets.only(left: 8, right: 8, bottom: 16, top: 16), decoration: BoxDecoration( border: Border(bottom: BorderSide(color: MediaQuery.of(context).platformBrightness == Brightness.dark ? Colors.grey[800]! : Colors.grey[300]!)), ), @@ -193,16 +193,16 @@ class _ShortURLCellState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - Text("${widget.shortURL.title ?? widget.shortURL.shortCode}", textScaleFactor: 1.4, style: TextStyle(fontWeight: FontWeight.bold),), - Text("${widget.shortURL.longUrl}",maxLines: 1, overflow: TextOverflow.ellipsis, textScaleFactor: 0.9, style: TextStyle(color: Colors.grey[600]),), + Text(widget.shortURL.title ?? widget.shortURL.shortCode, textScaleFactor: 1.4, style: const TextStyle(fontWeight: FontWeight.bold),), + Text(widget.shortURL.longUrl,maxLines: 1, overflow: TextOverflow.ellipsis, textScaleFactor: 0.9, 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: EdgeInsets.only(right: 4, top: 4), + padding: const EdgeInsets.only(right: 4, top: 4), child: Container( - padding: EdgeInsets.only(top: 4, bottom: 4, left: 12, right: 12), + padding: const EdgeInsets.only(top: 4, bottom: 4, left: 12, right: 12), decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), color: randomColor, @@ -218,12 +218,12 @@ class _ShortURLCellState extends State { ), IconButton(onPressed: () async { await Clipboard.setData(ClipboardData(text: widget.shortURL.shortUrl)); - final snackBar = SnackBar(content: Text("Copied to clipboard!"), behavior: SnackBarBehavior.floating, backgroundColor: Colors.green[400]); + final snackBar = SnackBar(content: const Text("Copied to clipboard!"), behavior: SnackBarBehavior.floating, backgroundColor: Colors.green[400]); ScaffoldMessenger.of(context).showSnackBar(snackBar); - }, icon: Icon(Icons.copy)), + }, icon: const Icon(Icons.copy)), IconButton(onPressed: () { widget.showQRCode(widget.shortURL.shortUrl); - }, icon: Icon(Icons.qr_code)) + }, icon: const Icon(Icons.qr_code)) ], ) ), diff --git a/pubspec.yaml b/pubspec.yaml index 1d4c1bc..21829f3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 0.9.0+4 +version: 0.9.1+5 environment: sdk: ^2.19.0