From 48e3b510cf5a7a726b685a40201a28103513c0a2 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Mon, 14 Oct 2024 22:05:49 +0800 Subject: [PATCH] :sparkles: Better audit logs --- assets/locales/en_us.json | 3 +- assets/locales/zh_cn.json | 3 +- lib/screens/account/audit_log.dart | 130 +++++++++++++++++++------- lib/screens/posts/post_detail.dart | 7 +- lib/widgets/channel/channel_list.dart | 12 ++- pubspec.lock | 16 ++++ pubspec.yaml | 1 + 7 files changed, 126 insertions(+), 46 deletions(-) diff --git a/assets/locales/en_us.json b/assets/locales/en_us.json index e827f65..2b6a569 100644 --- a/assets/locales/en_us.json +++ b/assets/locales/en_us.json @@ -485,5 +485,6 @@ "auditLog": "Audit log", "shareImage": "Share as image", "shareImageFooter": "Only on the Solar Network", - "fileSavedAt": "File saved at @path" + "fileSavedAt": "File saved at @path", + "showIp": "Show IP Address" } diff --git a/assets/locales/zh_cn.json b/assets/locales/zh_cn.json index 63ef445..99374d3 100644 --- a/assets/locales/zh_cn.json +++ b/assets/locales/zh_cn.json @@ -481,5 +481,6 @@ "auditLog": "活动日志", "shareImage": "分享图片", "shareImageFooter": "上 Solar Network 看更多有趣帖子", - "fileSavedAt": "文件保存于 @path" + "fileSavedAt": "文件保存于 @path", + "showIp": "显示 IP 地址" } diff --git a/lib/screens/account/audit_log.dart b/lib/screens/account/audit_log.dart index 4012c70..94e7e80 100644 --- a/lib/screens/account/audit_log.dart +++ b/lib/screens/account/audit_log.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:get/get.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:marquee/marquee.dart'; import 'package:solian/exceptions/request.dart'; import 'package:solian/exts.dart'; import 'package:solian/models/audit_log.dart'; @@ -29,7 +30,7 @@ class _AuditLogScreenState extends State { final AuthProvider auth = Get.find(); final client = await auth.configureClient('id'); final resp = - await client.get('/users/me/events?take=10&offset=${_events.length}'); + await client.get('/users/me/events?take=15&offset=${_events.length}'); if (resp.statusCode != 200) { context.showErrorDialog(RequestException(resp)); } @@ -45,6 +46,22 @@ class _AuditLogScreenState extends State { }); } + bool _showIp = false; + + String _censorIpAddress(String ip) { + List parts = ip.split('.'); + + if (parts.length == 4) { + String censoredPart1 = '*' * parts[1].length; + String censoredPart2 = '*' * parts[2].length; + String censoredPart3 = '*' * parts[3].length; + + return '${parts[0]}.$censoredPart1.$censoredPart2.$censoredPart3'; + } else { + return '***.***.***.***'; + } + } + @override void initState() { super.initState(); @@ -53,42 +70,85 @@ class _AuditLogScreenState extends State { @override Widget build(BuildContext context) { - return InfiniteList( - itemCount: _events.length, - isLoading: _isBusy, - onFetchData: () { - _getEvents(); - }, - itemBuilder: (context, idx) { - final element = _events[idx]; - return TimelineTile( - isFirst: idx == 0, - isLast: _events.length - 1 == idx, - alignment: TimelineAlign.start, - endChild: Container( - child: Card( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - element.type, - style: GoogleFonts.robotoMono(fontSize: 15), + return Column( + children: [ + CheckboxListTile( + value: _showIp, + title: Text('showIp'.tr), + contentPadding: const EdgeInsets.symmetric(horizontal: 24), + secondary: const Icon(Icons.alternate_email), + tileColor: + Theme.of(context).colorScheme.surfaceContainer.withOpacity(0.5), + onChanged: (val) { + setState(() => _showIp = val ?? false); + }, + ), + Expanded( + child: RefreshIndicator( + onRefresh: () { + _events.clear(); + return _getEvents(); + }, + child: InfiniteList( + padding: const EdgeInsets.symmetric(vertical: 12), + itemCount: _events.length, + isLoading: _isBusy, + onFetchData: () { + _getEvents(); + }, + itemBuilder: (context, idx) { + final element = _events[idx]; + return TimelineTile( + isFirst: idx == 0, + isLast: _events.length - 1 == idx, + alignment: TimelineAlign.start, + indicatorStyle: IndicatorStyle(width: 15), + endChild: Container( + child: Card( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + element.type, + style: GoogleFonts.robotoMono(fontSize: 15), + ), + Text( + _showIp + ? element.ipAddress + : _censorIpAddress(element.ipAddress), + style: GoogleFonts.sourceCodePro( + fontWeight: FontWeight.bold, + ), + ), + SizedBox( + height: 20, + width: double.maxFinite, + child: Marquee( + text: element.userAgent, + velocity: 25, + startAfter: Duration(milliseconds: 500), + pauseAfterRound: Duration(milliseconds: 3000), + ), + ), + Row( + children: [ + RelativeDate(element.createdAt), + const Gap(6), + Text('·'), + const Gap(6), + RelativeDate(element.createdAt, isFull: true), + ], + ), + ], + ).paddingSymmetric(horizontal: 12, vertical: 8), + ).paddingOnly(left: 16), ), - Row( - children: [ - RelativeDate(element.createdAt), - const Gap(6), - Text('·'), - const Gap(6), - RelativeDate(element.createdAt, isFull: true), - ], - ), - ], - ).paddingSymmetric(horizontal: 12, vertical: 8), - ).paddingOnly(left: 16), + ).paddingSymmetric(horizontal: 18); + }, + ), ), - ).paddingSymmetric(horizontal: 18); - }, + ), + ], ); } } diff --git a/lib/screens/posts/post_detail.dart b/lib/screens/posts/post_detail.dart index 2fc16da..4fc7037 100644 --- a/lib/screens/posts/post_detail.dart +++ b/lib/screens/posts/post_detail.dart @@ -62,10 +62,9 @@ class _PostDetailScreenState extends State { return CustomScrollView( slivers: [ - if (_isBusy) - SliverToBoxAdapter( - child: LoadingIndicator(), - ), + SliverToBoxAdapter( + child: LoadingIndicator(isActive: _isBusy), + ), SliverToBoxAdapter( child: PostItem( item: _item!, diff --git a/lib/widgets/channel/channel_list.dart b/lib/widgets/channel/channel_list.dart index d149196..fa4fcaa 100644 --- a/lib/widgets/channel/channel_list.dart +++ b/lib/widgets/channel/channel_list.dart @@ -38,11 +38,13 @@ class _ChannelListWidgetState extends State { Future _loadLastMessages() async { final messages = await _eventController.src.getLastInAllChannels(); - setState(() { - _lastMessages = messages - .map((k, v) => MapEntry(k, v.firstOrNull)) - .cast(); - }); + if (mounted) { + setState(() { + _lastMessages = messages + .map((k, v) => MapEntry(k, v.firstOrNull)) + .cast(); + }); + } } @override diff --git a/pubspec.lock b/pubspec.lock index 37276f8..7b5138c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -414,6 +414,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.5" + fading_edge_scrollview: + dependency: transitive + description: + name: fading_edge_scrollview + sha256: "1f84fe3ea8e251d00d5735e27502a6a250e4aa3d3b330d3fdcb475af741464ef" + url: "https://pub.dev" + source: hosted + version: "4.1.1" fake_async: dependency: transitive description: @@ -1277,6 +1285,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.0" + marquee: + dependency: "direct main" + description: + name: marquee + sha256: a87e7e80c5d21434f90ad92add9f820cf68be374b226404fe881d2bba7be0862 + url: "https://pub.dev" + source: hosted + version: "2.3.0" matcher: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 8b21825..8a7b4b2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -89,6 +89,7 @@ dependencies: qr_flutter: ^4.1.0 flutter_resizable_container: ^3.0.0 file_saver: ^0.2.14 + marquee: ^2.3.0 dev_dependencies: flutter_test: