shlink-manager/lib/views/url_detail_view.dart

270 lines
9.9 KiB
Dart
Raw Normal View History

2023-07-09 23:00:00 +02:00
import 'package:flutter/material.dart';
2024-01-27 23:07:06 +01:00
import 'package:shlink_app/API/Classes/ShortURL/short_url.dart';
2023-07-09 23:00:00 +02:00
import 'package:intl/intl.dart';
2024-01-27 23:07:06 +01:00
import 'package:shlink_app/API/server_manager.dart';
2024-07-25 17:31:54 +02:00
import 'package:shlink_app/views/redirect_rules_detail_view.dart';
2024-01-28 22:55:28 +01:00
import 'package:shlink_app/views/short_url_edit_view.dart';
2024-01-28 01:05:35 +01:00
import 'package:shlink_app/widgets/url_tags_list_widget.dart';
import 'package:url_launcher/url_launcher.dart';
2024-01-27 23:07:06 +01:00
import '../globals.dart' as globals;
2023-07-09 23:00:00 +02:00
class URLDetailView extends StatefulWidget {
const URLDetailView({super.key, required this.shortURL});
final ShortURL shortURL;
@override
State<URLDetailView> createState() => _URLDetailViewState();
}
class _URLDetailViewState extends State<URLDetailView> {
2024-01-28 22:55:28 +01:00
ShortURL shortURL = ShortURL.empty();
@override
void initState() {
super.initState();
setState(() {
shortURL = widget.shortURL;
});
}
2023-07-09 23:00:00 +02:00
Future showDeletionConfirmation() {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("Delete Short URL"),
content: SingleChildScrollView(
child: ListBody(
children: [
const Text("You're about to delete"),
2024-01-27 23:07:06 +01:00
const SizedBox(height: 4),
2024-01-28 00:32:09 +01:00
Text(
2024-01-28 22:55:28 +01:00
shortURL.title ?? shortURL.shortCode,
2024-01-28 00:32:09 +01:00
style: const TextStyle(fontStyle: FontStyle.italic),
),
2024-01-27 23:07:06 +01:00
const SizedBox(height: 4),
2023-07-09 23:00:00 +02:00
const Text("It'll be gone forever! (a very long time)")
],
),
),
actions: [
2024-01-28 00:32:09 +01:00
TextButton(
onPressed: () => {Navigator.of(context).pop()},
child: const Text("Cancel")),
2023-07-09 23:00:00 +02:00
TextButton(
onPressed: () async {
2024-01-28 00:32:09 +01:00
var response = await globals.serverManager
2024-01-28 22:55:28 +01:00
.deleteShortUrl(shortURL.shortCode);
2023-07-09 23:00:00 +02:00
response.fold((l) {
Navigator.pop(context);
2024-01-28 22:55:28 +01:00
Navigator.pop(context);
2023-07-09 23:00:00 +02:00
2024-01-28 00:32:09 +01:00
final snackBar = SnackBar(
content: const Text("Short URL deleted!"),
backgroundColor: Colors.green[400],
behavior: SnackBarBehavior.floating);
2023-07-09 23:00:00 +02:00
ScaffoldMessenger.of(context).showSnackBar(snackBar);
return true;
}, (r) {
var text = "";
if (r is RequestFailure) {
text = r.description;
2024-01-28 00:32:09 +01:00
} else {
2023-07-09 23:00:00 +02:00
text = (r as ApiFailure).detail;
}
2024-01-28 00:32:09 +01:00
final snackBar = SnackBar(
content: Text(text),
backgroundColor: Colors.red[400],
behavior: SnackBarBehavior.floating);
2023-07-09 23:00:00 +02:00
ScaffoldMessenger.of(context).showSnackBar(snackBar);
return false;
});
},
2024-01-28 00:32:09 +01:00
child:
const Text("Delete", style: TextStyle(color: Colors.red)),
2023-07-09 23:00:00 +02:00
)
],
);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: [
SliverAppBar.medium(
2024-01-28 22:55:28 +01:00
title: Text(shortURL.title ?? shortURL.shortCode,
2024-01-28 00:32:09 +01:00
style: const TextStyle(fontWeight: FontWeight.bold)),
2023-07-09 23:00:00 +02:00
actions: [
2024-01-28 22:55:28 +01:00
IconButton(
2024-03-31 21:58:31 +02:00
onPressed: () async {
ShortURL updatedUrl = await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
ShortURLEditView(shortUrl: shortURL)));
setState(() {
shortURL = updatedUrl;
});
},
icon: const Icon(Icons.edit)),
2024-01-28 00:32:09 +01:00
IconButton(
onPressed: () {
showDeletionConfirmation();
},
icon: const Icon(
Icons.delete,
color: Colors.red,
))
2023-07-09 23:00:00 +02:00
],
),
SliverToBoxAdapter(
child: Padding(
2024-03-31 21:58:31 +02:00
padding: const EdgeInsets.only(left: 16.0, right: 16.0),
child: UrlTagsListWidget(tags: shortURL.tags)),
2023-07-09 23:00:00 +02:00
),
2024-01-28 22:55:28 +01:00
_ListCell(title: "Short Code", content: shortURL.shortCode),
2024-01-28 00:32:09 +01:00
_ListCell(
2024-03-31 21:58:31 +02:00
title: "Short URL", content: shortURL.shortUrl, isUrl: true),
_ListCell(title: "Long URL", content: shortURL.longUrl, isUrl: true),
_ListCell(title: "Creation Date", content: shortURL.dateCreated),
2024-07-25 17:37:17 +02:00
_ListCell(
title: "Redirect Rules",
content: null,
2024-07-25 17:31:54 +02:00
clickableDetailView: RedirectRulesDetailView(shortURL: shortURL)),
2024-01-27 23:07:06 +01:00
const _ListCell(title: "Visits", content: ""),
2024-01-28 00:32:09 +01:00
_ListCell(
2024-03-31 21:58:31 +02:00
title: "Total", content: shortURL.visitsSummary.total, sub: true),
2024-01-28 00:32:09 +01:00
_ListCell(
title: "Non-Bots",
2024-01-28 22:55:28 +01:00
content: shortURL.visitsSummary.nonBots,
2024-01-28 00:32:09 +01:00
sub: true),
_ListCell(
2024-03-31 21:58:31 +02:00
title: "Bots", content: shortURL.visitsSummary.bots, sub: true),
2024-01-27 23:07:06 +01:00
const _ListCell(title: "Meta", content: ""),
2024-01-28 00:32:09 +01:00
_ListCell(
title: "Valid Since",
2024-01-28 22:55:28 +01:00
content: shortURL.meta.validSince,
2024-01-28 00:32:09 +01:00
sub: true),
_ListCell(
title: "Valid Until",
2024-01-28 22:55:28 +01:00
content: shortURL.meta.validUntil,
2024-01-28 00:32:09 +01:00
sub: true),
_ListCell(
2024-03-31 21:58:31 +02:00
title: "Max Visits", content: shortURL.meta.maxVisits, sub: true),
2024-01-28 22:55:28 +01:00
_ListCell(title: "Domain", content: shortURL.domain),
2024-03-31 21:58:31 +02:00
_ListCell(title: "Crawlable", content: shortURL.crawlable, last: true)
2023-07-09 23:00:00 +02:00
],
),
);
}
}
class _ListCell extends StatefulWidget {
2024-01-28 00:32:09 +01:00
const _ListCell(
{required this.title,
required this.content,
this.sub = false,
this.last = false,
2024-07-25 17:31:54 +02:00
this.isUrl = false,
this.clickableDetailView = null});
2023-07-09 23:00:00 +02:00
final String title;
final dynamic content;
final bool sub;
final bool last;
final bool isUrl;
2024-07-25 17:31:54 +02:00
final Widget? clickableDetailView;
2023-07-09 23:00:00 +02:00
@override
State<_ListCell> createState() => _ListCellState();
}
class _ListCellState extends State<_ListCell> {
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
2024-01-28 00:32:09 +01:00
child: Padding(
2024-03-31 21:58:31 +02:00
padding: EdgeInsets.only(top: 16, bottom: widget.last ? 30 : 0),
child: GestureDetector(
onTap: () async {
2024-07-25 17:31:54 +02:00
if (widget.clickableDetailView != null) {
2024-07-25 17:37:17 +02:00
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => widget.clickableDetailView!));
2024-07-25 17:31:54 +02:00
} else if (widget.content is String) {
Uri? parsedUrl = Uri.tryParse(widget.content);
if (widget.isUrl &&
parsedUrl != null &&
await canLaunchUrl(parsedUrl)) {
launchUrl(parsedUrl);
}
2024-03-31 21:58:31 +02:00
}
},
child: Container(
padding: const EdgeInsets.only(top: 16, left: 8, right: 8),
decoration: BoxDecoration(
border: Border(
top: BorderSide(
color: MediaQuery.of(context).platformBrightness ==
Brightness.dark
? Colors.grey[800]!
: Colors.grey[300]!)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
if (widget.sub)
Padding(
padding: const EdgeInsets.only(right: 4),
child: SizedBox(
width: 20,
height: 6,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Theme.of(context).brightness ==
Brightness.dark
? Colors.grey[700]
: Colors.grey[300],
),
),
),
),
2024-03-31 21:58:31 +02:00
Text(
widget.title,
style: const TextStyle(fontWeight: FontWeight.bold),
)
],
2023-07-09 23:00:00 +02:00
),
2024-03-31 21:58:31 +02:00
if (widget.content is bool)
Icon(widget.content ? Icons.check : Icons.close,
color: widget.content ? Colors.green : Colors.red)
else if (widget.content is int)
Text(widget.content.toString())
else if (widget.content is String)
Expanded(
child: Text(
widget.content,
textAlign: TextAlign.end,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
)
else if (widget.content is DateTime)
Text(DateFormat('yyyy-MM-dd - HH:mm')
.format(widget.content))
2024-07-25 17:31:54 +02:00
else if (widget.clickableDetailView != null)
2024-07-25 17:37:17 +02:00
const Icon(Icons.chevron_right)
2024-03-31 21:58:31 +02:00
else
const Text("N/A")
],
),
),
2024-03-31 21:58:31 +02:00
)));
2023-07-09 23:00:00 +02:00
}
}