diff --git a/README.md b/README.md index 00c28b9..ddf12dd 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,10 @@ -# dashboard +## dashboard -A new Flutter project. +个人项目信息面板 -## Getting Started +## 配置获取 -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) - -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev/), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +- leetcode:浏览器获取随便找一个xhr请求,从Header中获取cookie,user-slug在个人页面url中 +- gitea: 用户设置 >> 应用 >> 生成令牌(授予全部权限) +- kodbox: 参考官方文档[获取accesstoken](https://doc.kodcloud.com/v2/#/user/user?id=%e8%8e%b7%e5%8f%96accesstoken)的方式 +- github: Settings >> Developer Settings >> Personal access tokens (classic) \ No newline at end of file diff --git a/lib/widgets/github_card.dart b/lib/widgets/github_card.dart index fd22651..225aad8 100644 --- a/lib/widgets/github_card.dart +++ b/lib/widgets/github_card.dart @@ -14,101 +14,200 @@ class GithubCard extends StatefulWidget { class _GithubCardState extends State { final GithubService _githubService = GithubService(); - late Future> _eventsFuture; + List _events = []; + bool _isLoading = false; + String? _error; + + String _getEventTypeText(String type) { + switch (type) { + case 'PushEvent': + return '推送代码'; + case 'CreateEvent': + return '创建仓库'; + case 'IssuesEvent': + return 'Issue 操作'; + case 'PullRequestEvent': + return 'Pull Request 操作'; + case 'ForkEvent': + return 'Fork 仓库'; + case 'WatchEvent': + return '关注仓库'; + case 'StarEvent': + return 'Star 仓库'; + default: + return type; + } + } + + Widget _buildCommitContent(List commits) { + 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.author.name} <${commit.author.email}>', + style: const TextStyle(fontSize: 12, color: Colors.grey), + ), + ], + ), + ); + }).toList(), + ); + } + + Future _fetchEvents() async { + setState(() { + _isLoading = true; + _error = null; + }); + + try { + final authService = Provider.of(context, listen: false); + final username = authService.credentials['github_username']; + final token = authService.credentials['github_token']; + + if (username == null || token == null) { + throw Exception('请在设置中配置 GitHub 用户名和 Token'); + } + + final events = await _githubService.getUserEvents(username, token); + setState(() { + _events = events; + _isLoading = false; + }); + } catch (e) { + setState(() { + _error = e.toString(); + _isLoading = false; + }); + } + } @override void initState() { super.initState(); - _loadEvents(); - } - - void _loadEvents() { - final authService = Provider.of(context, listen: false); - final username = authService.credentials['github_username']; - final token = authService.credentials['github_token']; - - if (username != null && token != null) { - _eventsFuture = _githubService.getUserEvents(username, token); - } + _fetchEvents(); } @override Widget build(BuildContext context) { - final authService = Provider.of(context); - final username = authService.credentials['github_username']; - final token = authService.credentials['github_token']; - - if (username == null || token == null) { - return const Center(child: Text('请在设置中配置 GitHub 用户名和 Token')); - } - - return FutureBuilder>( - future: _eventsFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } - - if (snapshot.hasError) { - return Center(child: Text('加载失败: ${snapshot.error}')); - } - - if (!snapshot.hasData || snapshot.data!.isEmpty) { - return const Center(child: Text('暂无活动数据')); - } - - return ListView.builder( - itemCount: snapshot.data!.length, - itemBuilder: (context, index) { - final event = snapshot.data![index]; - return Card( - margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: ListTile( - leading: CircleAvatar( - backgroundImage: NetworkImage(event.actor.avatarUrl), + return Card( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + 'GitHub 最近活动', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), - title: Text( - _getEventTitle(event), - style: const TextStyle(fontWeight: FontWeight.bold), + IconButton( + icon: const Icon(Icons.refresh), + onPressed: _isLoading ? null : _fetchEvents, ), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('仓库: ${event.repo.name}'), - Text( - '时间: ${timeago.format(event.createdAt, locale: "zh_CN")}', - ), - if (event.type == 'PushEvent' && - event.payload.commits != null) - ...event.payload.commits!.map( - (commit) => Text( - '提交: ${commit.message}', - style: const TextStyle(fontSize: 12), + ], + ), + 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 (_events.isEmpty) + const Center( + child: Padding( + padding: EdgeInsets.all(16.0), + child: Text('暂无活动记录'), + ), + ) + else + Expanded( + child: ListView.builder( + itemCount: _events.length, + itemBuilder: (context, index) { + final event = _events[index]; + 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( + backgroundImage: NetworkImage( + event.actor.avatarUrl, + ), + ), + const SizedBox(width: 8), + Text( + event.actor.login, + style: const TextStyle( + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(width: 8), + Text(_getEventTypeText(event.type)), + ], + ), + const SizedBox(height: 8), + Text( + event.repo.name, + style: const TextStyle( + color: Colors.blue, + fontWeight: FontWeight.bold, + ), + ), + if (event.type == 'PushEvent' && + event.payload.commits != null) + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: _buildCommitContent( + event.payload.commits!, + ), + ), + const SizedBox(height: 8), + Text( + '时间: ${timeago.format(event.createdAt, locale: 'zh_CN')}', + style: const TextStyle( + fontSize: 12, + color: Colors.grey, + ), + ), + ], ), ), - ], + ); + }, ), - isThreeLine: true, ), - ); - }, - ); - }, + ], + ), + ), ); } - - String _getEventTitle(GithubEvent event) { - switch (event.type) { - case 'WatchEvent': - return '${event.actor.login} 关注了 ${event.repo.name}'; - case 'PushEvent': - return '${event.actor.login} 推送了代码到 ${event.repo.name}'; - case 'CreateEvent': - return '${event.actor.login} 创建了 ${event.repo.name}'; - case 'ForkEvent': - return '${event.actor.login} Fork 了 ${event.repo.name}'; - default: - return '${event.actor.login} 在 ${event.repo.name} 进行了 ${event.type} 操作'; - } - } }