Loading files
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import '../shared/theme/app_theme.dart' as grid;
import '../core/pull_request_status.dart';
/// A separate PR label: branch colour does not encode review state.
class PullRequestBadge extends StatefulWidget {
const PullRequestBadge({
super.key,
required this.identity,
required this.read,
this.open,
this.compact = false,
});
final Object identity;
final bool compact;
final Future<Map<String, dynamic>> Function() read;
final Future<bool> Function(Uri)? open;
@override
State<PullRequestBadge> createState() => _PullRequestBadgeState();
}
class _PullRequestBadgeState extends State<PullRequestBadge> {
Timer? _timer;
int _revision = 0;
Map<String, dynamic>? _result;
@override
void initState() {
super.initState();
_restart();
}
@override
void didUpdateWidget(PullRequestBadge oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.identity != widget.identity) _restart();
}
void _restart() {
_timer?.cancel();
_result = null;
final revision = ++_revision;
unawaited(_refresh(revision));
}
Future<void> _refresh(int revision) async {
Map<String, dynamic> result;
try {
result = await widget.read();
} catch (_) {
result = {'status': 'unavailable'};
}
if (!mounted || revision != _revision) return;
setState(() => _result = result);
_timer = Timer(const Duration(seconds: 60), () => _refresh(revision));
}
@override
void dispose() {
_revision++;
_timer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
grid.AppTheme.watch(context);
final pr = PullRequestStatus.fromResult(_result);
if (pr == null) return const SizedBox.shrink();
final number = pr.number, state = pr.state, uri = pr.url;
final label = pr.label;
return Tooltip(
message: '#$number $state — Open on GitHub',
child: TextButton(
style: TextButton.styleFrom(
minimumSize: const Size(0, 28),
padding: const EdgeInsets.symmetric(horizontal: 6),
textStyle: grid.AppType.monoLabel(),
),
onPressed: () async {
final opened =
await (widget.open?.call(uri) ??
launchUrl(uri, mode: LaunchMode.externalApplication));
if (!opened && context.mounted) {
ScaffoldMessenger.maybeOf(context)?.showSnackBar(
const SnackBar(content: Text('Could not open GitHub.')),
);
}
},
child: Text(label, maxLines: 1, overflow: TextOverflow.ellipsis),
),
);
}
}
710fbeaaeThis file is pinned to the selected release. Download the original to open it in your design tool.