✨ Report abuse
This commit is contained in:
parent
befc647b03
commit
91a32e6736
@ -440,5 +440,10 @@
|
|||||||
"termRelated": "Related Terms",
|
"termRelated": "Related Terms",
|
||||||
"appDetails": "App Details",
|
"appDetails": "App Details",
|
||||||
"projectWebsite": "Project Website",
|
"projectWebsite": "Project Website",
|
||||||
"iAmNotRobot": "I'm not a Robot"
|
"iAmNotRobot": "I'm not a Robot",
|
||||||
|
"report": "Report",
|
||||||
|
"reportAbuse": "Report abuse",
|
||||||
|
"reportAbuseResource": "Resource identifier",
|
||||||
|
"reportAbuseReason": "Report reason",
|
||||||
|
"reportSubmitted": "Report submitted, thank you for your contribution. We will send a notification about the result of the report within 24 hours for you."
|
||||||
}
|
}
|
||||||
|
@ -436,5 +436,10 @@
|
|||||||
"termRelated": "相关条款",
|
"termRelated": "相关条款",
|
||||||
"projectWebsite": "项目网站",
|
"projectWebsite": "项目网站",
|
||||||
"appDetails": "应用详情",
|
"appDetails": "应用详情",
|
||||||
"iAmNotRobot": "我不是机器人"
|
"iAmNotRobot": "我不是机器人",
|
||||||
|
"report": "举报",
|
||||||
|
"reportAbuse": "举报滥用",
|
||||||
|
"reportAbuseResource": "举报的资源",
|
||||||
|
"reportAbuseReason": "举报的原因",
|
||||||
|
"reportSubmitted": "举报已提交,感谢你的贡献。我们将通过通知在 24 小时内通知该举报的处理结果。"
|
||||||
}
|
}
|
||||||
|
@ -41,30 +41,21 @@ class _BootstrapperShellState extends State<BootstrapperShell> {
|
|||||||
|
|
||||||
final Completer _bootCompleter = Completer();
|
final Completer _bootCompleter = Completer();
|
||||||
|
|
||||||
late final List<({String label, Future<void> Function() action})> _periods = [
|
Future<void> _checkForUpdate() async {
|
||||||
(
|
|
||||||
label: 'bsLoadingTheme',
|
|
||||||
action: () async {
|
|
||||||
await context.read<ThemeSwitcher>().restoreTheme();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
(
|
|
||||||
label: 'bsCheckForUpdate',
|
|
||||||
action: () async {
|
|
||||||
if (PlatformInfo.isWeb) return;
|
if (PlatformInfo.isWeb) return;
|
||||||
try {
|
try {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
final info = await PackageInfo.fromPlatform();
|
final info = await PackageInfo.fromPlatform();
|
||||||
final localVersionString = '${info.version}+${info.buildNumber}';
|
final localVersionString = '${info.version}+${info.buildNumber}';
|
||||||
final resp = await GetConnect().get(
|
final resp = await GetConnect(
|
||||||
|
timeout: const Duration(seconds: 60),
|
||||||
|
).get(
|
||||||
'https://git.solsynth.dev/api/v1/repos/hydrogen/solian/tags?page=1&limit=1',
|
'https://git.solsynth.dev/api/v1/repos/hydrogen/solian/tags?page=1&limit=1',
|
||||||
);
|
);
|
||||||
final remoteVersionString =
|
final remoteVersionString =
|
||||||
(resp.body as List).firstOrNull?['name'] ?? '0.0.0+0';
|
(resp.body as List).firstOrNull?['name'] ?? '0.0.0+0';
|
||||||
final remoteVersion =
|
final remoteVersion = Version.parse(remoteVersionString.split('+').first);
|
||||||
Version.parse(remoteVersionString.split('+').first);
|
final localVersion = Version.parse(localVersionString.split('+').first);
|
||||||
final localVersion =
|
|
||||||
Version.parse(localVersionString.split('+').first);
|
|
||||||
final remoteBuildNumber =
|
final remoteBuildNumber =
|
||||||
int.tryParse(remoteVersionString.split('+').last) ?? 0;
|
int.tryParse(remoteVersionString.split('+').last) ?? 0;
|
||||||
final localBuildNumber =
|
final localBuildNumber =
|
||||||
@ -74,11 +65,6 @@ class _BootstrapperShellState extends State<BootstrapperShell> {
|
|||||||
(remoteVersion == localVersion &&
|
(remoteVersion == localVersion &&
|
||||||
remoteBuildNumber > localBuildNumber) ||
|
remoteBuildNumber > localBuildNumber) ||
|
||||||
(remoteVersionString != localVersionString && strictUpdate)) {
|
(remoteVersionString != localVersionString && strictUpdate)) {
|
||||||
setState(() {
|
|
||||||
_isErrored = true;
|
|
||||||
_subtitle = 'bsCheckForUpdateDesc'.tr;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (PlatformInfo.isAndroid) {
|
if (PlatformInfo.isAndroid) {
|
||||||
context
|
context
|
||||||
.showConfirmDialog(
|
.showConfirmDialog(
|
||||||
@ -100,10 +86,10 @@ class _BootstrapperShellState extends State<BootstrapperShell> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
setState(() {
|
context.showInfoDialog(
|
||||||
_isErrored = true;
|
'updateAvailable'.tr,
|
||||||
_subtitle = 'bsCheckForUpdateDesc'.tr;
|
'bsCheckForUpdateDesc'.tr,
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
} else if (remoteVersionString != localVersionString) {
|
} else if (remoteVersionString != localVersionString) {
|
||||||
_bootCompleter.future.then((_) {
|
_bootCompleter.future.then((_) {
|
||||||
@ -115,6 +101,13 @@ class _BootstrapperShellState extends State<BootstrapperShell> {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
context.showErrorDialog('Unable to check update: $e');
|
context.showErrorDialog('Unable to check update: $e');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
late final List<({String label, Future<void> Function() action})> _periods = [
|
||||||
|
(
|
||||||
|
label: 'bsLoadingTheme',
|
||||||
|
action: () async {
|
||||||
|
await context.read<ThemeSwitcher>().restoreTheme();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@ -214,6 +207,7 @@ class _BootstrapperShellState extends State<BootstrapperShell> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_runPeriods();
|
_runPeriods();
|
||||||
|
_checkForUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -146,7 +146,10 @@ class _SignUpScreenState extends State<SignUpScreen> {
|
|||||||
const Gap(8),
|
const Gap(8),
|
||||||
CheckboxListTile(
|
CheckboxListTile(
|
||||||
value: _isTermAccepted,
|
value: _isTermAccepted,
|
||||||
title: Text('termAccept'.tr),
|
title: Text(
|
||||||
|
'termAccept'.tr,
|
||||||
|
style: const TextStyle(height: 1.2),
|
||||||
|
).paddingOnly(bottom: 4),
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.all(
|
borderRadius: BorderRadius.all(
|
||||||
Radius.circular(8),
|
Radius.circular(8),
|
||||||
|
@ -74,9 +74,13 @@ class LinkExpansion extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
).paddingOnly(right: 8),
|
).paddingOnly(right: 8),
|
||||||
if (snapshot.data!.siteName != null)
|
if (snapshot.data!.siteName != null)
|
||||||
Text(
|
Expanded(
|
||||||
|
child: Text(
|
||||||
snapshot.data!.siteName!,
|
snapshot.data!.siteName!,
|
||||||
style: Theme.of(context).textTheme.labelLarge,
|
style: Theme.of(context).textTheme.labelLarge,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
).paddingOnly(
|
).paddingOnly(
|
||||||
|
@ -12,6 +12,7 @@ import 'package:solian/platform.dart';
|
|||||||
import 'package:solian/providers/auth.dart';
|
import 'package:solian/providers/auth.dart';
|
||||||
import 'package:solian/router.dart';
|
import 'package:solian/router.dart';
|
||||||
import 'package:solian/screens/posts/post_editor.dart';
|
import 'package:solian/screens/posts/post_editor.dart';
|
||||||
|
import 'package:solian/widgets/reports/abuse_report.dart';
|
||||||
|
|
||||||
class PostAction extends StatefulWidget {
|
class PostAction extends StatefulWidget {
|
||||||
final Post item;
|
final Post item;
|
||||||
@ -149,6 +150,23 @@ class _PostActionState extends State<PostAction> {
|
|||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
leading: const Icon(Icons.report),
|
||||||
|
title: Text('report'.tr),
|
||||||
|
onTap: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AbuseReportDialog(
|
||||||
|
resourceId: 'post:${widget.item.id}',
|
||||||
|
),
|
||||||
|
).then((status) {
|
||||||
|
if (status == true) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
if (!widget.noReact)
|
if (!widget.noReact)
|
||||||
ListTile(
|
ListTile(
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
107
lib/widgets/reports/abuse_report.dart
Normal file
107
lib/widgets/reports/abuse_report.dart
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:solian/exceptions/request.dart';
|
||||||
|
import 'package:solian/exts.dart';
|
||||||
|
import 'package:solian/providers/auth.dart';
|
||||||
|
|
||||||
|
class AbuseReportDialog extends StatefulWidget {
|
||||||
|
final String? resourceId;
|
||||||
|
|
||||||
|
const AbuseReportDialog({super.key, this.resourceId});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AbuseReportDialog> createState() => _AbuseReportDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AbuseReportDialogState extends State<AbuseReportDialog> {
|
||||||
|
final TextEditingController _resourceController = TextEditingController();
|
||||||
|
final TextEditingController _reasonController = TextEditingController();
|
||||||
|
|
||||||
|
bool _isBusy = false;
|
||||||
|
|
||||||
|
Future<void> _submit() async {
|
||||||
|
final auth = Get.find<AuthProvider>();
|
||||||
|
if (!auth.isAuthorized.value) return;
|
||||||
|
final client = await auth.configureClient('id');
|
||||||
|
|
||||||
|
setState(() => _isBusy = true);
|
||||||
|
|
||||||
|
final resp = await client.post('/reports/abuse', {
|
||||||
|
'resource': _resourceController.text,
|
||||||
|
'reason': _reasonController.text,
|
||||||
|
});
|
||||||
|
|
||||||
|
setState(() => _isBusy = false);
|
||||||
|
|
||||||
|
if (resp.statusCode != 200) {
|
||||||
|
context.showErrorDialog(RequestException(resp));
|
||||||
|
} else {
|
||||||
|
context.showSnackbar('reportSubmitted'.tr);
|
||||||
|
Navigator.pop(context, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
if (widget.resourceId != null) {
|
||||||
|
_resourceController.text = widget.resourceId!;
|
||||||
|
}
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_resourceController.dispose();
|
||||||
|
_reasonController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text('reportAbuse'.tr),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Gap(4),
|
||||||
|
TextField(
|
||||||
|
controller: _resourceController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
|
labelText: 'reportAbuseResource'.tr,
|
||||||
|
enabled: widget.resourceId == null,
|
||||||
|
isDense: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(12),
|
||||||
|
TextField(
|
||||||
|
controller: _reasonController,
|
||||||
|
textInputAction: TextInputAction.newline,
|
||||||
|
minLines: 1,
|
||||||
|
maxLines: 3,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
|
labelText: 'reportAbuseReason'.tr,
|
||||||
|
isDense: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: _isBusy
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Text('cancel'.tr),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: _isBusy ? null : () => _submit(),
|
||||||
|
child: Text('okay'.tr),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user