Compare commits
	
		
			7 Commits
		
	
	
		
			3.0.0+111
			...
			f8d1940af6
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| f8d1940af6 | |||
| b2b0891d24 | |||
| 274168d4bc | |||
| 2c98b348d5 | |||
| afc7887ddd | |||
| 99ff78a3d5 | |||
| 2ad85addf6 | 
| @@ -590,6 +590,7 @@ | ||||
|   "yes": "Yes", | ||||
|   "navigateToChat": "Navigate to Chat", | ||||
|   "wouldYouLikeToNavigateToChat": "Would You like to navigate to the chat?", | ||||
|   "abuseReports": "Abuse Reports", | ||||
|   "abuseReport": "Report", | ||||
|   "abuseReportTitle": "Report Content", | ||||
|   "abuseReportDescription": "Help us keep the community safe by reporting inappropriate content or behavior.", | ||||
|   | ||||
							
								
								
									
										23
									
								
								lib/models/abuse_report.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								lib/models/abuse_report.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| import 'package:freezed_annotation/freezed_annotation.dart'; | ||||
|  | ||||
| part 'abuse_report.freezed.dart'; | ||||
| part 'abuse_report.g.dart'; | ||||
|  | ||||
| @freezed | ||||
| sealed class SnAbuseReport with _$SnAbuseReport { | ||||
|   const factory SnAbuseReport({ | ||||
|     required String id, | ||||
|     required String resourceIdentifier, | ||||
|     required int type, | ||||
|     required String reason, | ||||
|     required DateTime? resolvedAt, | ||||
|     required String? resolution, | ||||
|     required String accountId, | ||||
|     required DateTime createdAt, | ||||
|     required DateTime updatedAt, | ||||
|     required DateTime? deletedAt, | ||||
|   }) = _SnAbuseReport; | ||||
|  | ||||
|   factory SnAbuseReport.fromJson(Map<String, dynamic> json) => | ||||
|       _$SnAbuseReportFromJson(json); | ||||
| } | ||||
							
								
								
									
										175
									
								
								lib/models/abuse_report.freezed.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								lib/models/abuse_report.freezed.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,175 @@ | ||||
| // dart format width=80 | ||||
| // coverage:ignore-file | ||||
| // GENERATED CODE - DO NOT MODIFY BY HAND | ||||
| // ignore_for_file: type=lint | ||||
| // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark | ||||
|  | ||||
| part of 'abuse_report.dart'; | ||||
|  | ||||
| // ************************************************************************** | ||||
| // FreezedGenerator | ||||
| // ************************************************************************** | ||||
|  | ||||
| // dart format off | ||||
| T _$identity<T>(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; 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) | ||||
| @pragma('vm:prefer-inline') | ||||
| $SnAbuseReportCopyWith<SnAbuseReport> get copyWith => _$SnAbuseReportCopyWithImpl<SnAbuseReport>(this as SnAbuseReport, _$identity); | ||||
|  | ||||
|   /// Serializes this SnAbuseReport to a JSON map. | ||||
|   Map<String, dynamic> 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.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,createdAt,updatedAt,deletedAt); | ||||
|  | ||||
| @override | ||||
| String toString() { | ||||
|   return 'SnAbuseReport(id: $id, resourceIdentifier: $resourceIdentifier, type: $type, reason: $reason, resolvedAt: $resolvedAt, resolution: $resolution, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; | ||||
| } | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
| /// @nodoc | ||||
| 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, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt | ||||
| }); | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| } | ||||
| /// @nodoc | ||||
| class _$SnAbuseReportCopyWithImpl<$Res> | ||||
|     implements $SnAbuseReportCopyWith<$Res> { | ||||
|   _$SnAbuseReportCopyWithImpl(this._self, this._then); | ||||
|  | ||||
|   final SnAbuseReport _self; | ||||
|   final $Res Function(SnAbuseReport) _then; | ||||
|  | ||||
| /// 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? 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 | ||||
| as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable | ||||
| as int,reason: null == reason ? _self.reason : reason // ignore: cast_nullable_to_non_nullable | ||||
| 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,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?, | ||||
|   )); | ||||
| } | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| /// @nodoc | ||||
| @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.createdAt, required this.updatedAt, required this.deletedAt}); | ||||
|   factory _SnAbuseReport.fromJson(Map<String, dynamic> json) => _$SnAbuseReportFromJson(json); | ||||
|  | ||||
| @override final  String id; | ||||
| @override final  String resourceIdentifier; | ||||
| @override final  int type; | ||||
| @override final  String reason; | ||||
| @override final  DateTime? resolvedAt; | ||||
| @override final  String? resolution; | ||||
| @override final  String accountId; | ||||
| @override final  DateTime createdAt; | ||||
| @override final  DateTime updatedAt; | ||||
| @override final  DateTime? deletedAt; | ||||
|  | ||||
| /// Create a copy of SnAbuseReport | ||||
| /// with the given fields replaced by the non-null parameter values. | ||||
| @override @JsonKey(includeFromJson: false, includeToJson: false) | ||||
| @pragma('vm:prefer-inline') | ||||
| _$SnAbuseReportCopyWith<_SnAbuseReport> get copyWith => __$SnAbuseReportCopyWithImpl<_SnAbuseReport>(this, _$identity); | ||||
|  | ||||
| @override | ||||
| Map<String, dynamic> toJson() { | ||||
|   return _$SnAbuseReportToJson(this, ); | ||||
| } | ||||
|  | ||||
| @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.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,createdAt,updatedAt,deletedAt); | ||||
|  | ||||
| @override | ||||
| String toString() { | ||||
|   return 'SnAbuseReport(id: $id, resourceIdentifier: $resourceIdentifier, type: $type, reason: $reason, resolvedAt: $resolvedAt, resolution: $resolution, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; | ||||
| } | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
| /// @nodoc | ||||
| abstract mixin class _$SnAbuseReportCopyWith<$Res> implements $SnAbuseReportCopyWith<$Res> { | ||||
|   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, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt | ||||
| }); | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| } | ||||
| /// @nodoc | ||||
| class __$SnAbuseReportCopyWithImpl<$Res> | ||||
|     implements _$SnAbuseReportCopyWith<$Res> { | ||||
|   __$SnAbuseReportCopyWithImpl(this._self, this._then); | ||||
|  | ||||
|   final _SnAbuseReport _self; | ||||
|   final $Res Function(_SnAbuseReport) _then; | ||||
|  | ||||
| /// 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? 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 | ||||
| as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable | ||||
| as int,reason: null == reason ? _self.reason : reason // ignore: cast_nullable_to_non_nullable | ||||
| 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,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?, | ||||
|   )); | ||||
| } | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
| // dart format on | ||||
							
								
								
									
										41
									
								
								lib/models/abuse_report.g.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								lib/models/abuse_report.g.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| // GENERATED CODE - DO NOT MODIFY BY HAND | ||||
|  | ||||
| part of 'abuse_report.dart'; | ||||
|  | ||||
| // ************************************************************************** | ||||
| // JsonSerializableGenerator | ||||
| // ************************************************************************** | ||||
|  | ||||
| _SnAbuseReport _$SnAbuseReportFromJson(Map<String, dynamic> json) => | ||||
|     _SnAbuseReport( | ||||
|       id: json['id'] as String, | ||||
|       resourceIdentifier: json['resource_identifier'] as String, | ||||
|       type: (json['type'] as num).toInt(), | ||||
|       reason: json['reason'] as String, | ||||
|       resolvedAt: | ||||
|           json['resolved_at'] == null | ||||
|               ? null | ||||
|               : DateTime.parse(json['resolved_at'] as String), | ||||
|       resolution: json['resolution'] as String?, | ||||
|       accountId: json['account_id'] as String, | ||||
|       createdAt: DateTime.parse(json['created_at'] as String), | ||||
|       updatedAt: DateTime.parse(json['updated_at'] as String), | ||||
|       deletedAt: | ||||
|           json['deleted_at'] == null | ||||
|               ? null | ||||
|               : DateTime.parse(json['deleted_at'] as String), | ||||
|     ); | ||||
|  | ||||
| Map<String, dynamic> _$SnAbuseReportToJson(_SnAbuseReport instance) => | ||||
|     <String, dynamic>{ | ||||
|       'id': instance.id, | ||||
|       'resource_identifier': instance.resourceIdentifier, | ||||
|       'type': instance.type, | ||||
|       'reason': instance.reason, | ||||
|       'resolved_at': instance.resolvedAt?.toIso8601String(), | ||||
|       'resolution': instance.resolution, | ||||
|       'account_id': instance.accountId, | ||||
|       'created_at': instance.createdAt.toIso8601String(), | ||||
|       'updated_at': instance.updatedAt.toIso8601String(), | ||||
|       'deleted_at': instance.deletedAt?.toIso8601String(), | ||||
|     }; | ||||
							
								
								
									
										38
									
								
								lib/models/abuse_report_type.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								lib/models/abuse_report_type.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -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'; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -11,7 +11,7 @@ import 'package:island/screens/posts/post_search.dart'; | ||||
| import 'package:island/widgets/app_wrapper.dart'; | ||||
| import 'package:island/screens/tabs.dart'; | ||||
| import 'package:island/screens/explore.dart'; | ||||
| import 'package:island/screens/article_detail_screen.dart'; | ||||
| import 'package:island/screens/discovery/article_detail.dart'; | ||||
| import 'package:island/screens/account.dart'; | ||||
| import 'package:island/screens/notification.dart'; | ||||
| import 'package:island/screens/wallet.dart'; | ||||
| @@ -41,6 +41,8 @@ import 'package:island/screens/realm/realms.dart'; | ||||
| import 'package:island/screens/realm/realm_detail.dart'; | ||||
| import 'package:island/screens/account/event_calendar.dart'; | ||||
| import 'package:island/screens/discovery/realms.dart'; | ||||
| import 'package:island/screens/reports/report_detail.dart'; | ||||
| import 'package:island/screens/reports/report_list.dart'; | ||||
|  | ||||
| // Shell route keys for nested navigation | ||||
| final rootNavigatorKey = GlobalKey<NavigatorState>(); | ||||
| @@ -258,6 +260,19 @@ final routerProvider = Provider<GoRouter>((ref) { | ||||
|             builder: (context, state) => const AboutScreen(), | ||||
|           ), | ||||
|  | ||||
|           GoRoute( | ||||
|             path: '/safety/reports/me', | ||||
|             builder: (context, state) => const AbuseReportListScreen(), | ||||
|           ), | ||||
|  | ||||
|           GoRoute( | ||||
|             path: '/safety/reports/me/:id', | ||||
|             builder: (context, state) { | ||||
|               final id = state.pathParameters['id']!; | ||||
|               return AbuseReportDetailScreen(reportId: id); | ||||
|             }, | ||||
|           ), | ||||
|  | ||||
|           // Main tabs with TabsScreen shell | ||||
|           ShellRoute( | ||||
|             navigatorKey: _tabsShellKey, | ||||
|   | ||||
| @@ -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<AboutScreen> { | ||||
|   Widget build(BuildContext context) { | ||||
|     final theme = Theme.of(context); | ||||
|  | ||||
|     return Scaffold( | ||||
|     return AppScaffold( | ||||
|       appBar: AppBar(title: Text('about'.tr()), elevation: 0), | ||||
|       body: | ||||
|           _isLoading | ||||
|   | ||||
| @@ -222,9 +222,17 @@ class AccountScreen extends HookConsumerWidget { | ||||
|               contentPadding: EdgeInsets.symmetric(horizontal: 24), | ||||
|               title: Text('relationships').tr(), | ||||
|               onTap: () { | ||||
|                 context.push('/account/relationship'); | ||||
|                 context.push('/account/relationships'); | ||||
|               }, | ||||
|             ), | ||||
|             ListTile( | ||||
|               minTileHeight: 48, | ||||
|               title: Text('abuseReports').tr(), | ||||
|               contentPadding: const EdgeInsets.only(left: 24, right: 17), | ||||
|               leading: const Icon(Symbols.gavel), | ||||
|               trailing: const Icon(Symbols.chevron_right), | ||||
|               onTap: () => context.push('/safety/reports/me'), | ||||
|             ), | ||||
|             const Divider(height: 1).padding(vertical: 8), | ||||
|             ListTile( | ||||
|               minTileHeight: 48, | ||||
|   | ||||
| @@ -22,6 +22,7 @@ import 'package:island/widgets/account/status.dart'; | ||||
| import 'package:island/widgets/alert.dart'; | ||||
| import 'package:island/widgets/app_scaffold.dart'; | ||||
| import 'package:island/widgets/content/cloud_files.dart'; | ||||
| import 'package:island/widgets/safety/abuse_report_helper.dart'; | ||||
| import 'package:material_symbols_icons/symbols.dart'; | ||||
| import 'package:palette_generator/palette_generator.dart'; | ||||
| import 'package:riverpod_annotation/riverpod_annotation.dart'; | ||||
| @@ -143,6 +144,23 @@ class AccountProfileScreen extends HookConsumerWidget { | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     Future<void> blockAction() async { | ||||
|       showLoadingModal(context); | ||||
|       try { | ||||
|         final client = ref.watch(apiClientProvider); | ||||
|         if (accountRelationship.value == null) { | ||||
|           await client.post('/relationships/${account.value!.id}/block'); | ||||
|         } else { | ||||
|           await client.delete('/relationships/${account.value!.id}/block'); | ||||
|         } | ||||
|         ref.invalidate(accountRelationshipProvider(name)); | ||||
|       } catch (err) { | ||||
|         showErrorAlert(err); | ||||
|       } finally { | ||||
|         if (context.mounted) hideLoadingModal(context); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     Future<void> directMessageAction() async { | ||||
|       if (!account.hasValue) return; | ||||
|       if (accountChat.value != null) { | ||||
| @@ -396,49 +414,108 @@ class AccountProfileScreen extends HookConsumerWidget { | ||||
|                     child: Row( | ||||
|                       spacing: 8, | ||||
|                       children: [ | ||||
|                         Expanded( | ||||
|                           child: FilledButton.icon( | ||||
|                             style: ButtonStyle( | ||||
|                               backgroundColor: WidgetStatePropertyAll( | ||||
|                                 accountRelationship.value == null | ||||
|                                     ? null | ||||
|                                     : Theme.of(context).colorScheme.secondary, | ||||
|                               ), | ||||
|                               foregroundColor: WidgetStatePropertyAll( | ||||
|                                 accountRelationship.value == null | ||||
|                                     ? null | ||||
|                                     : Theme.of(context).colorScheme.onSecondary, | ||||
|                               ), | ||||
|                             ), | ||||
|                             onPressed: relationshipAction, | ||||
|                             label: | ||||
|                                 Text( | ||||
|                         if (accountRelationship.value == null || | ||||
|                             accountRelationship.value!.status > -100) | ||||
|                           Expanded( | ||||
|                             child: FilledButton.icon( | ||||
|                               style: ButtonStyle( | ||||
|                                 backgroundColor: WidgetStatePropertyAll( | ||||
|                                   accountRelationship.value == null | ||||
|                                       ? 'addFriendShort' | ||||
|                                       : 'added', | ||||
|                                 ).tr(), | ||||
|                             icon: | ||||
|                                 accountRelationship.value == null | ||||
|                                     ? const Icon(Symbols.person_add) | ||||
|                                     : const Icon(Symbols.person_check), | ||||
|                                       ? null | ||||
|                                       : Theme.of(context).colorScheme.secondary, | ||||
|                                 ), | ||||
|                                 foregroundColor: WidgetStatePropertyAll( | ||||
|                                   accountRelationship.value == null | ||||
|                                       ? null | ||||
|                                       : Theme.of( | ||||
|                                         context, | ||||
|                                       ).colorScheme.onSecondary, | ||||
|                                 ), | ||||
|                               ), | ||||
|                               onPressed: relationshipAction, | ||||
|                               label: | ||||
|                                   Text( | ||||
|                                     accountRelationship.value == null | ||||
|                                         ? 'addFriendShort' | ||||
|                                         : 'added', | ||||
|                                   ).tr(), | ||||
|                               icon: | ||||
|                                   accountRelationship.value == null | ||||
|                                       ? const Icon(Symbols.person_add) | ||||
|                                       : const Icon(Symbols.person_check), | ||||
|                             ), | ||||
|                           ), | ||||
|                         ), | ||||
|                         Expanded( | ||||
|                           child: FilledButton.icon( | ||||
|                             onPressed: directMessageAction, | ||||
|                             icon: const Icon(Symbols.message), | ||||
|                             label: | ||||
|                                 Text( | ||||
|                                   accountChat.value == null | ||||
|                                       ? 'createDirectMessage' | ||||
|                                       : 'gotoDirectMessage', | ||||
|                                   maxLines: 1, | ||||
|                                 ).tr(), | ||||
|                         if (accountRelationship.value == null || | ||||
|                             accountRelationship.value!.status <= -100) | ||||
|                           Expanded( | ||||
|                             child: FilledButton.icon( | ||||
|                               style: ButtonStyle( | ||||
|                                 backgroundColor: WidgetStatePropertyAll( | ||||
|                                   accountRelationship.value == null | ||||
|                                       ? null | ||||
|                                       : Theme.of(context).colorScheme.secondary, | ||||
|                                 ), | ||||
|                                 foregroundColor: WidgetStatePropertyAll( | ||||
|                                   accountRelationship.value == null | ||||
|                                       ? null | ||||
|                                       : Theme.of( | ||||
|                                         context, | ||||
|                                       ).colorScheme.onSecondary, | ||||
|                                 ), | ||||
|                               ), | ||||
|                               onPressed: blockAction, | ||||
|                               label: | ||||
|                                   Text( | ||||
|                                     accountRelationship.value == null | ||||
|                                         ? 'blockUser' | ||||
|                                         : 'unblockUser', | ||||
|                                   ).tr(), | ||||
|                               icon: | ||||
|                                   accountRelationship.value == null | ||||
|                                       ? const Icon(Symbols.block) | ||||
|                                       : const Icon(Symbols.person_cancel), | ||||
|                             ), | ||||
|                           ), | ||||
|                         ), | ||||
|                       ], | ||||
|                     ).padding(horizontal: 16), | ||||
|                   ), | ||||
|                 SliverToBoxAdapter( | ||||
|                   child: Row( | ||||
|                     spacing: 8, | ||||
|                     children: [ | ||||
|                       Expanded( | ||||
|                         child: FilledButton.icon( | ||||
|                           onPressed: directMessageAction, | ||||
|                           icon: const Icon(Symbols.message), | ||||
|                           label: | ||||
|                               Text( | ||||
|                                 accountChat.value == null | ||||
|                                     ? 'createDirectMessage' | ||||
|                                     : 'gotoDirectMessage', | ||||
|                                 maxLines: 1, | ||||
|                               ).tr(), | ||||
|                         ), | ||||
|                       ), | ||||
|                       IconButton.filled( | ||||
|                         onPressed: () { | ||||
|                           showAbuseReportSheet( | ||||
|                             context, | ||||
|                             resourceIdentifier: 'account/${data.id}', | ||||
|                           ); | ||||
|                         }, | ||||
|                         icon: Icon( | ||||
|                           Symbols.flag, | ||||
|                           color: Theme.of(context).colorScheme.onError, | ||||
|                         ), | ||||
|                         style: ButtonStyle( | ||||
|                           backgroundColor: WidgetStatePropertyAll( | ||||
|                             Theme.of(context).colorScheme.error, | ||||
|                           ), | ||||
|                         ), | ||||
|                       ), | ||||
|                     ], | ||||
|                   ).padding(horizontal: 16, top: 4), | ||||
|                 ), | ||||
|                 SliverToBoxAdapter( | ||||
|                   child: const Divider(height: 1).padding(top: 12), | ||||
|                 ), | ||||
|   | ||||
| @@ -395,7 +395,7 @@ class _AccountAppbarForcegroundColorProviderElement | ||||
|   String get uname => (origin as AccountAppbarForcegroundColorProvider).uname; | ||||
| } | ||||
|  | ||||
| String _$accountDirectChatHash() => r'60d0015fc2a3c8fc2190bb41d6818cf3027d9d0a'; | ||||
| String _$accountDirectChatHash() => r'3d28c8ba8079159f724fe3cd47bbe00db55cedcc'; | ||||
|  | ||||
| /// See also [accountDirectChat]. | ||||
| @ProviderFor(accountDirectChat) | ||||
| @@ -517,7 +517,7 @@ class _AccountDirectChatProviderElement | ||||
| } | ||||
|  | ||||
| String _$accountRelationshipHash() => | ||||
|     r'cb7d0d3f8cd4f23ad9d2d529872c540dac483d4f'; | ||||
|     r'0be2420e1f6a65b8dcead9617191471924aaf232'; | ||||
|  | ||||
| /// See also [accountRelationship]. | ||||
| @ProviderFor(accountRelationship) | ||||
|   | ||||
| @@ -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: [ | ||||
|   | ||||
| @@ -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( | ||||
|   | ||||
| @@ -338,7 +338,7 @@ class _ActivityListView extends HookConsumerWidget { | ||||
|                             bottom: 16, | ||||
|                           ) | ||||
|                           : null, | ||||
|                   onRefresh: (_) { | ||||
|                   onRefresh: () { | ||||
|                     activitiesNotifier.forceRefresh(); | ||||
|                   }, | ||||
|                   onUpdate: (post) { | ||||
|   | ||||
| @@ -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<PostSearchScreen> { | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Scaffold( | ||||
|     return AppScaffold( | ||||
|       appBar: AppBar( | ||||
|         title: TextField( | ||||
|           controller: _searchController, | ||||
|   | ||||
| @@ -187,7 +187,7 @@ class PublisherProfileScreen extends HookConsumerWidget { | ||||
|                         ), | ||||
|                         onTap: () { | ||||
|                           Navigator.pop(context, true); | ||||
|                           context.push('/account/${data.name}'); | ||||
|                           context.push('/account/${data.account?.name}'); | ||||
|                         }, | ||||
|                       ), | ||||
|                       Expanded( | ||||
|   | ||||
| @@ -77,6 +77,7 @@ class RealmDetailScreen extends HookConsumerWidget { | ||||
|     ); | ||||
|  | ||||
|     return AppScaffold( | ||||
|       noBackground: false, | ||||
|       body: realmState.when( | ||||
|         loading: () => const Center(child: CircularProgressIndicator()), | ||||
|         error: (error, _) => Center(child: Text('Error: $error')), | ||||
|   | ||||
							
								
								
									
										105
									
								
								lib/screens/reports/report_detail.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								lib/screens/reports/report_detail.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | ||||
| 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; | ||||
|  | ||||
|   const AbuseReportDetailScreen({super.key, required this.reportId}); | ||||
|  | ||||
|   @override | ||||
|   ConsumerState<AbuseReportDetailScreen> createState() => | ||||
|       _AbuseReportDetailScreenState(); | ||||
| } | ||||
|  | ||||
| class _AbuseReportDetailScreenState | ||||
|     extends ConsumerState<AbuseReportDetailScreen> { | ||||
|   Future<SnAbuseReport>? _reportFuture; | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|     _reportFuture = ref | ||||
|         .read(abuseReportServiceProvider) | ||||
|         .getReport(widget.reportId); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return AppScaffold( | ||||
|       appBar: AppBar(title: const Text('Abuse Report Details')), | ||||
|       body: FutureBuilder<SnAbuseReport>( | ||||
|         future: _reportFuture, | ||||
|         builder: (context, snapshot) { | ||||
|           if (snapshot.connectionState == ConnectionState.waiting) { | ||||
|             return const Center(child: CircularProgressIndicator()); | ||||
|           } else if (snapshot.hasError) { | ||||
|             return Center(child: Text('Error: ${snapshot.error}')); | ||||
|           } else if (snapshot.hasData) { | ||||
|             final report = snapshot.data!; | ||||
|             return Padding( | ||||
|               padding: const EdgeInsets.all(16.0), | ||||
|               child: Column( | ||||
|                 crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                 children: [ | ||||
|                   _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(), | ||||
|                   ), | ||||
|                 ], | ||||
|               ), | ||||
|             ); | ||||
|           } else { | ||||
|             return const Center(child: Text('No data')); | ||||
|           } | ||||
|         }, | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   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), | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										153
									
								
								lib/screens/reports/report_list.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								lib/screens/reports/report_list.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,153 @@ | ||||
| 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}); | ||||
|  | ||||
|   @override | ||||
|   ConsumerState<AbuseReportListScreen> createState() => | ||||
|       _AbuseReportListScreenState(); | ||||
| } | ||||
|  | ||||
| class _AbuseReportListScreenState extends ConsumerState<AbuseReportListScreen> { | ||||
|   Future<List<SnAbuseReport>>? _reportsFuture; | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|     _reportsFuture = ref.read(abuseReportServiceProvider).getReports(); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return AppScaffold( | ||||
|       appBar: AppBar(title: Text('abuseReports').tr()), | ||||
|       floatingActionButton: FloatingActionButton( | ||||
|         child: const Icon(Icons.add), | ||||
|         onPressed: () { | ||||
|           showAbuseReportSheet(context, resourceIdentifier: 'unidentified'); | ||||
|         }, | ||||
|       ), | ||||
|       body: FutureBuilder<List<SnAbuseReport>>( | ||||
|         future: _reportsFuture, | ||||
|         builder: (context, snapshot) { | ||||
|           if (snapshot.connectionState == ConnectionState.waiting) { | ||||
|             return const Center(child: CircularProgressIndicator()); | ||||
|           } else if (snapshot.hasError) { | ||||
|             return Center(child: Text('Error: ${snapshot.error}')); | ||||
|           } 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 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, | ||||
|                                 ), | ||||
|                               ), | ||||
|                             ], | ||||
|                           ), | ||||
|                         ], | ||||
|                       ), | ||||
|                     ), | ||||
|                   ), | ||||
|                 ); | ||||
|               }, | ||||
|             ); | ||||
|           } else { | ||||
|             return const Center(child: Text('No data')); | ||||
|           } | ||||
|         }, | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										25
									
								
								lib/services/abuse_report_service.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								lib/services/abuse_report_service.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:island/models/abuse_report.dart'; | ||||
| import 'package:island/pods/network.dart'; | ||||
|  | ||||
| final abuseReportServiceProvider = Provider<AbuseReportService>((ref) { | ||||
|   return AbuseReportService(ref); | ||||
| }); | ||||
|  | ||||
| class AbuseReportService { | ||||
|   final Ref ref; | ||||
|   AbuseReportService(this.ref); | ||||
|  | ||||
|   Future<SnAbuseReport> getReport(String id) async { | ||||
|     final response = | ||||
|         await ref.read(apiClientProvider).get('/safety/reports/me/$id'); | ||||
|     return SnAbuseReport.fromJson(response.data); | ||||
|   } | ||||
|  | ||||
|   Future<List<SnAbuseReport>> getReports() async { | ||||
|     final response = await ref.read(apiClientProvider).get('/safety/reports/me'); | ||||
|     return (response.data as List) | ||||
|         .map((json) => SnAbuseReport.fromJson(json)) | ||||
|         .toList(); | ||||
|   } | ||||
| } | ||||
| @@ -68,7 +68,6 @@ Future<void> subscribePushNotification( | ||||
|   bool detailedErrors = false, | ||||
| }) async { | ||||
|   await FirebaseMessaging.instance.requestPermission( | ||||
|     provisional: true, | ||||
|     alert: true, | ||||
|     badge: true, | ||||
|     sound: true, | ||||
|   | ||||
							
								
								
									
										22
									
								
								lib/utils/abuse_report_utils.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								lib/utils/abuse_report_utils.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -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'; | ||||
|   } | ||||
| } | ||||
| @@ -384,7 +384,12 @@ class PostItem extends HookConsumerWidget { | ||||
|                         // Show truncation hint if post is truncated | ||||
|                         if (item.isTruncated && !isFullPost && item.type != 1) | ||||
|                           _PostTruncateHint().padding( | ||||
|                             bottom: item.attachments.isNotEmpty ? 8 : null, | ||||
|                             bottom: | ||||
|                                 (item.attachments.isNotEmpty || | ||||
|                                         item.repliedPost != null || | ||||
|                                         item.forwardedPost != null) | ||||
|                                     ? 8 | ||||
|                                     : null, | ||||
|                           ), | ||||
|                         if ((item.repliedPost != null || | ||||
|                                 item.forwardedPost != null) && | ||||
| @@ -571,7 +576,7 @@ class PostItem extends HookConsumerWidget { | ||||
|               callback: () { | ||||
|                 showAbuseReportSheet( | ||||
|                   context, | ||||
|                   resourceIdentifier: 'posts:${item.id}', | ||||
|                   resourceIdentifier: 'post/${item.id}', | ||||
|                 ); | ||||
|               }, | ||||
|             ), | ||||
|   | ||||
		Reference in New Issue
	
	Block a user