✨ Better audit logs
This commit is contained in:
parent
77288713e1
commit
48e3b510cf
@ -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"
|
||||
}
|
||||
|
@ -481,5 +481,6 @@
|
||||
"auditLog": "活动日志",
|
||||
"shareImage": "分享图片",
|
||||
"shareImageFooter": "上 Solar Network 看更多有趣帖子",
|
||||
"fileSavedAt": "文件保存于 @path"
|
||||
"fileSavedAt": "文件保存于 @path",
|
||||
"showIp": "显示 IP 地址"
|
||||
}
|
||||
|
@ -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<AuditLogScreen> {
|
||||
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<AuditLogScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
bool _showIp = false;
|
||||
|
||||
String _censorIpAddress(String ip) {
|
||||
List<String> 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<AuditLogScreen> {
|
||||
|
||||
@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);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -62,10 +62,9 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
|
||||
|
||||
return CustomScrollView(
|
||||
slivers: [
|
||||
if (_isBusy)
|
||||
SliverToBoxAdapter(
|
||||
child: LoadingIndicator(),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: LoadingIndicator(isActive: _isBusy),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: PostItem(
|
||||
item: _item!,
|
||||
|
@ -38,11 +38,13 @@ class _ChannelListWidgetState extends State<ChannelListWidget> {
|
||||
|
||||
Future<void> _loadLastMessages() async {
|
||||
final messages = await _eventController.src.getLastInAllChannels();
|
||||
setState(() {
|
||||
_lastMessages = messages
|
||||
.map((k, v) => MapEntry(k, v.firstOrNull))
|
||||
.cast<int, LocalMessageEventTableData>();
|
||||
});
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_lastMessages = messages
|
||||
.map((k, v) => MapEntry(k, v.firstOrNull))
|
||||
.cast<int, LocalMessageEventTableData>();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
16
pubspec.lock
16
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:
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user