From f33d5d71e90a6d318efe128d0784bca7a5ffd0b4 Mon Sep 17 00:00:00 2001 From: Nikhil Rajput Date: Tue, 31 Jan 2023 12:58:13 +0530 Subject: [PATCH] bumped to version 1.0.1+31 --- assets/svgs/add_files.svg | 1 + ...ofile_pic_female.svg => avatar_female.svg} | 2 +- .../{profile_pic_male.svg => avatar_male.svg} | 2 +- assets/svgs/messaging.svg | 1 + assets/svgs/no_data.svg | 1 + assets/svgs/update.svg | 1 + assets/svgs/upgrade.svg | 1 - .../responses/blocked_users_response.dart | 55 +++++ .../responses/blocked_users_response.g.dart | 39 +++ lib/apis/providers/api_provider.dart | 15 +- lib/constants/assets.dart | 9 +- lib/constants/colors.dart | 4 +- lib/constants/enums.dart | 1 + lib/constants/hive_box_names.dart | 12 + lib/constants/strings.dart | 12 + lib/constants/urls.dart | 1 + .../expandable_text_widget.dart | 2 +- lib/global_widgets/no_data_widget.dart | 44 ++++ lib/main.dart | 21 +- lib/modules/app_update/app_update_view.dart | 2 +- .../auth/controllers/register_controller.dart | 22 +- .../block_user/block_user_binding.dart | 9 + .../block_user/block_user_controller.dart | 98 ++++++++ lib/modules/block_user/block_user_view.dart | 113 +++++++++ .../widgets/follow_request_widget.dart | 3 +- .../follower/views/followers_list_view.dart | 3 +- .../follower/views/following_list_view.dart | 3 +- .../home/controllers/profile_controller.dart | 226 +++++++++++++++--- .../home/views/trending_tabs/people_tab.dart | 3 +- .../views/widgets/notification_widget.dart | 4 +- .../home/views/widgets/post_widget.dart | 45 +++- .../post/views/post_liked_users_view.dart | 4 +- .../post/views/widgets/comment_widget.dart | 9 +- .../views/widgets/post_details_widget.dart | 6 +- lib/modules/profile/views/profile_view.dart | 2 +- lib/modules/report/report_controller.dart | 9 +- .../pages/privacy/blocked_users_view.dart | 133 +++++++++++ .../mute_block_privacy_settings_view.dart | 73 ++++++ .../settings/views/privacy_settings_view.dart | 70 ++++-- lib/modules/user/user_profile_view.dart | 181 +++++++++++--- lib/routes/app_pages.dart | 27 +++ lib/routes/app_routes.dart | 6 +- lib/routes/route_management.dart | 50 ++-- 43 files changed, 1166 insertions(+), 159 deletions(-) create mode 100644 assets/svgs/add_files.svg rename assets/svgs/{profile_pic_female.svg => avatar_female.svg} (96%) rename assets/svgs/{profile_pic_male.svg => avatar_male.svg} (98%) create mode 100644 assets/svgs/messaging.svg create mode 100644 assets/svgs/no_data.svg create mode 100644 assets/svgs/update.svg delete mode 100644 assets/svgs/upgrade.svg create mode 100644 lib/apis/models/responses/blocked_users_response.dart create mode 100644 lib/apis/models/responses/blocked_users_response.g.dart create mode 100644 lib/constants/hive_box_names.dart create mode 100644 lib/global_widgets/no_data_widget.dart create mode 100644 lib/modules/block_user/block_user_binding.dart create mode 100644 lib/modules/settings/views/pages/privacy/blocked_users_view.dart create mode 100644 lib/modules/settings/views/pages/privacy/mute_block_privacy_settings_view.dart diff --git a/assets/svgs/add_files.svg b/assets/svgs/add_files.svg new file mode 100644 index 0000000..201e2dc --- /dev/null +++ b/assets/svgs/add_files.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/profile_pic_female.svg b/assets/svgs/avatar_female.svg similarity index 96% rename from assets/svgs/profile_pic_female.svg rename to assets/svgs/avatar_female.svg index c34faf7..5b36b03 100644 --- a/assets/svgs/profile_pic_female.svg +++ b/assets/svgs/avatar_female.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/svgs/profile_pic_male.svg b/assets/svgs/avatar_male.svg similarity index 98% rename from assets/svgs/profile_pic_male.svg rename to assets/svgs/avatar_male.svg index 308eb59..b6635be 100644 --- a/assets/svgs/profile_pic_male.svg +++ b/assets/svgs/avatar_male.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/svgs/messaging.svg b/assets/svgs/messaging.svg new file mode 100644 index 0000000..05dce8b --- /dev/null +++ b/assets/svgs/messaging.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/no_data.svg b/assets/svgs/no_data.svg new file mode 100644 index 0000000..e51de65 --- /dev/null +++ b/assets/svgs/no_data.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/update.svg b/assets/svgs/update.svg new file mode 100644 index 0000000..371e88f --- /dev/null +++ b/assets/svgs/update.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/upgrade.svg b/assets/svgs/upgrade.svg deleted file mode 100644 index 4a791e6..0000000 --- a/assets/svgs/upgrade.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/lib/apis/models/responses/blocked_users_response.dart b/lib/apis/models/responses/blocked_users_response.dart new file mode 100644 index 0000000..8de618b --- /dev/null +++ b/lib/apis/models/responses/blocked_users_response.dart @@ -0,0 +1,55 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:social_media_app/apis/models/entities/user.dart'; + +part 'blocked_users_response.g.dart'; + +@JsonSerializable() +class BlockedUsersResponse { + const BlockedUsersResponse({ + this.success, + this.currentPage, + this.totalPages, + this.limit, + this.hasPrevPage, + this.prevPage, + this.hasNextPage, + this.nextPage, + this.length, + this.results, + }); + + factory BlockedUsersResponse.fromJson(Map json) => + _$BlockedUsersResponseFromJson(json); + + Map toJson() => _$BlockedUsersResponseToJson(this); + + @JsonKey(name: 'success') + final bool? success; + + @JsonKey(name: 'currentPage') + final int? currentPage; + + @JsonKey(name: 'totalPages') + final int? totalPages; + + @JsonKey(name: 'limit') + final int? limit; + + @JsonKey(name: 'hasPrevPage') + final bool? hasPrevPage; + + @JsonKey(name: 'prevPage') + final String? prevPage; + + @JsonKey(name: 'hasNextPage') + final bool? hasNextPage; + + @JsonKey(name: 'nextPage') + final String? nextPage; + + @JsonKey(name: 'length') + final int? length; + + @JsonKey(name: 'results') + final List? results; +} diff --git a/lib/apis/models/responses/blocked_users_response.g.dart b/lib/apis/models/responses/blocked_users_response.g.dart new file mode 100644 index 0000000..05b1c18 --- /dev/null +++ b/lib/apis/models/responses/blocked_users_response.g.dart @@ -0,0 +1,39 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'blocked_users_response.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +BlockedUsersResponse _$BlockedUsersResponseFromJson( + Map json) => + BlockedUsersResponse( + success: json['success'] as bool?, + currentPage: json['currentPage'] as int?, + totalPages: json['totalPages'] as int?, + limit: json['limit'] as int?, + hasPrevPage: json['hasPrevPage'] as bool?, + prevPage: json['prevPage'] as String?, + hasNextPage: json['hasNextPage'] as bool?, + nextPage: json['nextPage'] as String?, + length: json['length'] as int?, + results: (json['results'] as List?) + ?.map((e) => User.fromJson(e as Map)) + .toList(), + ); + +Map _$BlockedUsersResponseToJson( + BlockedUsersResponse instance) => + { + 'success': instance.success, + 'currentPage': instance.currentPage, + 'totalPages': instance.totalPages, + 'limit': instance.limit, + 'hasPrevPage': instance.hasPrevPage, + 'prevPage': instance.prevPage, + 'hasNextPage': instance.hasNextPage, + 'nextPage': instance.nextPage, + 'length': instance.length, + 'results': instance.results, + }; diff --git a/lib/apis/providers/api_provider.dart b/lib/apis/providers/api_provider.dart index 904693e..9d8da37 100644 --- a/lib/apis/providers/api_provider.dart +++ b/lib/apis/providers/api_provider.dart @@ -1432,6 +1432,20 @@ class ApiProvider { return response; } + /// Report Comment Reply + Future reportCommentReply( + String token, Map body) async { + final response = await _catchAsyncApiError( + endPoint: AppUrls.reportCommentReplyEndpoint, + method: 'POST', + feature: 'Report Comment Reply', + headers: {"authorization": "Bearer $token"}, + body: body, + ); + + return response; + } + /// -------------------------------------------------------------------------- /// Block -------------------------------------------------------------------- @@ -1541,7 +1555,6 @@ class ApiProvider { try { switch (method) { - /// GET request case "GET": var response = await _client.get( diff --git a/lib/constants/assets.dart b/lib/constants/assets.dart index 124ac81..75b1ac1 100644 --- a/lib/constants/assets.dart +++ b/lib/constants/assets.dart @@ -12,7 +12,10 @@ abstract class RiveAssets { } abstract class SvgAssets { - static const femaleAvatar = 'assets/svgs/profile_pic_female.svg'; - static const maleAvatar = 'assets/svgs/profile_pic_male.svg'; - static const upgrade = 'assets/svgs/upgrade.svg'; + static const femaleAvatar = 'assets/svgs/avatar_female.svg'; + static const maleAvatar = 'assets/svgs/avatar_male.svg'; + static const update = 'assets/svgs/update.svg'; + static const noData = 'assets/svgs/no_data.svg'; + static const addFiles = 'assets/svgs/add_files.svg'; + static const messaging = 'assets/svgs/messaging.svg'; } diff --git a/lib/constants/colors.dart b/lib/constants/colors.dart index ac666b2..c4967a4 100644 --- a/lib/constants/colors.dart +++ b/lib/constants/colors.dart @@ -28,8 +28,8 @@ abstract class ColorValues { Color.fromARGB(255, 100, 100, 100); static const Color linkColor = Color.fromARGB(255, 56, 130, 240); - static const Color primaryColor = Color.fromARGB(255, 238, 113, 30); - static const Color primaryLightColor = Color.fromARGB(255, 235, 146, 87); + static const Color primaryColor = Color.fromARGB(255, 240, 90, 100); + static const Color primaryLightColor = Color.fromARGB(200, 240, 90, 100); static const Color shadowColor = Color.fromARGB(255, 0, 0, 0); static const Color successColor = Color.fromARGB(255, 76, 175, 80); diff --git a/lib/constants/enums.dart b/lib/constants/enums.dart index 39af4d4..51fb3eb 100644 --- a/lib/constants/enums.dart +++ b/lib/constants/enums.dart @@ -19,4 +19,5 @@ enum ReportType { user, post, comment, + commentReply, } diff --git a/lib/constants/hive_box_names.dart b/lib/constants/hive_box_names.dart new file mode 100644 index 0000000..62e6d7c --- /dev/null +++ b/lib/constants/hive_box_names.dart @@ -0,0 +1,12 @@ +abstract class HiveBoxNames { + static const themeMode = 'themeMode'; + static const posts = 'posts'; + static const trendingPosts = 'trendingPosts'; + static const recommendedUsers = 'recommendedUsers'; + static const lastMessages = 'lastMessages'; + static const notifications = 'notifications'; + static const profilePosts = 'profilePosts'; + static const followers = 'followers'; + static const followings = 'followings'; + static const blockedUsers = 'blockedUsers'; +} diff --git a/lib/constants/strings.dart b/lib/constants/strings.dart index 9e2dde0..17910d6 100644 --- a/lib/constants/strings.dart +++ b/lib/constants/strings.dart @@ -43,6 +43,10 @@ abstract class StringValues { static const back = 'Back'; static const birthDate = 'Date of Birth'; static const block = 'Block'; + static const blockUser = 'Block User'; + static const blockUserHelp = 'Are you sure you want to block this user?'; + static const blockDesc = + 'They won\'t be able to see your posts or find you. They will be removed from your followers and you won\'t be able to see their posts or find them. You can unblock them anytime. They won\'t be notified that you blocked them.'; static const blocked = 'Blocked'; static const blockedAccountWarningDesc = 'You can\'t follow, message or see their posts'; @@ -338,6 +342,12 @@ abstract class StringValues { static const postDetailsNotFound = 'Post deleted or archived'; static const postPrivacy = 'Post Privacy'; static const postPrivacyDesc = 'Who can see your posts?'; + static const muteAndBlock = 'Mute & Block'; + static const blockedUsers = 'Blocked Users'; + static const blockedUsersDesc = 'Manage users you have blocked.'; + static const noBlockedUsers = 'No blocked users yet'; + static const muteAndBlockDesc = + 'Mute and block users to stop seeing their posts and stories.'; static const posts = 'Posts'; static const privacy = 'Privacy'; static const privacyPolicy = 'Privacy Policy'; @@ -456,6 +466,8 @@ abstract class StringValues { 'Help protect your account from unauthorised access by requiring a second authentication method in addition to your password.'; static const unblock = 'Unblock'; + static const unblockDesc = + 'The user will be able to follow and message you again. They won\'t be notified that you\'ve unblocked them.'; static const unfollow = 'Unfollow'; static const unknownErrorOccurred = 'An unknown error occurred.'; static const update = 'Update'; diff --git a/lib/constants/urls.dart b/lib/constants/urls.dart index cf57d5b..f0804e3 100644 --- a/lib/constants/urls.dart +++ b/lib/constants/urls.dart @@ -54,6 +54,7 @@ abstract class AppUrls { static const registerEndpoint = '/register'; static const removeFollowRequestEndpoint = '/remove-follow-request'; static const reportCommentEndpoint = '/report-comment'; + static const reportCommentReplyEndpoint = '/report-comment-reply'; static const reportPostEndpoint = '/report-post'; static const reportUserEndpoint = '/report-user'; static const requestVerificationEndpoint = '/request-verification'; diff --git a/lib/global_widgets/expandable_text_widget.dart b/lib/global_widgets/expandable_text_widget.dart index da8c098..d10d3e3 100644 --- a/lib/global_widgets/expandable_text_widget.dart +++ b/lib/global_widgets/expandable_text_widget.dart @@ -76,7 +76,7 @@ class NxExpandableTextState extends State { } else if (link.url.contains("#")) { AppUtility.printLog(link.text); } else if (link.url.contains("@")) { - RouteManagement.goToUserProfileViewByUsername( + RouteManagement.goToUserProfileDetailsViewByUsername( link.url.replaceAll('@', '')); } else { AppUtility.printLog(link); diff --git a/lib/global_widgets/no_data_widget.dart b/lib/global_widgets/no_data_widget.dart new file mode 100644 index 0000000..bd4da2f --- /dev/null +++ b/lib/global_widgets/no_data_widget.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:social_media_app/constants/assets.dart'; +import 'package:social_media_app/constants/dimens.dart'; +import 'package:social_media_app/constants/styles.dart'; + +class NoDataWidget extends StatelessWidget { + const NoDataWidget({ + super.key, + this.message, + this.padding, + }); + + final String? message; + final EdgeInsetsGeometry? padding; + + @override + Widget build(BuildContext context) { + return Padding( + padding: padding ?? Dimens.edgeInsets0, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + SvgPicture.asset( + SvgAssets.noData, + width: Dimens.screenWidth * 0.25, + height: Dimens.screenWidth * 0.25, + ), + if (message != null) Dimens.boxHeight16, + if (message != null) + Text( + message!, + textAlign: TextAlign.center, + style: AppStyles.style32Bold.copyWith( + color: Theme.of(context).textTheme.titleSmall!.color, + ), + ), + ], + ), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index 9472ebe..1b01853 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -24,6 +24,7 @@ import 'package:social_media_app/app_services/network_controller.dart'; import 'package:social_media_app/app_services/route_service.dart'; import 'package:social_media_app/app_services/theme_controller.dart'; import 'package:social_media_app/constants/enums.dart'; +import 'package:social_media_app/constants/hive_box_names.dart'; import 'package:social_media_app/constants/strings.dart'; import 'package:social_media_app/modules/app_update/app_update_controller.dart'; import 'package:social_media_app/modules/chat/controllers/chat_controller.dart'; @@ -94,16 +95,16 @@ Future _initPreAppServices() async { AppUtility.log('Opening Hive Boxes'); - await Hive.openBox('themeMode'); - await Hive.openBox('posts'); - await Hive.openBox('trendingPosts'); - await Hive.openBox('recommendedUsers'); - await Hive.openBox('lastMessages'); - await Hive.openBox('notifications'); - await Hive.openBox('profilePosts'); - await Hive.openBox('followers'); - await Hive.openBox('followings'); - await Hive.openBox('blockedUsers'); + await Hive.openBox(HiveBoxNames.themeMode); + await Hive.openBox(HiveBoxNames.posts); + await Hive.openBox(HiveBoxNames.trendingPosts); + await Hive.openBox(HiveBoxNames.recommendedUsers); + await Hive.openBox(HiveBoxNames.lastMessages); + await Hive.openBox(HiveBoxNames.notifications); + await Hive.openBox(HiveBoxNames.profilePosts); + await Hive.openBox(HiveBoxNames.followers); + await Hive.openBox(HiveBoxNames.followings); + await Hive.openBox(HiveBoxNames.blockedUsers); AppUtility.log('Hive Boxes Opened'); diff --git a/lib/modules/app_update/app_update_view.dart b/lib/modules/app_update/app_update_view.dart index d0f2dda..ac5fb9f 100644 --- a/lib/modules/app_update/app_update_view.dart +++ b/lib/modules/app_update/app_update_view.dart @@ -78,7 +78,7 @@ class AppUpdateView extends StatelessWidget { SvgPicture _buildSvgImage(BuildContext context) { return SvgPicture.asset( - SvgAssets.upgrade, + SvgAssets.update, width: Dimens.screenWidth * 0.25, height: Dimens.screenWidth * 0.25, ); diff --git a/lib/modules/auth/controllers/register_controller.dart b/lib/modules/auth/controllers/register_controller.dart index 1cb09e0..fa755a9 100644 --- a/lib/modules/auth/controllers/register_controller.dart +++ b/lib/modules/auth/controllers/register_controller.dart @@ -252,7 +252,6 @@ class RegisterController extends GetxController { 'uname': unameTextController.text.trim(), 'password': passwordTextController.text.trim(), 'confirmPassword': confirmPasswordTextController.text.trim(), - 'isValidated': '${_isEmailVerified.value}', }; AppUtility.showLoadingDialog(); @@ -263,16 +262,33 @@ class RegisterController extends GetxController { final response = await _apiProvider.register(body); if (response.isSuccessful) { + final decodedData = response.data; + + String validateOtp = decodedData['data']['otp']; + + if (validateOtp.isNotEmpty) { + final data = { + 'email': emailTextController.text.trim(), + 'otp': validateOtp, + }; + final validationResponse = await _apiProvider.validateUser(data); + + if (validationResponse.isSuccessful) { + final decodedData = validationResponse.data; + AppUtility.log('Validation Response: $decodedData'); + } + } + _clearRegisterTextControllers(); AppUtility.closeDialog(); _isLoading.value = false; update(); + RouteManagement.goToBack(); + RouteManagement.goToLoginView(); AppUtility.showSnackBar( StringValues.registrationSuccessful, StringValues.success, ); - RouteManagement.goToBack(); - RouteManagement.goToLoginView(); } else { final decodedData = response.data; AppUtility.closeDialog(); diff --git a/lib/modules/block_user/block_user_binding.dart b/lib/modules/block_user/block_user_binding.dart new file mode 100644 index 0000000..6deb7f1 --- /dev/null +++ b/lib/modules/block_user/block_user_binding.dart @@ -0,0 +1,9 @@ +import 'package:get/get.dart'; +import 'package:social_media_app/modules/block_user/block_user_controller.dart'; + +class BlockUserBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut(BlockUserController.new); + } +} diff --git a/lib/modules/block_user/block_user_controller.dart b/lib/modules/block_user/block_user_controller.dart index e69de29..853ee6f 100644 --- a/lib/modules/block_user/block_user_controller.dart +++ b/lib/modules/block_user/block_user_controller.dart @@ -0,0 +1,98 @@ +import 'dart:async'; + +import 'package:get/get.dart'; +import 'package:http/http.dart' as http; +import 'package:social_media_app/apis/models/entities/media_file.dart'; +import 'package:social_media_app/apis/models/entities/user.dart'; +import 'package:social_media_app/apis/providers/api_provider.dart'; +import 'package:social_media_app/app_services/auth_service.dart'; +import 'package:social_media_app/constants/hive_box_names.dart'; +import 'package:social_media_app/constants/strings.dart'; +import 'package:social_media_app/modules/home/controllers/profile_controller.dart'; +import 'package:social_media_app/routes/route_management.dart'; +import 'package:social_media_app/services/hive_service.dart'; +import 'package:social_media_app/utils/utility.dart'; + +class BlockUserController extends GetxController { + late String id; + late String uname; + late MediaFile avatar; + + final _apiProvider = ApiProvider(http.Client()); + final _profileController = ProfileController.find; + final _auth = AuthService.find; + final _isLoading = false.obs; + + @override + void onInit() { + super.onInit(); + + if (Get.arguments != null) { + id = Get.arguments['id']; + uname = Get.arguments['uname']; + avatar = Get.arguments['avatar']; + } + } + + static BlockUserController get find => Get.find(); + + bool get isLoading => _isLoading.value; + + Future blockUser() async { + AppUtility.closeFocus(); + await _blockUser(); + } + + Future _blockUser() async { + if (id.isEmpty) { + AppUtility.showSnackBar( + StringValues.userIdNotFound, + StringValues.warning, + ); + return; + } + + AppUtility.showLoadingDialog(); + _isLoading.value = true; + update(); + + try { + final response = await _apiProvider.blockUser(_auth.token, id); + + if (response.isSuccessful) { + final decodedData = response.data; + final item = User.fromJson(decodedData['user']); + await HiveService.put( + HiveBoxNames.blockedUsers, + item.id, + item, + ); + _profileController.blockedUsers.add(item); + _profileController.update(); + AppUtility.closeDialog(); + _isLoading.value = false; + update(); + RouteManagement.goToBack(); + RouteManagement.goToBack(); + AppUtility.showSnackBar( + decodedData[StringValues.message], + StringValues.success, + ); + } else { + final decodedData = response.data; + AppUtility.closeDialog(); + _isLoading.value = false; + update(); + AppUtility.showSnackBar( + decodedData[StringValues.message], + StringValues.error, + ); + } + } catch (exc) { + AppUtility.closeDialog(); + _isLoading.value = false; + update(); + AppUtility.showSnackBar('Error: $exc', StringValues.error); + } + } +} diff --git a/lib/modules/block_user/block_user_view.dart b/lib/modules/block_user/block_user_view.dart index e69de29..66a53d9 100644 --- a/lib/modules/block_user/block_user_view.dart +++ b/lib/modules/block_user/block_user_view.dart @@ -0,0 +1,113 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:social_media_app/constants/dimens.dart'; +import 'package:social_media_app/constants/strings.dart'; +import 'package:social_media_app/constants/styles.dart'; +import 'package:social_media_app/global_widgets/avatar_widget.dart'; +import 'package:social_media_app/global_widgets/custom_app_bar.dart'; +import 'package:social_media_app/global_widgets/custom_list_tile.dart'; +import 'package:social_media_app/global_widgets/primary_filled_btn.dart'; +import 'package:social_media_app/global_widgets/unfocus_widget.dart'; +import 'package:social_media_app/modules/block_user/block_user_controller.dart'; + +class BlockUserView extends StatelessWidget { + const BlockUserView({super.key}); + + Widget _buildBody(BuildContext context) { + return Expanded( + child: SingleChildScrollView( + child: GetBuilder( + builder: (logic) => Padding( + padding: Dimens.edgeInsetsHorizDefault, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Dimens.boxHeight12, + _buildBlockUserHelpText(context), + Dimens.boxHeight4, + _buildBlockUserHelpTextDesc(context), + Dimens.boxHeight12, + _buildUserDetails(logic), + Dimens.boxHeight24, + _buildActionBtn(context, logic), + Dimens.boxHeight12, + ], + ), + ), + ), + ), + ); + } + + NxListTile _buildUserDetails(BlockUserController logic) { + return NxListTile( + showBorder: false, + padding: Dimens.edgeInsets12, + borderRadius: BorderRadius.circular(Dimens.four), + leading: AvatarWidget( + avatar: logic.avatar, + size: Dimens.twentyFour, + ), + title: Text( + logic.uname, + style: AppStyles.style14Bold, + ), + ); + } + + NxFilledButton _buildActionBtn( + BuildContext context, BlockUserController logic) { + return NxFilledButton( + onTap: () => BlockUserController.find.blockUser(), + label: StringValues.block.toUpperCase(), + padding: Dimens.edgeInsetsDefault, + height: Dimens.fiftySix, + ); + } + + Text _buildBlockUserHelpTextDesc(BuildContext context) { + return Text( + StringValues.blockDesc, + style: AppStyles.style13Normal.copyWith( + color: Theme.of(context).textTheme.titleMedium!.color, + ), + ); + } + + Text _buildBlockUserHelpText(BuildContext context) { + return Text( + StringValues.blockUserHelp, + style: AppStyles.style14Bold, + ); + } + + NxAppBar _buildAppBar() { + return NxAppBar( + padding: Dimens.edgeInsetsDefault, + title: StringValues.blockUser, + ); + } + + @override + Widget build(BuildContext context) { + return UnFocusWidget( + child: Scaffold( + body: SafeArea( + child: SizedBox( + width: Dimens.screenWidth, + height: Dimens.screenHeight, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildAppBar(), + _buildBody(context), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/modules/follow_request/widgets/follow_request_widget.dart b/lib/modules/follow_request/widgets/follow_request_widget.dart index b2fd595..abb3eea 100644 --- a/lib/modules/follow_request/widgets/follow_request_widget.dart +++ b/lib/modules/follow_request/widgets/follow_request_widget.dart @@ -38,7 +38,8 @@ class FollowRequestWidget extends StatelessWidget { final profile = ProfileController.find; return GestureDetector( onTap: onTap ?? - () => RouteManagement.goToUserProfileView(followRequest.from.id), + () => RouteManagement.goToUserProfileDetailsViewByUserId( + followRequest.from.id), child: Container( margin: Dimens.edgeInsets6_0, padding: padding ?? Dimens.edgeInsets8, diff --git a/lib/modules/follower/views/followers_list_view.dart b/lib/modules/follower/views/followers_list_view.dart index fe592c6..7c00d02 100644 --- a/lib/modules/follower/views/followers_list_view.dart +++ b/lib/modules/follower/views/followers_list_view.dart @@ -150,7 +150,8 @@ class FollowersListView extends StatelessWidget { user: item, totalLength: logic.followersList.length, index: index, - onTap: () => RouteManagement.goToUserProfileView(item.id), + onTap: () => + RouteManagement.goToUserProfileDetailsViewByUserId(item.id), onActionTap: () { if (item.followingStatus == "requested") { logic.cancelFollowRequest(item); diff --git a/lib/modules/follower/views/following_list_view.dart b/lib/modules/follower/views/following_list_view.dart index 2db8f7f..17f34e6 100644 --- a/lib/modules/follower/views/following_list_view.dart +++ b/lib/modules/follower/views/following_list_view.dart @@ -150,7 +150,8 @@ class FollowingListView extends StatelessWidget { user: item, totalLength: logic.followingList.length, index: index, - onTap: () => RouteManagement.goToUserProfileView(item.id), + onTap: () => + RouteManagement.goToUserProfileDetailsViewByUserId(item.id), onActionTap: () { if (item.followingStatus == "requested") { logic.cancelFollowRequest(item); diff --git a/lib/modules/home/controllers/profile_controller.dart b/lib/modules/home/controllers/profile_controller.dart index ef71db3..865af5e 100644 --- a/lib/modules/home/controllers/profile_controller.dart +++ b/lib/modules/home/controllers/profile_controller.dart @@ -5,28 +5,32 @@ import 'package:get/get.dart'; import 'package:http/http.dart' as http; import 'package:social_media_app/apis/models/entities/post.dart'; import 'package:social_media_app/apis/models/entities/user.dart'; +import 'package:social_media_app/apis/models/responses/blocked_users_response.dart'; import 'package:social_media_app/apis/models/responses/post_response.dart'; import 'package:social_media_app/apis/models/responses/profile_response.dart'; import 'package:social_media_app/apis/providers/api_provider.dart'; import 'package:social_media_app/app_services/auth_service.dart'; +import 'package:social_media_app/constants/hive_box_names.dart'; import 'package:social_media_app/constants/strings.dart'; import 'package:social_media_app/services/hive_service.dart'; import 'package:social_media_app/services/storage_service.dart'; import 'package:social_media_app/utils/utility.dart'; class ProfileController extends GetxController { - static ProfileController get find => Get.find(); - - final _auth = AuthService.find; - final _apiProvider = ApiProvider(http.Client()); - + final _auth = AuthService.find; + final List _blockedUsers = []; + final _blockedUsersResponse = const BlockedUsersResponse().obs; var _isLoading = false; - final _isPostLoading = false.obs; final _isMorePostLoading = false.obs; - final _profileDetails = const ProfileResponse().obs; + final _isPostLoading = false.obs; + final _loadingBlockedUsers = false.obs; + final _loadingMoreBlockedUsers = false.obs; final _postData = PostResponse().obs; final List _postList = []; + final _profileDetails = const ProfileResponse().obs; + + static ProfileController get find => Get.find(); /// Getters bool get isLoading => _isLoading; @@ -35,26 +39,65 @@ class ProfileController extends GetxController { bool get isMorePostLoading => _isMorePostLoading.value; + bool get loadingBlockedUsers => _loadingBlockedUsers.value; + + bool get loadingMoreBlockedUsers => _loadingMoreBlockedUsers.value; + PostResponse? get postData => _postData.value; List get postList => _postList; + List get blockedUsers => _blockedUsers; + ProfileResponse? get profileDetails => _profileDetails.value; + BlockedUsersResponse? get blockedUsersResponse => _blockedUsersResponse.value; + /// Setters set setPostData(PostResponse value) => _postData.value = value; set setProfileDetailsData(ProfileResponse value) => _profileDetails.value = value; - static Future _saveProfileDataToLocalStorage( - ProfileResponse respone) async { + set setBlockedUsersResponse(BlockedUsersResponse value) => + _blockedUsersResponse.value = value; + + Future unblockUser(String userId) async => await _unblockUser(userId); + + Future followUnfollowUser(User user) async => + await _followUnfollowUser(user); + + Future cancelFollowRequest(User user) async => + await _cancelFollowRequest(user); + + Future fetchProfileDetails({bool fetchPost = true}) async => + await _fetchProfileDetails(fetchPost: fetchPost); + + Future loadProfileDetails() async => await _loadProfileDetails(); + + Future fetchProfilePosts() async => await _fetchProfilePosts(); + + Future fetchBlockedUsers() async => await _fetchBlockedUsers(); + + Future loadMoreBlockedUsers() async => await _fetchMoreBlockedUsers(); + + Future loadMorePosts() async => + await _loadMoreProfilePosts(page: _postData.value.currentPage! + 1); + + Future updateProfile(Map details, + {bool? showLoading}) async => + await _updateProfile(details, showLoading: showLoading); + + Future applyForBlueTick(Map details) async => + await _applyForBlueTick(details); + + Future _saveProfileDataToLocalStorage(ProfileResponse respone) async { var data = jsonEncode(respone.toJson()); await StorageService.write('profileData', data); AppUtility.log('Profile data saved to local storage'); } - static Future _readProfileDataFromLocalStorage() async { + Future _readProfileDataFromLocalStorage() async { var hasData = await StorageService.hasData('profileData'); if (hasData) { @@ -67,7 +110,7 @@ class ProfileController extends GetxController { } } - static Future _saveProfilePostsToLocalStorage(List posts) async { + Future _saveProfilePostsToLocalStorage(List posts) async { AppUtility.log('Saving profile posts to local storage'); if (posts.isEmpty) { @@ -77,7 +120,7 @@ class ProfileController extends GetxController { for (var item in posts) { await HiveService.put( - 'profilePosts', + HiveBoxNames.profilePosts, item.id, item, ); @@ -85,11 +128,29 @@ class ProfileController extends GetxController { AppUtility.log('Profile posts saved to local storage'); } - static Future?> _readProfilePostsFromLocalStorage() async { + Future _saveBlockedUsersToLocalStorage(List users) async { + AppUtility.log('Saving blocked users to local storage'); + + if (users.isEmpty) { + AppUtility.log('No users found to save'); + return; + } + + for (var item in users) { + await HiveService.put( + HiveBoxNames.blockedUsers, + item.id, + item, + ); + } + AppUtility.log('Blocked Users saved to local storage'); + } + + Future?> _readProfilePostsFromLocalStorage() async { AppUtility.log('Loading User Posts From Local Storage'); - var isExists = await HiveService.hasLength('profilePosts'); + var isExists = await HiveService.hasLength(HiveBoxNames.profilePosts); if (isExists) { - var data = await HiveService.getAll('profilePosts'); + var data = await HiveService.getAll(HiveBoxNames.profilePosts); data.sort((a, b) => b.createdAt!.compareTo(a.createdAt!)); AppUtility.log('User Posts From Local Storage Loaded'); return data; @@ -98,6 +159,19 @@ class ProfileController extends GetxController { return null; } + static Future?> _readPBlockedUsersFromLocalStorage() async { + AppUtility.log('Loading Blocked Users From Local Storage'); + var isExists = await HiveService.hasLength(HiveBoxNames.blockedUsers); + if (isExists) { + var data = await HiveService.getAll(HiveBoxNames.blockedUsers); + data.sort((a, b) => b.uname.compareTo(a.uname)); + AppUtility.log('Blocked Users From Local Storage Loaded'); + return data; + } + AppUtility.log('Blocked Users From Local Storage Loaded'); + return null; + } + Future _loadProfileDetails() async { AppUtility.log("Loading Profile Details"); @@ -107,18 +181,19 @@ class ProfileController extends GetxController { setProfileDetailsData = decodedData; final profilePosts = await _readProfilePostsFromLocalStorage(); + final blockedUsers = await _readPBlockedUsersFromLocalStorage(); if (profilePosts != null) { _postList.clear(); _postList.addAll(profilePosts); - // await _fetchPosts(); - return true; - } else { - AppUtility.log("Failed To Load Profile Posts From Local Storage", - tag: 'error'); - await _auth.deleteAllLocalDataAndCache(); - return false; } + + if (blockedUsers != null) { + _blockedUsers.clear(); + _blockedUsers.addAll(blockedUsers); + } + + return true; } else { AppUtility.log("Failed To Load Profile Details From Local Storage", tag: 'error'); @@ -138,6 +213,7 @@ class ProfileController extends GetxController { final decodedData = response.data; setProfileDetailsData = ProfileResponse.fromJson(decodedData); await _saveProfileDataToLocalStorage(_profileDetails.value); + await _fetchBlockedUsers(); _isLoading = false; update(); if (fetchPost) await _fetchProfilePosts(); @@ -385,26 +461,102 @@ class ProfileController extends GetxController { } } - Future followUnfollowUser(User user) async => - await _followUnfollowUser(user); + Future _fetchBlockedUsers() async { + _loadingBlockedUsers.value = true; + update(); - Future cancelFollowRequest(User user) async => - await _cancelFollowRequest(user); + try { + final response = await _apiProvider.getBlockedUsers(_auth.token); - Future fetchProfileDetails({bool fetchPost = true}) async => - await _fetchProfileDetails(fetchPost: fetchPost); + if (response.isSuccessful) { + final decodedData = response.data; + setBlockedUsersResponse = BlockedUsersResponse.fromJson(decodedData); + _blockedUsers.clear(); + _blockedUsers.addAll(_blockedUsersResponse.value.results!); + _loadingBlockedUsers.value = false; + update(); + await _saveBlockedUsersToLocalStorage( + _blockedUsersResponse.value.results!); + } else { + final decodedData = response.data; + _loadingBlockedUsers.value = false; + update(); + AppUtility.showSnackBar( + decodedData[StringValues.message], + StringValues.error, + ); + } + } catch (exc) { + _loadingBlockedUsers.value = false; + update(); + AppUtility.showSnackBar('Error: ${exc.toString()}', StringValues.error); + } + } - Future loadProfileDetails() async => await _loadProfileDetails(); + Future _fetchMoreBlockedUsers() async { + _loadingMoreBlockedUsers.value = true; + update(); - Future fetchProfilePosts() async => await _fetchProfilePosts(); + try { + final response = await _apiProvider.getBlockedUsers( + _auth.token, + page: _blockedUsersResponse.value.currentPage! + 1, + ); - Future loadMore() async => - await _loadMoreProfilePosts(page: _postData.value.currentPage! + 1); + if (response.isSuccessful) { + final decodedData = response.data; + setBlockedUsersResponse = BlockedUsersResponse.fromJson(decodedData); + _blockedUsers.addAll(_blockedUsersResponse.value.results!); + _loadingMoreBlockedUsers.value = false; + update(); + await _saveBlockedUsersToLocalStorage( + _blockedUsersResponse.value.results!); + } else { + final decodedData = response.data; + _loadingMoreBlockedUsers.value = false; + update(); + AppUtility.showSnackBar( + decodedData[StringValues.message], + StringValues.error, + ); + } + } catch (exc) { + _loadingMoreBlockedUsers.value = false; + update(); + AppUtility.showSnackBar('Error: ${exc.toString()}', StringValues.error); + } + } - Future updateProfile(Map details, - {bool? showLoading}) async => - await _updateProfile(details, showLoading: showLoading); + Future _unblockUser(String userId) async { + if (userId.isEmpty) { + AppUtility.showSnackBar( + StringValues.userIdNotFound, + StringValues.warning, + ); + return; + } - Future applyForBlueTick(Map details) async => - await _applyForBlueTick(details); + try { + final response = await _apiProvider.unblockUser(_auth.token, userId); + + if (response.isSuccessful) { + final decodedData = response.data; + _blockedUsers.removeWhere((element) => element.id == userId); + update(); + await HiveService.delete(HiveBoxNames.blockedUsers, userId); + AppUtility.showSnackBar( + decodedData[StringValues.message], + StringValues.success, + ); + } else { + final decodedData = response.data; + AppUtility.showSnackBar( + decodedData[StringValues.message], + StringValues.error, + ); + } + } catch (exc) { + AppUtility.showSnackBar('Error: $exc', StringValues.error); + } + } } diff --git a/lib/modules/home/views/trending_tabs/people_tab.dart b/lib/modules/home/views/trending_tabs/people_tab.dart index e4f850a..1eb7fc0 100644 --- a/lib/modules/home/views/trending_tabs/people_tab.dart +++ b/lib/modules/home/views/trending_tabs/people_tab.dart @@ -136,7 +136,8 @@ class PeopleTab extends StatelessWidget { user: item, totalLength: logic.recommendedUsersList.length, index: index, - onTap: () => RouteManagement.goToUserProfileView(item.id), + onTap: () => + RouteManagement.goToUserProfileDetailsViewByUserId(item.id), onActionTap: () { if (item.followingStatus == "requested") { logic.cancelFollowRequest(item); diff --git a/lib/modules/home/views/widgets/notification_widget.dart b/lib/modules/home/views/widgets/notification_widget.dart index b452c66..1334fb1 100644 --- a/lib/modules/home/views/widgets/notification_widget.dart +++ b/lib/modules/home/views/widgets/notification_widget.dart @@ -52,8 +52,8 @@ class NotificationWidget extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, children: [ GestureDetector( - onTap: () => - RouteManagement.goToUserProfileView(notification.from.id), + onTap: () => RouteManagement.goToUserProfileDetailsViewByUserId( + notification.from.id), child: AvatarWidget( avatar: notification.from.avatar, size: Dimens.twentyFour, diff --git a/lib/modules/home/views/widgets/post_widget.dart b/lib/modules/home/views/widgets/post_widget.dart index c4e7da8..e2cd788 100644 --- a/lib/modules/home/views/widgets/post_widget.dart +++ b/lib/modules/home/views/widgets/post_widget.dart @@ -50,7 +50,8 @@ class PostWidget extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, children: [ GestureDetector( - onTap: () => RouteManagement.goToUserProfileView(post.owner!.id), + onTap: () => RouteManagement.goToUserProfileDetailsViewByUserId( + post.owner!.id), child: AvatarWidget( avatar: avatar, size: Dimens.twenty, @@ -91,7 +92,8 @@ class PostWidget extends StatelessWidget { ), recognizer: TapGestureRecognizer() ..onTap = () => - RouteManagement.goToUserProfileView(post.owner!.id), + RouteManagement.goToUserProfileDetailsViewByUserId( + post.owner!.id), ), overflow: TextOverflow.ellipsis, ), @@ -522,6 +524,45 @@ class PostWidget extends StatelessWidget { ), ), + /// Block User + if (post.owner!.id != currentUser.id) + NxListTile( + bgColor: ColorValues.transparent, + padding: Dimens.edgeInsets12, + showBorder: false, + onTap: () { + AppUtility.closeBottomSheet(); + RouteManagement.goToBlockUserView( + post.owner!.id, + post.owner!.uname, + post.owner!.avatar!, + ); + }, + leading: Icon( + Icons.block, + color: Theme.of(context).textTheme.bodyLarge!.color, + size: Dimens.twentyFour, + ), + title: RichText( + text: TextSpan( + children: [ + TextSpan( + text: StringValues.block, + style: AppStyles.style16Normal.copyWith( + color: Theme.of(context).textTheme.bodyLarge!.color, + ), + ), + TextSpan( + text: ' ${post.owner!.uname}', + style: AppStyles.style16Bold.copyWith( + color: Theme.of(context).textTheme.bodyLarge!.color, + ), + ), + ], + ), + ), + ), + /// Report Post NxListTile( diff --git a/lib/modules/post/views/post_liked_users_view.dart b/lib/modules/post/views/post_liked_users_view.dart index eb1d721..3619e20 100644 --- a/lib/modules/post/views/post_liked_users_view.dart +++ b/lib/modules/post/views/post_liked_users_view.dart @@ -74,8 +74,8 @@ class PostLikedUsersView extends StatelessWidget { user: item!, totalLength: logic.postLikedUsersList.length, index: index, - onTap: () => - RouteManagement.goToUserProfileView(item.id), + onTap: () => RouteManagement + .goToUserProfileDetailsViewByUserId(item.id), onActionTap: () { if (item.followingStatus == "requested") { logic.cancelFollowRequest(item); diff --git a/lib/modules/post/views/widgets/comment_widget.dart b/lib/modules/post/views/widgets/comment_widget.dart index 1c93291..82591de 100644 --- a/lib/modules/post/views/widgets/comment_widget.dart +++ b/lib/modules/post/views/widgets/comment_widget.dart @@ -30,8 +30,8 @@ class CommentWidget extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, children: [ GestureDetector( - onTap: () => - RouteManagement.goToUserProfileView(comment.user!.id), + onTap: () => RouteManagement.goToUserProfileDetailsViewByUserId( + comment.user!.id), child: AvatarWidget( avatar: comment.user!.avatar, size: Dimens.twenty, @@ -70,8 +70,9 @@ class CommentWidget extends StatelessWidget { color: Theme.of(context).textTheme.bodyLarge!.color, ), recognizer: TapGestureRecognizer() - ..onTap = () => RouteManagement.goToUserProfileView( - comment.user!.id), + ..onTap = () => + RouteManagement.goToUserProfileDetailsViewByUserId( + comment.user!.id), ), overflow: TextOverflow.ellipsis, ), diff --git a/lib/modules/post/views/widgets/post_details_widget.dart b/lib/modules/post/views/widgets/post_details_widget.dart index eb73773..2e037bb 100644 --- a/lib/modules/post/views/widgets/post_details_widget.dart +++ b/lib/modules/post/views/widgets/post_details_widget.dart @@ -46,7 +46,8 @@ class PostDetailsWidget extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, children: [ GestureDetector( - onTap: () => RouteManagement.goToUserProfileView(post.owner!.id), + onTap: () => RouteManagement.goToUserProfileDetailsViewByUserId( + post.owner!.id), child: AvatarWidget( avatar: avatar, size: Dimens.twenty, @@ -87,7 +88,8 @@ class PostDetailsWidget extends StatelessWidget { ), recognizer: TapGestureRecognizer() ..onTap = () => - RouteManagement.goToUserProfileView(post.owner!.id), + RouteManagement.goToUserProfileDetailsViewByUserId( + post.owner!.id), ), overflow: TextOverflow.ellipsis, ), diff --git a/lib/modules/profile/views/profile_view.dart b/lib/modules/profile/views/profile_view.dart index cd3bcb0..3776297 100644 --- a/lib/modules/profile/views/profile_view.dart +++ b/lib/modules/profile/views/profile_view.dart @@ -523,7 +523,7 @@ class ProfileView extends StatelessWidget { loadingCondition: logic.isMorePostLoading, hasMoreCondition: logic.postData!.results != null && logic.postData!.hasNextPage!, - loadMore: logic.loadMore, + loadMore: logic.loadMorePosts, ), ], ), diff --git a/lib/modules/report/report_controller.dart b/lib/modules/report/report_controller.dart index f921a74..739f20e 100644 --- a/lib/modules/report/report_controller.dart +++ b/lib/modules/report/report_controller.dart @@ -64,7 +64,7 @@ class ReportController extends GetxController { update(); final body = { - 'reason': _reason.value, + 'reportReason': _reason.value, }; if (reportType == ReportType.user) { @@ -73,6 +73,8 @@ class ReportController extends GetxController { body['postId'] = id; } else if (reportType == ReportType.comment) { body['commentId'] = id; + } else if (reportType == ReportType.commentReply) { + body['commentReplyId'] = id; } try { @@ -93,6 +95,11 @@ class ReportController extends GetxController { _auth.token, body, ); + } else if (reportType == ReportType.commentReply) { + response = await _apiProvider.reportCommentReply( + _auth.token, + body, + ); } if (response == null) { diff --git a/lib/modules/settings/views/pages/privacy/blocked_users_view.dart b/lib/modules/settings/views/pages/privacy/blocked_users_view.dart new file mode 100644 index 0000000..693f293 --- /dev/null +++ b/lib/modules/settings/views/pages/privacy/blocked_users_view.dart @@ -0,0 +1,133 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:social_media_app/constants/dimens.dart'; +import 'package:social_media_app/constants/strings.dart'; +import 'package:social_media_app/constants/styles.dart'; +import 'package:social_media_app/global_widgets/avatar_widget.dart'; +import 'package:social_media_app/global_widgets/circular_progress_indicator.dart'; +import 'package:social_media_app/global_widgets/custom_app_bar.dart'; +import 'package:social_media_app/global_widgets/custom_list_tile.dart'; +import 'package:social_media_app/global_widgets/custom_refresh_indicator.dart'; +import 'package:social_media_app/global_widgets/load_more_widget.dart'; +import 'package:social_media_app/global_widgets/no_data_widget.dart'; +import 'package:social_media_app/global_widgets/primary_filled_btn.dart'; +import 'package:social_media_app/modules/home/controllers/profile_controller.dart'; + +class BlockedUsersView extends StatelessWidget { + const BlockedUsersView({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + child: SizedBox( + width: Dimens.screenWidth, + height: Dimens.screenHeight, + child: NxRefreshIndicator( + onRefresh: () => ProfileController.find.fetchBlockedUsers(), + showProgress: false, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + NxAppBar( + title: StringValues.blockedUsers, + padding: Dimens.edgeInsetsDefault, + ), + _buildBody(context), + ], + ), + ), + ), + ), + ); + } + + Widget _buildBody(BuildContext context) { + return Expanded( + child: Padding( + padding: Dimens.edgeInsetsHorizDefault, + child: GetBuilder( + builder: (logic) { + if (logic.loadingBlockedUsers) { + return const Center(child: NxCircularProgressIndicator()); + } + + return SingleChildScrollView( + physics: const BouncingScrollPhysics( + parent: AlwaysScrollableScrollPhysics(), + ), + child: logic.blockedUsers.isEmpty + ? _buildBlockedUsersEmptyText(context) + : _buildBlockedUsersList(logic, context), + ); + }, + ), + ), + ); + } + + Widget _buildBlockedUsersEmptyText(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + Dimens.heightedBox(Dimens.screenHeight * 0.2), + const NoDataWidget( + message: StringValues.noBlockedUsers, + ), + ], + ); + } + + Widget _buildBlockedUsersList(ProfileController logic, BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Dimens.boxHeight8, + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: logic.blockedUsers.length, + itemBuilder: (ctx, index) { + var user = logic.blockedUsers[index]; + return NxListTile( + padding: Dimens.edgeInsets12, + bgColor: Theme.of(context).cardColor, + borderRadius: BorderRadius.circular(Dimens.four), + leading: AvatarWidget( + avatar: user.avatar, + size: Dimens.twentyFour, + ), + title: Text( + user.uname, + style: AppStyles.style14Bold, + ), + subtitle: Text( + user.email, + style: AppStyles.style13Normal.copyWith( + color: Theme.of(context).textTheme.titleSmall!.color, + ), + ), + trailing: NxFilledButton( + label: StringValues.unblock, + labelStyle: AppStyles.style12Bold, + padding: Dimens.edgeInsets4_12, + onTap: () => logic.unblockUser(user.id), + ), + ); + }, + ), + LoadMoreWidget( + loadingCondition: logic.loadingMoreBlockedUsers, + hasMoreCondition: logic.blockedUsersResponse!.results != null && + logic.blockedUsersResponse!.hasNextPage!, + loadMore: logic.loadMoreBlockedUsers, + ), + ], + ); + } +} diff --git a/lib/modules/settings/views/pages/privacy/mute_block_privacy_settings_view.dart b/lib/modules/settings/views/pages/privacy/mute_block_privacy_settings_view.dart new file mode 100644 index 0000000..7cf4030 --- /dev/null +++ b/lib/modules/settings/views/pages/privacy/mute_block_privacy_settings_view.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:social_media_app/constants/dimens.dart'; +import 'package:social_media_app/constants/strings.dart'; +import 'package:social_media_app/constants/styles.dart'; +import 'package:social_media_app/global_widgets/custom_app_bar.dart'; +import 'package:social_media_app/global_widgets/custom_list_tile.dart'; +import 'package:social_media_app/routes/route_management.dart'; + +class MuteBlockPrivacySettingsView extends StatelessWidget { + const MuteBlockPrivacySettingsView({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + child: SizedBox( + width: Dimens.screenWidth, + height: Dimens.screenHeight, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + NxAppBar( + title: StringValues.muteAndBlock, + padding: Dimens.edgeInsetsDefault, + ), + _buildBody(context), + ], + ), + ), + ), + ); + } + + Widget _buildBody(BuildContext context) { + return Expanded( + child: Padding( + padding: Dimens.edgeInsetsHorizDefault, + child: SingleChildScrollView( + physics: const BouncingScrollPhysics( + parent: AlwaysScrollableScrollPhysics(), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Dimens.boxHeight8, + + /// Blocked Users + NxListTile( + padding: Dimens.edgeInsets12, + bgColor: Theme.of(context).cardColor, + borderRadius: BorderRadius.circular(Dimens.four), + title: Text( + StringValues.blockedUsers, + style: AppStyles.style14Bold, + ), + subtitle: Text( + StringValues.blockedUsersDesc, + style: AppStyles.style13Normal.copyWith( + color: Theme.of(context).textTheme.titleMedium!.color, + ), + ), + onTap: RouteManagement.goToBlockedUsersSettingsView, + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/modules/settings/views/privacy_settings_view.dart b/lib/modules/settings/views/privacy_settings_view.dart index 018f89a..73e3f5d 100644 --- a/lib/modules/settings/views/privacy_settings_view.dart +++ b/lib/modules/settings/views/privacy_settings_view.dart @@ -116,36 +116,37 @@ class PrivacySettingsView extends StatelessWidget { Dimens.boxHeight8, - /// Posts + /// Mute and Block NxListTile( padding: Dimens.edgeInsets12, bgColor: Theme.of(context).cardColor, borderRadius: BorderRadius.circular(Dimens.four), title: Text( - StringValues.postPrivacy, + StringValues.muteAndBlock, style: AppStyles.style14Bold, ), subtitle: Text( - StringValues.postPrivacyDesc, + StringValues.muteAndBlockDesc, style: AppStyles.style13Normal.copyWith( color: Theme.of(context).textTheme.titleMedium!.color, ), ), + onTap: RouteManagement.goToMuteAndBlockSettingsView, ), Dimens.boxHeight8, - /// Comments + /// Posts NxListTile( padding: Dimens.edgeInsets12, bgColor: Theme.of(context).cardColor, borderRadius: BorderRadius.circular(Dimens.four), title: Text( - StringValues.commentPrivacy, + StringValues.postPrivacy, style: AppStyles.style14Bold, ), subtitle: Text( - StringValues.commentPrivacyDesc, + StringValues.postPrivacyDesc, style: AppStyles.style13Normal.copyWith( color: Theme.of(context).textTheme.titleMedium!.color, ), @@ -154,13 +155,13 @@ class PrivacySettingsView extends StatelessWidget { Dimens.boxHeight8, - /// Messages + /// Comments NxListTile( padding: Dimens.edgeInsets12, bgColor: Theme.of(context).cardColor, borderRadius: BorderRadius.circular(Dimens.four), title: Text( - StringValues.messagePrivacy, + StringValues.commentPrivacy, style: AppStyles.style14Bold, ), subtitle: Text( @@ -171,24 +172,43 @@ class PrivacySettingsView extends StatelessWidget { ), ), - Dimens.boxHeight8, + // Dimens.boxHeight8, - /// Moments - NxListTile( - padding: Dimens.edgeInsets12, - bgColor: Theme.of(context).cardColor, - borderRadius: BorderRadius.circular(Dimens.four), - title: Text( - StringValues.storyPrivacy, - style: AppStyles.style14Bold, - ), - subtitle: Text( - StringValues.storyPrivacyDesc, - style: AppStyles.style13Normal.copyWith( - color: Theme.of(context).textTheme.titleMedium!.color, - ), - ), - ), + // /// Messages + // NxListTile( + // padding: Dimens.edgeInsets12, + // bgColor: Theme.of(context).cardColor, + // borderRadius: BorderRadius.circular(Dimens.four), + // title: Text( + // StringValues.messagePrivacy, + // style: AppStyles.style14Bold, + // ), + // subtitle: Text( + // StringValues.commentPrivacyDesc, + // style: AppStyles.style13Normal.copyWith( + // color: Theme.of(context).textTheme.titleMedium!.color, + // ), + // ), + // ), + + // Dimens.boxHeight8, + + // /// Moments + // NxListTile( + // padding: Dimens.edgeInsets12, + // bgColor: Theme.of(context).cardColor, + // borderRadius: BorderRadius.circular(Dimens.four), + // title: Text( + // StringValues.storyPrivacy, + // style: AppStyles.style14Bold, + // ), + // subtitle: Text( + // StringValues.storyPrivacyDesc, + // style: AppStyles.style13Normal.copyWith( + // color: Theme.of(context).textTheme.titleMedium!.color, + // ), + // ), + // ), Dimens.boxHeight16, ], diff --git a/lib/modules/user/user_profile_view.dart b/lib/modules/user/user_profile_view.dart index da689fe..fd40133 100644 --- a/lib/modules/user/user_profile_view.dart +++ b/lib/modules/user/user_profile_view.dart @@ -4,6 +4,7 @@ import 'package:intl/intl.dart'; import 'package:social_media_app/apis/models/entities/user.dart'; import 'package:social_media_app/constants/colors.dart'; import 'package:social_media_app/constants/dimens.dart'; +import 'package:social_media_app/constants/enums.dart'; import 'package:social_media_app/constants/strings.dart'; import 'package:social_media_app/constants/styles.dart'; import 'package:social_media_app/extensions/string_extensions.dart'; @@ -11,6 +12,7 @@ import 'package:social_media_app/global_widgets/avatar_widget.dart'; import 'package:social_media_app/global_widgets/circular_progress_indicator.dart'; import 'package:social_media_app/global_widgets/count_widget.dart'; import 'package:social_media_app/global_widgets/custom_app_bar.dart'; +import 'package:social_media_app/global_widgets/custom_list_tile.dart'; import 'package:social_media_app/global_widgets/custom_refresh_indicator.dart'; import 'package:social_media_app/global_widgets/expandable_text_widget.dart'; import 'package:social_media_app/global_widgets/image_viewer_widget.dart'; @@ -19,6 +21,7 @@ import 'package:social_media_app/global_widgets/post_thumb_widget.dart'; import 'package:social_media_app/global_widgets/primary_filled_btn.dart'; import 'package:social_media_app/global_widgets/primary_outlined_btn.dart'; import 'package:social_media_app/global_widgets/verified_widget.dart'; +import 'package:social_media_app/modules/home/controllers/profile_controller.dart'; import 'package:social_media_app/modules/user/user_details_controller.dart'; import 'package:social_media_app/routes/route_management.dart'; import 'package:social_media_app/utils/utility.dart'; @@ -33,28 +36,128 @@ class UserProfileView extends StatelessWidget { child: SizedBox( width: Dimens.screenWidth, height: Dimens.screenHeight, - child: GetBuilder(builder: (logic) { - return NxRefreshIndicator( - onRefresh: logic.fetchUserDetailsById, - showProgress: false, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - _buildProfileHeader(logic, context), - _buildProfileBody(logic, context), - ], + child: GetBuilder( + builder: (logic) { + if (logic.isLoading) { + return const Center(child: NxCircularProgressIndicator()); + } + + return NxRefreshIndicator( + onRefresh: logic.fetchUserDetailsById, + showProgress: false, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _buildProfileHeader(logic, context), + _buildProfileBody(logic, context), + ], + ), + ); + }, + ), + ), + ), + ); + } + + void _showHeaderOptionBottomSheet( + BuildContext context, UserDetailsController logic) { + final currentUser = ProfileController.find.profileDetails!.user!; + AppUtility.showBottomSheet( + children: [ + /// Block User + + if (logic.userDetails!.user!.id != currentUser.id && + !logic.userDetails!.user!.isBlockedByYou && + !logic.userDetails!.user!.isBlockedByUser) + NxListTile( + bgColor: ColorValues.transparent, + padding: Dimens.edgeInsets12, + showBorder: false, + onTap: () { + AppUtility.closeBottomSheet(); + RouteManagement.goToBlockUserView( + logic.userDetails!.user!.id, + logic.userDetails!.user!.uname, + logic.userDetails!.user!.avatar!, + ); + }, + leading: Icon( + Icons.block, + color: Theme.of(context).textTheme.bodyLarge!.color, + size: Dimens.twentyFour, + ), + title: Text( + StringValues.block, + style: AppStyles.style16Normal.copyWith( + color: Theme.of(context).textTheme.bodyLarge!.color, + ), + ), + ), + + /// Report User + + if (logic.userDetails!.user!.id != currentUser.id && + !logic.userDetails!.user!.isBlockedByYou && + !logic.userDetails!.user!.isBlockedByUser) + NxListTile( + bgColor: ColorValues.transparent, + padding: Dimens.edgeInsets12, + showBorder: false, + onTap: () { + AppUtility.closeBottomSheet(); + RouteManagement.goToReportIssueView( + logic.userDetails!.user!.id, + ReportType.user, + ); + }, + leading: Icon( + Icons.report, + color: Theme.of(context).textTheme.bodyLarge!.color, + size: Dimens.twentyFour, + ), + title: Text( + StringValues.report, + style: AppStyles.style16Normal.copyWith( + color: Theme.of(context).textTheme.bodyLarge!.color, ), + ), + ), + + /// Share User Profile + + NxListTile( + bgColor: ColorValues.transparent, + padding: Dimens.edgeInsets12, + showBorder: false, + onTap: () { + AppUtility.closeBottomSheet(); + AppUtility.showShareDialog( + context, + '${StringValues.websiteUrl}/user/${logic.userDetails!.user!.id}', ); - }), + }, + leading: Icon( + Icons.share, + color: Theme.of(context).textTheme.bodyLarge!.color, + size: Dimens.twentyFour, + ), + title: Text( + StringValues.share, + style: AppStyles.style16Normal.copyWith( + color: Theme.of(context).textTheme.bodyLarge!.color, + ), + ), ), - ), + ], ); } Widget _buildProfileHeader( UserDetailsController logic, BuildContext context) { - final user = logic.userDetails!.user; + final user = logic.userDetails!.user!; + final currentUser = ProfileController.find.profileDetails!.user!; return NxAppBar( padding: Dimens.edgeInsetsDefault, child: Expanded( @@ -62,42 +165,44 @@ class UserProfileView extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - user != null ? user.uname : StringValues.profile, + user.uname, style: AppStyles.style20Bold, ), const Spacer(), - GestureDetector( - child: Container( - padding: Dimens.edgeInsets6, - decoration: BoxDecoration( - color: Theme.of(context).cardColor, - shape: BoxShape.circle, - border: Border.all( - color: Theme.of(context).dividerColor, - ), - ), - child: Icon( - Icons.more_vert, - size: Dimens.twenty, - color: Theme.of(context).textTheme.bodyLarge!.color, - ), + if (user.id != currentUser.id && + !user.isBlockedByYou && + !user.isBlockedByUser) + GestureDetector( + onTap: () => _showHeaderOptionBottomSheet(context, logic), + child: _buildMoreIconBtn(context), ), - ), ], ), ), ); } - Widget _buildProfileBody(UserDetailsController logic, BuildContext context) { - if (logic.isLoading) { - return const Expanded( - child: Center(child: NxCircularProgressIndicator()), - ); - } + Container _buildMoreIconBtn(BuildContext context) { + return Container( + padding: Dimens.edgeInsets6, + decoration: BoxDecoration( + color: Theme.of(context).cardColor, + shape: BoxShape.circle, + border: Border.all( + color: Theme.of(context).dividerColor, + ), + ), + child: Icon( + Icons.more_vert, + size: Dimens.twenty, + color: Theme.of(context).textTheme.bodyLarge!.color, + ), + ); + } + Widget _buildProfileBody(UserDetailsController logic, BuildContext context) { /// If user not found - else if (logic.userDetails == null || logic.userDetails!.user == null) { + if (logic.userDetails == null || logic.userDetails!.user == null) { return Expanded( child: Center( child: Padding( diff --git a/lib/routes/app_pages.dart b/lib/routes/app_pages.dart index f3ec3c8..5c23779 100644 --- a/lib/routes/app_pages.dart +++ b/lib/routes/app_pages.dart @@ -10,6 +10,8 @@ import 'package:social_media_app/modules/auth/views/login_view.dart'; import 'package:social_media_app/modules/auth/views/reactivate_account_view.dart'; import 'package:social_media_app/modules/auth/views/register_view.dart'; import 'package:social_media_app/modules/auth/views/reset_password_view.dart'; +import 'package:social_media_app/modules/block_user/block_user_binding.dart'; +import 'package:social_media_app/modules/block_user/block_user_view.dart'; import 'package:social_media_app/modules/chat/bindings/single_chat_binding.dart'; import 'package:social_media_app/modules/chat/views/p2p_chat_settings_view.dart'; import 'package:social_media_app/modules/chat/views/p2p_chat_view.dart'; @@ -72,6 +74,8 @@ import 'package:social_media_app/modules/settings/views/pages/account/verified_a import 'package:social_media_app/modules/settings/views/pages/help/report_app_issue_view.dart'; import 'package:social_media_app/modules/settings/views/pages/help/send_suggestions_view.dart'; import 'package:social_media_app/modules/settings/views/pages/privacy/account_privacy_view.dart'; +import 'package:social_media_app/modules/settings/views/pages/privacy/blocked_users_view.dart'; +import 'package:social_media_app/modules/settings/views/pages/privacy/mute_block_privacy_settings_view.dart'; import 'package:social_media_app/modules/settings/views/pages/privacy/online_status_view.dart'; import 'package:social_media_app/modules/settings/views/pages/security/change_password_view.dart'; import 'package:social_media_app/modules/settings/views/pages/security/login_info_history_view.dart'; @@ -230,6 +234,15 @@ abstract class AppPages { transition: defaultTransition, ), + /// Block User + GetPage( + name: _Routes.blockUser, + page: BlockUserView.new, + binding: BlockUserBinding(), + transitionDuration: transitionDuration, + transition: defaultTransition, + ), + /// ------------------------------------------------------------------------ /// Edit Profile Pages ----------------------------------------------------- @@ -550,6 +563,20 @@ abstract class AppPages { transition: defaultTransition, ), + GetPage( + name: _Routes.muteBlockPrivacySettings, + page: MuteBlockPrivacySettingsView.new, + transitionDuration: transitionDuration, + transition: defaultTransition, + ), + + GetPage( + name: _Routes.blockedUsersSettings, + page: BlockedUsersView.new, + transitionDuration: transitionDuration, + transition: defaultTransition, + ), + /// ------------------------------------------------------------------------ /// App Update ------------------------------------------------------------- diff --git a/lib/routes/app_routes.dart b/lib/routes/app_routes.dart index 545fe2e..a3a6839 100644 --- a/lib/routes/app_routes.dart +++ b/lib/routes/app_routes.dart @@ -12,7 +12,7 @@ abstract class AppRoutes { static const blueTickVerification = _Routes.blueTickVerification; static const blueTickVerificationSettings = _Routes.blueTickVerificationSettings; - + static const blockUser = _Routes.blockUser; static const changeEmailSettings = _Routes.changeEmailSettings; static const changePassword = _Routes.changePassword; static const changePhoneSettings = _Routes.changePhoneSettings; @@ -45,6 +45,7 @@ abstract class AppRoutes { static const loginActivitySettings = _Routes.loginActivitySettings; static const maintenance = _Routes.maintenance; static const momentsSettings = _Routes.momentsSettings; + static const muteBlockPrivacySettings = _Routes.muteBlockPrivacySettings; static const noNetwork = _Routes.noNetwork; static const offline = _Routes.offline; static const onlineStatusSettings = _Routes.onlineStatusSettings; @@ -90,7 +91,7 @@ abstract class _Routes { static const blueTickVerification = "/blue_tick_verification"; static const blueTickVerificationSettings = "/blue_tick_verification_settings"; - + static const blockUser = '/block_user'; static const changeEmailSettings = '/change_email_settings'; static const changePassword = '/change_password'; static const changePhoneSettings = '/change_phone_settings'; @@ -123,6 +124,7 @@ abstract class _Routes { static const loginActivitySettings = '/login_activity_settings'; static const maintenance = '/maintenance'; static const momentsSettings = '/moments_settings'; + static const muteBlockPrivacySettings = '/mute_block_privacy_settings'; static const noNetwork = '/no_network'; static const offline = '/offline'; static const onlineStatusSettings = '/online_status_settings'; diff --git a/lib/routes/route_management.dart b/lib/routes/route_management.dart index e3c8b6e..92c03f0 100644 --- a/lib/routes/route_management.dart +++ b/lib/routes/route_management.dart @@ -1,13 +1,16 @@ import 'package:flutter/foundation.dart'; import 'package:get/get.dart'; +import 'package:social_media_app/apis/models/entities/media_file.dart'; import 'package:social_media_app/apis/models/entities/post.dart'; import 'package:social_media_app/apis/models/entities/user.dart'; import 'package:social_media_app/constants/enums.dart'; +import 'package:social_media_app/modules/block_user/block_user_controller.dart'; import 'package:social_media_app/modules/chat/controllers/p2p_chat_controller.dart'; import 'package:social_media_app/modules/follower/controllers/followers_list_controller.dart'; import 'package:social_media_app/modules/follower/controllers/following_list_controller.dart'; import 'package:social_media_app/modules/post/controllers/comment_controller.dart'; import 'package:social_media_app/modules/post/controllers/post_details_controller.dart'; +import 'package:social_media_app/modules/report/report_controller.dart'; import 'package:social_media_app/modules/user/user_details_controller.dart'; import 'package:social_media_app/routes/app_pages.dart'; @@ -37,18 +40,36 @@ abstract class RouteManagement { Get.offAllNamed(AppRoutes.home); } + /// User --------------------------------------------------------------------- + + static void goToBlockUserView(String id, String uname, MediaFile avatar) { + Get.delete(); + Get.toNamed(AppRoutes.blockUser, arguments: { + 'id': id, + 'uname': uname, + 'avatar': avatar, + }); + } + + static void goToUserProfileDetailsViewByUserId(String userId) { + Get.delete(); + Get.toNamed(AppRoutes.userProfile, arguments: [userId, 'uid']); + } + + static void goToUserProfileDetailsViewByUsername(String username) { + Get.delete(); + Get.toNamed(AppRoutes.userProfile, arguments: [username, 'uname']); + } + + /// -------------------------------------------------------------------------- + /// Auth --------------------------------------------------------------------- static void goToLoginView() { Get.toNamed(AppRoutes.login); } - // static void _goToRegisterView() { - - // } - static void goToRegisterView() { - // goToSendOtpToEmailView(callback: _goToRegisterView); Get.toNamed(AppRoutes.register); } @@ -191,16 +212,6 @@ abstract class RouteManagement { Get.toNamed(AppRoutes.following, arguments: userId); } - static void goToUserProfileView(String userId) { - Get.delete(); - Get.toNamed(AppRoutes.userProfile, arguments: [userId, 'uid']); - } - - static void goToUserProfileViewByUsername(String username) { - Get.delete(); - Get.toNamed(AppRoutes.userProfile, arguments: [username, 'uname']); - } - static void goToPostDetailsView(String postId, Post? post) { Get.delete(); Get.delete(); @@ -295,6 +306,14 @@ abstract class RouteManagement { Get.toNamed(AppRoutes.onlineStatusSettings); } + static void goToMuteAndBlockSettingsView() { + Get.toNamed(AppRoutes.muteBlockPrivacySettings); + } + + static void goToBlockedUsersSettingsView() { + Get.toNamed(AppRoutes.blockedUsersSettings); + } + /// -------------------------------------------------------------------------- /// Go to App Update View ---------------------------------------------------- @@ -316,6 +335,7 @@ abstract class RouteManagement { /// Report Issue ------------------------------------------------------------- static void goToReportIssueView(String id, ReportType type) { + Get.delete(); Get.toNamed(AppRoutes.reportIssue, arguments: {'id': id, 'reportType': type}); }