diff --git a/lib/models/abuse_report.dart b/lib/models/abuse_report.dart index 630320a..714189e 100644 --- a/lib/models/abuse_report.dart +++ b/lib/models/abuse_report.dart @@ -1,5 +1,4 @@ import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:island/models/user.dart'; part 'abuse_report.freezed.dart'; part 'abuse_report.g.dart'; @@ -14,7 +13,6 @@ sealed class SnAbuseReport with _$SnAbuseReport { required DateTime? resolvedAt, required String? resolution, required String accountId, - required SnAccount? account, required DateTime createdAt, required DateTime updatedAt, required DateTime? deletedAt, diff --git a/lib/models/abuse_report.freezed.dart b/lib/models/abuse_report.freezed.dart index ba3cffb..b07331d 100644 --- a/lib/models/abuse_report.freezed.dart +++ b/lib/models/abuse_report.freezed.dart @@ -16,7 +16,7 @@ T _$identity(T value) => value; /// @nodoc mixin _$SnAbuseReport { - String get id; String get resourceIdentifier; int get type; String get reason; DateTime? get resolvedAt; String? get resolution; String get accountId; SnAccount? get account; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; + String get id; String get resourceIdentifier; int get type; String get reason; DateTime? get resolvedAt; String? get resolution; String get accountId; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; /// Create a copy of SnAbuseReport /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -29,16 +29,16 @@ $SnAbuseReportCopyWith get copyWith => _$SnAbuseReportCopyWithImp @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is SnAbuseReport&&(identical(other.id, id) || other.id == id)&&(identical(other.resourceIdentifier, resourceIdentifier) || other.resourceIdentifier == resourceIdentifier)&&(identical(other.type, type) || other.type == type)&&(identical(other.reason, reason) || other.reason == reason)&&(identical(other.resolvedAt, resolvedAt) || other.resolvedAt == resolvedAt)&&(identical(other.resolution, resolution) || other.resolution == resolution)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.account, account) || other.account == account)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is SnAbuseReport&&(identical(other.id, id) || other.id == id)&&(identical(other.resourceIdentifier, resourceIdentifier) || other.resourceIdentifier == resourceIdentifier)&&(identical(other.type, type) || other.type == type)&&(identical(other.reason, reason) || other.reason == reason)&&(identical(other.resolvedAt, resolvedAt) || other.resolvedAt == resolvedAt)&&(identical(other.resolution, resolution) || other.resolution == resolution)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,id,resourceIdentifier,type,reason,resolvedAt,resolution,accountId,account,createdAt,updatedAt,deletedAt); +int get hashCode => Object.hash(runtimeType,id,resourceIdentifier,type,reason,resolvedAt,resolution,accountId,createdAt,updatedAt,deletedAt); @override String toString() { - return 'SnAbuseReport(id: $id, resourceIdentifier: $resourceIdentifier, type: $type, reason: $reason, resolvedAt: $resolvedAt, resolution: $resolution, accountId: $accountId, account: $account, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; + return 'SnAbuseReport(id: $id, resourceIdentifier: $resourceIdentifier, type: $type, reason: $reason, resolvedAt: $resolvedAt, resolution: $resolution, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; } @@ -49,11 +49,11 @@ abstract mixin class $SnAbuseReportCopyWith<$Res> { factory $SnAbuseReportCopyWith(SnAbuseReport value, $Res Function(SnAbuseReport) _then) = _$SnAbuseReportCopyWithImpl; @useResult $Res call({ - String id, String resourceIdentifier, int type, String reason, DateTime? resolvedAt, String? resolution, String accountId, SnAccount? account, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt + String id, String resourceIdentifier, int type, String reason, DateTime? resolvedAt, String? resolution, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt }); -$SnAccountCopyWith<$Res>? get account; + } /// @nodoc @@ -66,7 +66,7 @@ class _$SnAbuseReportCopyWithImpl<$Res> /// Create a copy of SnAbuseReport /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? resourceIdentifier = null,Object? type = null,Object? reason = null,Object? resolvedAt = freezed,Object? resolution = freezed,Object? accountId = null,Object? account = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? resourceIdentifier = null,Object? type = null,Object? reason = null,Object? resolvedAt = freezed,Object? resolution = freezed,Object? accountId = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { return _then(_self.copyWith( id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable as String,resourceIdentifier: null == resourceIdentifier ? _self.resourceIdentifier : resourceIdentifier // ignore: cast_nullable_to_non_nullable @@ -75,26 +75,13 @@ as int,reason: null == reason ? _self.reason : reason // ignore: cast_nullable_t as String,resolvedAt: freezed == resolvedAt ? _self.resolvedAt : resolvedAt // ignore: cast_nullable_to_non_nullable as DateTime?,resolution: freezed == resolution ? _self.resolution : resolution // ignore: cast_nullable_to_non_nullable as String?,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable -as String,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable -as SnAccount?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable +as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable as DateTime?, )); } -/// Create a copy of SnAbuseReport -/// with the given fields replaced by the non-null parameter values. -@override -@pragma('vm:prefer-inline') -$SnAccountCopyWith<$Res>? get account { - if (_self.account == null) { - return null; - } - return $SnAccountCopyWith<$Res>(_self.account!, (value) { - return _then(_self.copyWith(account: value)); - }); -} } @@ -102,7 +89,7 @@ $SnAccountCopyWith<$Res>? get account { @JsonSerializable() class _SnAbuseReport implements SnAbuseReport { - const _SnAbuseReport({required this.id, required this.resourceIdentifier, required this.type, required this.reason, required this.resolvedAt, required this.resolution, required this.accountId, required this.account, required this.createdAt, required this.updatedAt, required this.deletedAt}); + const _SnAbuseReport({required this.id, required this.resourceIdentifier, required this.type, required this.reason, required this.resolvedAt, required this.resolution, required this.accountId, required this.createdAt, required this.updatedAt, required this.deletedAt}); factory _SnAbuseReport.fromJson(Map json) => _$SnAbuseReportFromJson(json); @override final String id; @@ -112,7 +99,6 @@ class _SnAbuseReport implements SnAbuseReport { @override final DateTime? resolvedAt; @override final String? resolution; @override final String accountId; -@override final SnAccount? account; @override final DateTime createdAt; @override final DateTime updatedAt; @override final DateTime? deletedAt; @@ -130,16 +116,16 @@ Map toJson() { @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnAbuseReport&&(identical(other.id, id) || other.id == id)&&(identical(other.resourceIdentifier, resourceIdentifier) || other.resourceIdentifier == resourceIdentifier)&&(identical(other.type, type) || other.type == type)&&(identical(other.reason, reason) || other.reason == reason)&&(identical(other.resolvedAt, resolvedAt) || other.resolvedAt == resolvedAt)&&(identical(other.resolution, resolution) || other.resolution == resolution)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.account, account) || other.account == account)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnAbuseReport&&(identical(other.id, id) || other.id == id)&&(identical(other.resourceIdentifier, resourceIdentifier) || other.resourceIdentifier == resourceIdentifier)&&(identical(other.type, type) || other.type == type)&&(identical(other.reason, reason) || other.reason == reason)&&(identical(other.resolvedAt, resolvedAt) || other.resolvedAt == resolvedAt)&&(identical(other.resolution, resolution) || other.resolution == resolution)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,id,resourceIdentifier,type,reason,resolvedAt,resolution,accountId,account,createdAt,updatedAt,deletedAt); +int get hashCode => Object.hash(runtimeType,id,resourceIdentifier,type,reason,resolvedAt,resolution,accountId,createdAt,updatedAt,deletedAt); @override String toString() { - return 'SnAbuseReport(id: $id, resourceIdentifier: $resourceIdentifier, type: $type, reason: $reason, resolvedAt: $resolvedAt, resolution: $resolution, accountId: $accountId, account: $account, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; + return 'SnAbuseReport(id: $id, resourceIdentifier: $resourceIdentifier, type: $type, reason: $reason, resolvedAt: $resolvedAt, resolution: $resolution, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; } @@ -150,11 +136,11 @@ abstract mixin class _$SnAbuseReportCopyWith<$Res> implements $SnAbuseReportCopy factory _$SnAbuseReportCopyWith(_SnAbuseReport value, $Res Function(_SnAbuseReport) _then) = __$SnAbuseReportCopyWithImpl; @override @useResult $Res call({ - String id, String resourceIdentifier, int type, String reason, DateTime? resolvedAt, String? resolution, String accountId, SnAccount? account, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt + String id, String resourceIdentifier, int type, String reason, DateTime? resolvedAt, String? resolution, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt }); -@override $SnAccountCopyWith<$Res>? get account; + } /// @nodoc @@ -167,7 +153,7 @@ class __$SnAbuseReportCopyWithImpl<$Res> /// Create a copy of SnAbuseReport /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? resourceIdentifier = null,Object? type = null,Object? reason = null,Object? resolvedAt = freezed,Object? resolution = freezed,Object? accountId = null,Object? account = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? resourceIdentifier = null,Object? type = null,Object? reason = null,Object? resolvedAt = freezed,Object? resolution = freezed,Object? accountId = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { return _then(_SnAbuseReport( id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable as String,resourceIdentifier: null == resourceIdentifier ? _self.resourceIdentifier : resourceIdentifier // ignore: cast_nullable_to_non_nullable @@ -176,27 +162,14 @@ as int,reason: null == reason ? _self.reason : reason // ignore: cast_nullable_t as String,resolvedAt: freezed == resolvedAt ? _self.resolvedAt : resolvedAt // ignore: cast_nullable_to_non_nullable as DateTime?,resolution: freezed == resolution ? _self.resolution : resolution // ignore: cast_nullable_to_non_nullable as String?,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable -as String,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable -as SnAccount?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable +as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable as DateTime?, )); } -/// Create a copy of SnAbuseReport -/// with the given fields replaced by the non-null parameter values. -@override -@pragma('vm:prefer-inline') -$SnAccountCopyWith<$Res>? get account { - if (_self.account == null) { - return null; - } - return $SnAccountCopyWith<$Res>(_self.account!, (value) { - return _then(_self.copyWith(account: value)); - }); -} } // dart format on diff --git a/lib/models/abuse_report.g.dart b/lib/models/abuse_report.g.dart index d78ad15..bb6010c 100644 --- a/lib/models/abuse_report.g.dart +++ b/lib/models/abuse_report.g.dart @@ -18,10 +18,6 @@ _SnAbuseReport _$SnAbuseReportFromJson(Map json) => : DateTime.parse(json['resolved_at'] as String), resolution: json['resolution'] as String?, accountId: json['account_id'] as String, - account: - json['account'] == null - ? null - : SnAccount.fromJson(json['account'] as Map), createdAt: DateTime.parse(json['created_at'] as String), updatedAt: DateTime.parse(json['updated_at'] as String), deletedAt: @@ -39,7 +35,6 @@ Map _$SnAbuseReportToJson(_SnAbuseReport instance) => 'resolved_at': instance.resolvedAt?.toIso8601String(), 'resolution': instance.resolution, 'account_id': instance.accountId, - 'account': instance.account?.toJson(), 'created_at': instance.createdAt.toIso8601String(), 'updated_at': instance.updatedAt.toIso8601String(), 'deleted_at': instance.deletedAt?.toIso8601String(), diff --git a/lib/models/abuse_report_type.dart b/lib/models/abuse_report_type.dart new file mode 100644 index 0000000..31d1c4b --- /dev/null +++ b/lib/models/abuse_report_type.dart @@ -0,0 +1,38 @@ +enum AbuseReportType { + copyright(0), + harassment(1), + impersonation(2), + offensiveContent(3), + spam(4), + privacyViolation(5), + illegalContent(6), + other(7); + + const AbuseReportType(this.value); + final int value; + + static AbuseReportType fromValue(int value) { + return values.firstWhere((e) => e.value == value); + } + + String get displayName { + switch (this) { + case AbuseReportType.copyright: + return 'Copyright'; + case AbuseReportType.harassment: + return 'Harassment'; + case AbuseReportType.impersonation: + return 'Impersonation'; + case AbuseReportType.offensiveContent: + return 'Offensive Content'; + case AbuseReportType.spam: + return 'Spam'; + case AbuseReportType.privacyViolation: + return 'Privacy Violation'; + case AbuseReportType.illegalContent: + return 'Illegal Content'; + case AbuseReportType.other: + return 'Other'; + } + } +} diff --git a/lib/screens/about.dart b/lib/screens/about.dart index 69bf2a8..7571ee2 100644 --- a/lib/screens/about.dart +++ b/lib/screens/about.dart @@ -7,6 +7,7 @@ import 'package:island/pods/network.dart'; import 'package:island/services/notify.dart'; import 'package:island/services/udid.native.dart'; import 'package:island/widgets/alert.dart'; +import 'package:island/widgets/app_scaffold.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:styled_widget/styled_widget.dart'; @@ -90,7 +91,7 @@ class _AboutScreenState extends ConsumerState { Widget build(BuildContext context) { final theme = Theme.of(context); - return Scaffold( + return AppScaffold( appBar: AppBar(title: Text('about'.tr()), elevation: 0), body: _isLoading diff --git a/lib/screens/creators/webfeed/webfeed_edit.dart b/lib/screens/creators/webfeed/webfeed_edit.dart index 379e9f5..f9c0d64 100644 --- a/lib/screens/creators/webfeed/webfeed_edit.dart +++ b/lib/screens/creators/webfeed/webfeed_edit.dart @@ -6,6 +6,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/models/webfeed.dart'; import 'package:island/pods/webfeed.dart'; import 'package:island/widgets/alert.dart'; +import 'package:island/widgets/app_scaffold.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:styled_widget/styled_widget.dart'; @@ -183,7 +184,7 @@ class WebFeedEditScreen extends HookConsumerWidget { } }, [pubName, feedId, ref, context]); - return Scaffold( + return AppScaffold( appBar: AppBar( title: Text(hasFeedId ? 'Edit Web Feed' : 'New Web Feed'), actions: [ diff --git a/lib/screens/discovery/articles.dart b/lib/screens/discovery/articles.dart index be14a50..46cef66 100644 --- a/lib/screens/discovery/articles.dart +++ b/lib/screens/discovery/articles.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/models/webfeed.dart'; import 'package:island/pods/network.dart'; +import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/web_article_card.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_paging_utils/riverpod_paging_utils.dart'; @@ -124,7 +125,7 @@ class ArticlesScreen extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - return Scaffold( + return AppScaffold( appBar: AppBar(title: Text(title ?? 'Articles')), body: Center( child: ConstrainedBox( diff --git a/lib/screens/posts/post_search.dart b/lib/screens/posts/post_search.dart index d501df3..7040a45 100644 --- a/lib/screens/posts/post_search.dart +++ b/lib/screens/posts/post_search.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/models/post.dart'; import 'package:island/pods/network.dart'; +import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/post/post_item.dart'; import 'package:riverpod_paging_utils/riverpod_paging_utils.dart'; @@ -107,7 +108,7 @@ class _PostSearchScreenState extends ConsumerState { @override Widget build(BuildContext context) { - return Scaffold( + return AppScaffold( appBar: AppBar( title: TextField( controller: _searchController, diff --git a/lib/screens/reports/report_detail.dart b/lib/screens/reports/report_detail.dart index 8d78586..d309de2 100644 --- a/lib/screens/reports/report_detail.dart +++ b/lib/screens/reports/report_detail.dart @@ -1,7 +1,10 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/models/abuse_report.dart'; +import 'package:island/models/abuse_report_type.dart'; import 'package:island/services/abuse_report_service.dart'; +import 'package:island/widgets/app_scaffold.dart'; +import 'package:styled_widget/styled_widget.dart'; class AbuseReportDetailScreen extends ConsumerStatefulWidget { final String reportId; @@ -20,16 +23,15 @@ class _AbuseReportDetailScreenState @override void initState() { super.initState(); - _reportFuture = - ref.read(abuseReportServiceProvider).getReport(widget.reportId); + _reportFuture = ref + .read(abuseReportServiceProvider) + .getReport(widget.reportId); } @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('Abuse Report Details'), - ), + return AppScaffold( + appBar: AppBar(title: const Text('Abuse Report Details')), body: FutureBuilder( future: _reportFuture, builder: (context, snapshot) { @@ -44,15 +46,39 @@ class _AbuseReportDetailScreenState child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('Report ID: ${report.id}'), - Text('Resource Identifier: ${report.resourceIdentifier}'), - Text('Type: ${report.type}'), - Text('Reason: ${report.reason}'), - Text('Resolved At: ${report.resolvedAt}'), - Text('Resolution: ${report.resolution}'), - Text('Account ID: ${report.accountId}'), - Text('Created At: ${report.createdAt}'), - Text('Updated At: ${report.updatedAt}'), + _buildDetailRow(context, 'Report ID', report.id), + _buildDetailRow( + context, + 'Resource Identifier', + report.resourceIdentifier, + ), + _buildDetailRow( + context, + 'Type', + AbuseReportType.fromValue(report.type).displayName, + ), + _buildDetailRow(context, 'Reason', report.reason), + _buildDetailRow( + context, + 'Resolved At', + report.resolvedAt?.toString() ?? 'N/A', + ), + _buildDetailRow( + context, + 'Resolution', + report.resolution ?? 'N/A', + ), + _buildDetailRow(context, 'Account ID', report.accountId), + _buildDetailRow( + context, + 'Created At', + report.createdAt.toString(), + ), + _buildDetailRow( + context, + 'Updated At', + report.updatedAt.toString(), + ), ], ), ); @@ -63,4 +89,17 @@ class _AbuseReportDetailScreenState ), ); } -} \ No newline at end of file + + Widget _buildDetailRow(BuildContext context, String label, String value) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(label, style: Theme.of(context).textTheme.titleMedium).bold(), + Text(value, style: Theme.of(context).textTheme.bodyLarge), + ], + ), + ); + } +} diff --git a/lib/screens/reports/report_list.dart b/lib/screens/reports/report_list.dart index 9bfa8c7..5f637e5 100644 --- a/lib/screens/reports/report_list.dart +++ b/lib/screens/reports/report_list.dart @@ -1,8 +1,13 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/models/abuse_report.dart'; +import 'package:island/models/abuse_report_type.dart'; import 'package:island/services/abuse_report_service.dart'; +import 'package:island/services/time.dart'; +import 'package:island/widgets/app_scaffold.dart'; +import 'package:island/widgets/safety/abuse_report_helper.dart'; class AbuseReportListScreen extends ConsumerStatefulWidget { const AbuseReportListScreen({super.key}); @@ -23,9 +28,13 @@ class _AbuseReportListScreenState extends ConsumerState { @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('My Abuse Reports'), + return AppScaffold( + appBar: AppBar(title: Text('abuseReports').tr()), + floatingActionButton: FloatingActionButton( + child: const Icon(Icons.add), + onPressed: () { + showAbuseReportSheet(context, resourceIdentifier: 'unidentified'); + }, ), body: FutureBuilder>( future: _reportsFuture, @@ -37,15 +46,100 @@ class _AbuseReportListScreenState extends ConsumerState { } else if (snapshot.hasData) { final reports = snapshot.data!; return ListView.builder( + padding: EdgeInsets.zero, itemCount: reports.length, itemBuilder: (context, index) { final report = reports[index]; - return ListTile( - title: Text(report.reason), - subtitle: Text(report.id), - onTap: () { - context.push('/safety/reports/me/${report.id}'); - }, + return Card( + elevation: 2, + margin: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 8, + ), + child: InkWell( + onTap: () { + context.push('/safety/reports/me/${report.id}'); + }, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + report.reason, + style: Theme.of(context).textTheme.titleMedium, + ), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'ID', + style: Theme.of(context).textTheme.bodySmall, + ), + Text( + report.id, + style: Theme.of(context).textTheme.bodyMedium, + ), + ], + ), + const SizedBox(height: 4), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Type', + style: Theme.of(context).textTheme.bodySmall, + ), + Text( + AbuseReportType.fromValue( + report.type, + ).displayName, + style: Theme.of(context).textTheme.bodyMedium, + ), + ], + ), + const SizedBox(height: 4), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Created at', + style: Theme.of(context).textTheme.bodySmall, + ), + Text( + '${report.createdAt.formatRelative(context)} ยท ${report.createdAt.formatSystem()}', + style: Theme.of(context).textTheme.bodyMedium, + ), + ], + ), + const SizedBox(height: 4), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Status', + style: Theme.of(context).textTheme.bodySmall, + ), + Text( + report.resolvedAt != null + ? 'Resolved' + : 'Unresolved', + style: Theme.of( + context, + ).textTheme.bodyMedium?.copyWith( + color: + report.resolvedAt != null + ? Colors.green + : Colors.orange, + ), + ), + ], + ), + ], + ), + ), + ), ); }, ); diff --git a/lib/utils/abuse_report_utils.dart b/lib/utils/abuse_report_utils.dart new file mode 100644 index 0000000..6d9b881 --- /dev/null +++ b/lib/utils/abuse_report_utils.dart @@ -0,0 +1,22 @@ +String getAbuseReportTypeString(int type) { + switch (type) { + case 0: + return 'Copyright'; + case 1: + return 'Harassment'; + case 2: + return 'Impersonation'; + case 3: + return 'Offensive Content'; + case 4: + return 'Spam'; + case 5: + return 'Privacy Violation'; + case 6: + return 'Illegal Content'; + case 7: + return 'Other'; + default: + return 'Unknown'; + } +}