feat(auth): 重构认证服务并添加时间格式化功能
- 重命名和更新认证服务中的配置键名 - 在主入口文件中添加时间格式化库的初始化 - 更新设置屏幕中的配置加载和保存逻辑 - 在各个卡片组件中使用时间格式化库显示时间信息
This commit is contained in:
parent
ae49aa13df
commit
7b1b7288eb
3
devtools_options.yaml
Normal file
3
devtools_options.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
description: This file stores settings for Dart & Flutter DevTools.
|
||||
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
|
||||
extensions:
|
||||
@ -2,8 +2,10 @@ import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'screens/home_screen.dart';
|
||||
import 'services/auth_service.dart';
|
||||
import 'package:timeago/timeago.dart' as timeago;
|
||||
|
||||
void main() {
|
||||
timeago.setLocaleMessages('zh_CN', timeago.ZhCnMessages());
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
Provider.of<AuthService>(context, listen: false).loadCredentials();
|
||||
Provider.of<AuthService>(context, listen: false).loadConfigs();
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@ -33,14 +33,15 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
|
||||
Future<void> _loadSettings() async {
|
||||
final authService = Provider.of<AuthService>(context, listen: false);
|
||||
await authService.loadCredentials();
|
||||
await authService.loadConfigs();
|
||||
|
||||
setState(() {
|
||||
_leetcodeController.text = authService.credentials['leetcode'] ?? '';
|
||||
_giteaController.text = authService.credentials['gitea'] ?? '';
|
||||
_leetcodeController.text =
|
||||
authService.credentials['leetcode_cookie'] ?? '';
|
||||
_giteaController.text = authService.credentials['gitea_token'] ?? '';
|
||||
_giteaUsernameController.text =
|
||||
authService.credentials['gitea_username'] ?? '';
|
||||
_kodboxController.text = authService.credentials['kodbox'] ?? '';
|
||||
_kodboxController.text = authService.credentials['kodbox_token'] ?? '';
|
||||
});
|
||||
}
|
||||
|
||||
@ -49,19 +50,22 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
final authService = Provider.of<AuthService>(context, listen: false);
|
||||
|
||||
if (_leetcodeController.text.isNotEmpty) {
|
||||
await authService.saveCredentials('leetcode', _leetcodeController.text);
|
||||
await authService.saveConfigs(
|
||||
'leetcode_cookie',
|
||||
_leetcodeController.text,
|
||||
);
|
||||
}
|
||||
if (_giteaController.text.isNotEmpty) {
|
||||
await authService.saveCredentials('gitea', _giteaController.text);
|
||||
await authService.saveConfigs('gitea_token', _giteaController.text);
|
||||
}
|
||||
if (_giteaUsernameController.text.isNotEmpty) {
|
||||
await authService.saveCredentials(
|
||||
await authService.saveConfigs(
|
||||
'gitea_username',
|
||||
_giteaUsernameController.text,
|
||||
);
|
||||
}
|
||||
if (_kodboxController.text.isNotEmpty) {
|
||||
await authService.saveCredentials('kodbox', _kodboxController.text);
|
||||
await authService.saveConfigs('kodbox_token', _kodboxController.text);
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
|
||||
@ -9,20 +9,32 @@ class AuthService extends ChangeNotifier {
|
||||
bool get isAuthenticated => _isAuthenticated;
|
||||
Map<String, String> get credentials => _credentials;
|
||||
|
||||
Future<void> saveCredentials(String platform, String cookie) async {
|
||||
await _storage.write(key: 'cookie_$platform', value: cookie);
|
||||
_credentials[platform] = cookie;
|
||||
Future<void> saveConfigs(String key, String value) async {
|
||||
await _storage.write(key: key, value: value);
|
||||
_credentials[key] = value;
|
||||
_isAuthenticated = true;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> loadCredentials() async {
|
||||
final leetcodeCookie = await _storage.read(key: 'cookie_leetcode');
|
||||
Future<void> loadConfigs() async {
|
||||
final leetcodeCookie = await _storage.read(key: 'leetcode_cookie');
|
||||
final giteaToken = await _storage.read(key: 'gitea_token');
|
||||
final giteaUserName = await _storage.read(key: 'gitea_username');
|
||||
final kodboxToken = await _storage.read(key: 'kodbox_token');
|
||||
if (leetcodeCookie != null) {
|
||||
_credentials['leetcode'] = leetcodeCookie;
|
||||
_isAuthenticated = true;
|
||||
notifyListeners();
|
||||
_credentials['leetcode_cookie'] = leetcodeCookie;
|
||||
}
|
||||
if (giteaToken != null) {
|
||||
_credentials['gitea_token'] = giteaToken;
|
||||
}
|
||||
if (giteaUserName != null) {
|
||||
_credentials['gitea_username'] = giteaUserName;
|
||||
}
|
||||
if (kodboxToken != null) {
|
||||
_credentials['kodbox_token'] = kodboxToken;
|
||||
}
|
||||
_isAuthenticated = true;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> logout() async {
|
||||
|
||||
23
lib/utils/time_utils.dart
Normal file
23
lib/utils/time_utils.dart
Normal file
@ -0,0 +1,23 @@
|
||||
class TimeUtils {
|
||||
static String getRelativeTime(String timestamp) {
|
||||
final now = DateTime.now();
|
||||
final date = DateTime.fromMillisecondsSinceEpoch(
|
||||
(double.parse(timestamp) * 1000).round(),
|
||||
);
|
||||
final difference = now.difference(date);
|
||||
|
||||
if (difference.inSeconds < 60) {
|
||||
return '刚刚';
|
||||
} else if (difference.inMinutes < 60) {
|
||||
return '${difference.inMinutes}分钟前';
|
||||
} else if (difference.inHours < 24) {
|
||||
return '${difference.inHours}小时前';
|
||||
} else if (difference.inDays < 30) {
|
||||
return '${difference.inDays}天前';
|
||||
} else if (difference.inDays < 365) {
|
||||
return '${(difference.inDays / 30).round()}个月前';
|
||||
} else {
|
||||
return '${(difference.inDays / 365).round()}年前';
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:convert';
|
||||
import '../services/auth_service.dart';
|
||||
import 'package:timeago/timeago.dart' as timeago;
|
||||
|
||||
class GiteaCard extends StatefulWidget {
|
||||
const GiteaCard({super.key});
|
||||
@ -60,7 +62,7 @@ class _GiteaCardState extends State<GiteaCard> {
|
||||
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||
),
|
||||
Text(
|
||||
'提交时间: ${commit['Timestamp']}',
|
||||
'提交时间: ${DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.parse(commit['Timestamp']))}',
|
||||
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||
),
|
||||
],
|
||||
@ -81,7 +83,7 @@ class _GiteaCardState extends State<GiteaCard> {
|
||||
|
||||
try {
|
||||
final authService = Provider.of<AuthService>(context, listen: false);
|
||||
final token = authService.credentials['gitea'];
|
||||
final token = authService.credentials['gitea_token'];
|
||||
final username = authService.credentials['gitea_username'];
|
||||
|
||||
if (token == null || token.isEmpty) {
|
||||
@ -172,7 +174,7 @@ class _GiteaCardState extends State<GiteaCard> {
|
||||
itemBuilder: (context, index) {
|
||||
final activity = _activities[index];
|
||||
final repo = activity['repo'];
|
||||
final created = DateTime.parse(activity['created']);
|
||||
final created = activity['created'];
|
||||
|
||||
return Card(
|
||||
margin: const EdgeInsets.symmetric(vertical: 4.0),
|
||||
@ -224,7 +226,7 @@ class _GiteaCardState extends State<GiteaCard> {
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'时间: ${created.toString().substring(0, 19)}',
|
||||
'时间: ${timeago.format(DateTime.parse(created), locale: 'zh_CN')}',
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey,
|
||||
|
||||
@ -3,6 +3,7 @@ import 'package:provider/provider.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:convert';
|
||||
import '../services/auth_service.dart';
|
||||
import 'package:timeago/timeago.dart' as timeago;
|
||||
|
||||
class KodBoxCard extends StatefulWidget {
|
||||
const KodBoxCard({super.key});
|
||||
@ -24,7 +25,7 @@ class _KodBoxCardState extends State<KodBoxCard> {
|
||||
|
||||
try {
|
||||
final authService = Provider.of<AuthService>(context, listen: false);
|
||||
final token = authService.credentials['kodbox'];
|
||||
final token = authService.credentials['kodbox_token'];
|
||||
|
||||
if (token == null || token.isEmpty) {
|
||||
throw Exception('未配置 KodBox Token');
|
||||
@ -74,11 +75,16 @@ class _KodBoxCardState extends State<KodBoxCard> {
|
||||
}
|
||||
}
|
||||
|
||||
String _formatTimestamp(String timestamp) {
|
||||
final date = DateTime.fromMillisecondsSinceEpoch(
|
||||
(double.parse(timestamp) * 1000).round(),
|
||||
);
|
||||
return date.toString().substring(0, 19);
|
||||
String _getFilePath(Map<String, dynamic> desc) {
|
||||
if (desc['sourceInfo'] == false) {
|
||||
// 处理删除文件的情况
|
||||
final parentInfo = desc['parentInfo'];
|
||||
final fileName = desc['desc']?['content'] ?? '未知文件';
|
||||
return '${parentInfo['pathDisplay']}$fileName';
|
||||
} else if (desc['sourceInfo'] != null) {
|
||||
return desc['sourceInfo']['pathDisplay'] ?? '未知路径';
|
||||
}
|
||||
return '未知路径';
|
||||
}
|
||||
|
||||
Widget _buildLogItem(Map<String, dynamic> log) {
|
||||
@ -109,16 +115,16 @@ class _KodBoxCardState extends State<KodBoxCard> {
|
||||
Text(log['title'] ?? '未知操作'),
|
||||
],
|
||||
),
|
||||
if (isFileOperation && desc['sourceInfo'] != null) ...[
|
||||
if (isFileOperation) ...[
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'文件路径: ${desc['sourceInfo']['pathDisplay']}',
|
||||
'文件路径: ${_getFilePath(desc)}',
|
||||
style: const TextStyle(color: Colors.blue),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'时间: ${_formatTimestamp(log['createTime'])}',
|
||||
'时间: ${timeago.format(DateTime.fromMillisecondsSinceEpoch(int.parse(log['createTime']) * 1000), locale: 'zh_CN')}',
|
||||
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||
),
|
||||
],
|
||||
|
||||
@ -3,6 +3,7 @@ import 'package:provider/provider.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:convert';
|
||||
import '../services/auth_service.dart';
|
||||
import 'package:timeago/timeago.dart' as timeago;
|
||||
|
||||
class LeetCodeCard extends StatefulWidget {
|
||||
const LeetCodeCard({super.key});
|
||||
@ -24,7 +25,7 @@ class _LeetCodeCardState extends State<LeetCodeCard> {
|
||||
|
||||
try {
|
||||
final authService = Provider.of<AuthService>(context, listen: false);
|
||||
final cookie = authService.credentials['leetcode'];
|
||||
final cookie = authService.credentials['leetcode_cookie'];
|
||||
|
||||
if (cookie == null || cookie.isEmpty) {
|
||||
throw Exception('未配置 LeetCode Cookie');
|
||||
@ -128,16 +129,13 @@ class _LeetCodeCardState extends State<LeetCodeCard> {
|
||||
itemBuilder: (context, index) {
|
||||
final submission = _submissions[index];
|
||||
final question = submission['question'];
|
||||
final submitTime = DateTime.fromMillisecondsSinceEpoch(
|
||||
submission['submitTime'] * 1000,
|
||||
);
|
||||
|
||||
return ListTile(
|
||||
title: Text(
|
||||
question['translatedTitle'] ?? question['title'],
|
||||
),
|
||||
subtitle: Text(
|
||||
'提交时间: ${submitTime.toString().substring(0, 19)}',
|
||||
'提交时间: ${timeago.format(DateTime.fromMillisecondsSinceEpoch(submission['submitTime'] * 1000), locale: 'zh_CN')}',
|
||||
),
|
||||
trailing: Text('#${question['questionFrontendId']}'),
|
||||
);
|
||||
|
||||
@ -549,6 +549,14 @@ packages:
|
||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||
source: hosted
|
||||
version: "0.7.4"
|
||||
timeago:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: timeago
|
||||
sha256: b05159406a97e1cbb2b9ee4faa9fb096fe0e2dfcd8b08fcd2a00553450d3422e
|
||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||
source: hosted
|
||||
version: "3.7.1"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@ -40,6 +40,7 @@ dependencies:
|
||||
intl: ^0.19.0
|
||||
flutter_secure_storage: ^9.0.0
|
||||
cached_network_image: ^3.3.1
|
||||
timeago: ^3.7.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user