feat: add app update checks and standalone release publishing panel
This commit is contained in:
@@ -24,6 +24,8 @@ class ProfilePage extends StatelessWidget {
|
||||
this.logoutLabel = '退出学校登录',
|
||||
this.onUpdateTerm,
|
||||
this.feedbackSender = const UnconfiguredFeedbackSender(),
|
||||
this.onCheckUpdates,
|
||||
this.checkingUpdates = false,
|
||||
});
|
||||
final AppController controller;
|
||||
final VoidCallback onLogin;
|
||||
@@ -32,6 +34,8 @@ class ProfilePage extends StatelessWidget {
|
||||
final VoidCallback? onUpdateTerm;
|
||||
final String authStatus, logoutLabel;
|
||||
final FeedbackSender feedbackSender;
|
||||
final VoidCallback? onCheckUpdates;
|
||||
final bool checkingUpdates;
|
||||
Future<void> _import(BuildContext context) async {
|
||||
try {
|
||||
final file = await FilePicker.pickFile(
|
||||
@@ -231,6 +235,32 @@ class ProfilePage extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
Divider(height: 1, color: ink.withValues(alpha: 0.06)),
|
||||
ListTile(
|
||||
key: const Key('check-app-update'),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
leading: const Icon(
|
||||
Icons.system_update_rounded,
|
||||
color: river,
|
||||
size: 23,
|
||||
),
|
||||
title: const Text(
|
||||
'检查更新',
|
||||
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
|
||||
),
|
||||
subtitle: Text(
|
||||
checkingUpdates ? '正在检查…' : '当前版本 $appVersion',
|
||||
style: const TextStyle(fontSize: 11),
|
||||
),
|
||||
trailing: checkingUpdates
|
||||
? const SizedBox(
|
||||
width: 18,
|
||||
height: 18,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
)
|
||||
: const Icon(Icons.chevron_right_rounded),
|
||||
onTap: checkingUpdates ? null : onCheckUpdates,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../app/app_version.dart';
|
||||
import '../../services/app_update_service.dart';
|
||||
|
||||
Future<void> showUpdatePrompt(
|
||||
BuildContext context,
|
||||
AppRelease release, {
|
||||
UpdateDownloadOpener openDownload = openUpdateInBrowser,
|
||||
}) async {
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text('发现新版本 ${release.version}'),
|
||||
content: SizedBox(
|
||||
width: 360,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('当前版本 $appVersion', style: TextStyle(fontSize: 12)),
|
||||
const SizedBox(height: 16),
|
||||
Text(release.notes, style: const TextStyle(height: 1.6)),
|
||||
const SizedBox(height: 16),
|
||||
const Text(
|
||||
'将打开浏览器下载,安装时请保留现有应用和课表。',
|
||||
style: TextStyle(fontSize: 12),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
child: const Text('稍后'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
child: const Text('立即更新'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
if (confirmed != true || !context.mounted) return;
|
||||
bool opened;
|
||||
try {
|
||||
opened = await openDownload(release.downloadUri);
|
||||
} catch (_) {
|
||||
opened = false;
|
||||
}
|
||||
if (!opened && context.mounted) {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('未能打开浏览器'),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('可以复制下方链接到浏览器下载:'),
|
||||
const SizedBox(height: 12),
|
||||
SelectableText(release.downloadUri.toString()),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('知道了'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user