- 在 HomeScreen 中添加 GiteaCard 组件 - 在 SettingsScreen 中增加 Gitea 用户名和 API Token 设置 - 优化 LeetCodeCard 组件,增加空 cookie 检查 - 调整 SettingsScreen 中平台设置的布局结构
246 lines
8.0 KiB
Dart
246 lines
8.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:http/http.dart' as http;
|
|
import 'dart:convert';
|
|
import '../services/auth_service.dart';
|
|
|
|
class GiteaCard extends StatefulWidget {
|
|
const GiteaCard({super.key});
|
|
|
|
@override
|
|
State<GiteaCard> createState() => _GiteaCardState();
|
|
}
|
|
|
|
class _GiteaCardState extends State<GiteaCard> {
|
|
List<dynamic> _activities = [];
|
|
bool _isLoading = false;
|
|
String? _error;
|
|
|
|
String _getOperationTypeText(String opType) {
|
|
switch (opType) {
|
|
case 'create_repo':
|
|
return '创建仓库';
|
|
case 'commit_repo':
|
|
return '提交代码';
|
|
case 'push_tag':
|
|
return '推送标签';
|
|
case 'create_issue':
|
|
return '创建 Issue';
|
|
case 'comment_issue':
|
|
return '评论 Issue';
|
|
case 'create_pull_request':
|
|
return '创建 Pull Request';
|
|
case 'merge_pull_request':
|
|
return '合并 Pull Request';
|
|
default:
|
|
return opType;
|
|
}
|
|
}
|
|
|
|
Widget _buildCommitContent(String content) {
|
|
try {
|
|
final data = jsonDecode(content);
|
|
final commits = data['Commits'] as List;
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children:
|
|
commits.map((commit) {
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
commit['Message'],
|
|
style: const TextStyle(fontWeight: FontWeight.bold),
|
|
),
|
|
Text(
|
|
'作者: ${commit['AuthorName']} <${commit['AuthorEmail']}>',
|
|
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
|
),
|
|
Text(
|
|
'提交时间: ${commit['Timestamp']}',
|
|
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}).toList(),
|
|
);
|
|
} catch (e) {
|
|
return Text(content);
|
|
}
|
|
}
|
|
|
|
Future<void> _fetchActivities() async {
|
|
setState(() {
|
|
_isLoading = true;
|
|
_error = null;
|
|
});
|
|
|
|
try {
|
|
final authService = Provider.of<AuthService>(context, listen: false);
|
|
final token = authService.credentials['gitea'];
|
|
final username = authService.credentials['gitea_username'];
|
|
|
|
if (token == null || token.isEmpty) {
|
|
throw Exception('未配置 Gitea Token');
|
|
}
|
|
if (username == null || username.isEmpty) {
|
|
throw Exception('未配置 Gitea 用户名');
|
|
}
|
|
|
|
final response = await http.get(
|
|
Uri.parse(
|
|
'https://git.jdysya.top/api/v1/users/$username/activities/feeds',
|
|
),
|
|
headers: {'Authorization': 'token $token'},
|
|
);
|
|
|
|
if (response.statusCode == 200) {
|
|
setState(() {
|
|
_activities = jsonDecode(response.body);
|
|
_isLoading = false;
|
|
});
|
|
} else {
|
|
throw Exception('请求失败: ${response.statusCode}');
|
|
}
|
|
} catch (e) {
|
|
setState(() {
|
|
_error = e.toString();
|
|
_isLoading = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_fetchActivities();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Card(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
const Text(
|
|
'Gitea 最近活动',
|
|
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
|
),
|
|
IconButton(
|
|
icon: const Icon(Icons.refresh),
|
|
onPressed: _isLoading ? null : _fetchActivities,
|
|
),
|
|
],
|
|
),
|
|
if (_isLoading)
|
|
const Center(
|
|
child: Padding(
|
|
padding: EdgeInsets.all(16.0),
|
|
child: CircularProgressIndicator(),
|
|
),
|
|
)
|
|
else if (_error != null)
|
|
Center(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Text(
|
|
_error!,
|
|
style: const TextStyle(color: Colors.red),
|
|
),
|
|
),
|
|
)
|
|
else if (_activities.isEmpty)
|
|
const Center(
|
|
child: Padding(
|
|
padding: EdgeInsets.all(16.0),
|
|
child: Text('暂无活动记录'),
|
|
),
|
|
)
|
|
else
|
|
Expanded(
|
|
child: ListView.builder(
|
|
itemCount: _activities.length,
|
|
itemBuilder: (context, index) {
|
|
final activity = _activities[index];
|
|
final repo = activity['repo'];
|
|
final created = DateTime.parse(activity['created']);
|
|
|
|
return Card(
|
|
margin: const EdgeInsets.symmetric(vertical: 4.0),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(12.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
CircleAvatar(
|
|
radius: 16,
|
|
child: Text(
|
|
activity['act_user']['login'][0]
|
|
.toUpperCase(),
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
Text(
|
|
activity['act_user']['login'],
|
|
style: const TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
Text(
|
|
_getOperationTypeText(activity['op_type']),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 8),
|
|
if (repo != null)
|
|
Text(
|
|
repo['full_name'],
|
|
style: const TextStyle(
|
|
color: Colors.blue,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
if (activity['content'] != null)
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: 8.0),
|
|
child:
|
|
activity['op_type'] == 'commit_repo'
|
|
? _buildCommitContent(
|
|
activity['content'],
|
|
)
|
|
: Text(activity['content']),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
'时间: ${created.toString().substring(0, 19)}',
|
|
style: const TextStyle(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|