diff --git a/ios/Podfile b/ios/Podfile index 18d9d50..c62ad49 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '12.0' +platform :ios, '13.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index ad1c36e..a0e778e 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -144,6 +144,7 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 87240C3E9927238D49A20AD9 /* [CP] Embed Pods Frameworks */, + 47EF1AD016902339FB844E27 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -219,6 +220,23 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; + 47EF1AD016902339FB844E27 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; 87240C3E9927238D49A20AD9 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; diff --git a/lib/core/route/app_route.dart b/lib/core/route/app_route.dart index ccafe6a..6d7dc16 100644 --- a/lib/core/route/app_route.dart +++ b/lib/core/route/app_route.dart @@ -107,11 +107,14 @@ class AppRoute { static const createCustomPostRoute = 'createCustomPost'; static const chat = 'chat'; - static const chatRoute = 'chatRoute/:channelId'; + static const chatRoute = 'chatRoute/:channelId/:channelName'; static const channelProfile = 'channelProfile'; static const channelProfileRoute = '/channelProfile/:channelId'; + static const subChannelProfile = 'subChannelProfile'; + static const subChannelProfileRoute = '/subChannelProfile/:subChannelId'; + static const channelList = 'channelList'; static const channelListRoute = '/channelList'; diff --git a/lib/core/route/app_router.dart b/lib/core/route/app_router.dart index 4ae9411..8f1a9d4 100644 --- a/lib/core/route/app_router.dart +++ b/lib/core/route/app_router.dart @@ -56,6 +56,7 @@ import 'package:flutter_social_sample_app/presentation/screen/stories_by_targets import 'package:flutter_social_sample_app/presentation/screen/story_details/story_details_screens.dart'; import 'package:flutter_social_sample_app/presentation/screen/story_targets_by_targets%20/targets_by_targets_screens.dart'; import 'package:flutter_social_sample_app/presentation/screen/stream_list/stream_list_screen.dart'; +import 'package:flutter_social_sample_app/presentation/screen/sub_channel/get_sub_channel.dart'; import 'package:flutter_social_sample_app/presentation/screen/token_exchange/token_exchange_screen.dart'; import 'package:flutter_social_sample_app/presentation/screen/user_blocked_list/user_blocked_list_screen.dart'; import 'package:flutter_social_sample_app/presentation/screen/user_feed/user_feed_screen.dart'; @@ -329,8 +330,11 @@ class AppRouter { GoRoute( name: AppRoute.chat, path: AppRoute.chatRoute, - builder: (context, state) => - ChatScreen(channelId: state.params['channelId']!), + builder: (context, state) { + print("ChannelId: ${state.params['channelId']} - ChannelName: ${state.params['channelName']}"); + return ChatScreen(channelId: state.params['channelId']! , channelName: state.params['channelName']!,); + } + , ), ], ), @@ -351,6 +355,15 @@ class AppRouter { channelId: state.params['channelId']!, ), ), + + + GoRoute( + name: AppRoute.subChannelProfile, + path: AppRoute.subChannelProfileRoute, + builder: (context, state) => GetSubChannelScreen( + subChannelId: state.params['subChannelId']!, + ), + ), GoRoute( name: AppRoute.channelList, path: AppRoute.channelListRoute, diff --git a/lib/core/widget/message_widget.dart b/lib/core/widget/message_widget.dart index eea94f4..6b68fc7 100644 --- a/lib/core/widget/message_widget.dart +++ b/lib/core/widget/message_widget.dart @@ -1,9 +1,7 @@ import 'dart:async'; import 'dart:developer'; import 'dart:io'; - import 'package:amity_sdk/amity_sdk.dart'; -import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_social_sample_app/core/route/app_route.dart'; import 'package:flutter_social_sample_app/core/utils/download_service.dart'; @@ -12,6 +10,7 @@ import 'package:flutter_social_sample_app/core/widget/dialog/edit_text_dialog.da import 'package:flutter_social_sample_app/core/widget/dialog/positive_dialog.dart'; import 'package:flutter_social_sample_app/core/widget/dialog/progress_dialog_widget.dart'; import 'package:flutter_social_sample_app/core/widget/dynamic_text_highlighting.dart'; +import 'package:get/utils.dart'; import 'package:go_router/go_router.dart'; class MessageWidget extends StatelessWidget { @@ -73,10 +72,10 @@ class MessageWidget extends StatelessWidget { AmityChatClient.newMessageRepository().getMessage(message.messageId!).then((value) { completer.complete(); - PositiveDialog.show(context, title: 'Message View', message: value.toString().replaceAll(',', ', \n\n')); + (context.mounted) ? PositiveDialog.show(context, title: 'Message View', message: value.toString().replaceAll(',', ', \n\n')) : null; }).onError((error, stackTrace) { completer.completeError(error!); - CommonSnackbar.showNagativeSnackbar(context, 'Error', error.toString()); + (context.mounted) ? CommonSnackbar.showNagativeSnackbar(context, 'Error', error.toString()) : null; }); }, onLongPress: () { @@ -132,10 +131,8 @@ class MessageWidget extends StatelessWidget { Row( children: [ Text( - value.createdAt?.toLocal().toIso8601String() ?? DateTime.now().toLocal().toIso8601String(), style: themeData.textTheme.bodySmall!.copyWith(), - ), const SizedBox(width: 12), Text( @@ -187,24 +184,22 @@ class MessageWidget extends StatelessWidget { onTap: () { final completer = Completer(); ProgressDialog.showCompleter(context, completer); + print("Flagged by me - ${message.isFlaggedByMe}"); if (message.isFlaggedByMe) { message.unflag().then((value) { completer.complete(); - CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Unflagged'); + (context.mounted) ? CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Unflagged') : null; }).onError((error, stackTrace) { completer.completeError(error!, stackTrace); - - CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Unflag Error - ${error}'); + (context.mounted) ? CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Unflag Error - $error') : null; }); } else { message.flag().then((value) { completer.complete(); - CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Flagged'); + (context.mounted) ? CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Flagged') : null; }).onError((error, stackTrace) { completer.completeError(error!, stackTrace); - - CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Flag Error - ${error}'); - + (context.mounted) ? CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Flag Error - $error') : null; }); } }, @@ -213,10 +208,7 @@ class MessageWidget extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 6), decoration: BoxDecoration(border: Border.all(color: Colors.grey, width: 1)), child: Row( - children: [ - Icon(value.isFlaggedByMe ? Icons.flag_rounded : Icons.flag_outlined), - if ((value.flagCount ?? 0) > 0) Text('${value.flagCount}') - ], + children: [Icon(value.isFlaggedByMe ? Icons.flag_rounded : Icons.flag_outlined), if ((value.flagCount ?? 0) > 0) Text('${value.flagCount}')], ), ), ), @@ -252,6 +244,7 @@ class MessageWidget extends StatelessWidget { value: 6, child: Text('Tags'), ), + ]; }, onSelected: (value) { @@ -262,17 +255,14 @@ class MessageWidget extends StatelessWidget { case 1: if (message.data is MessageTextData) { /// Update Message - GoRouter.of(context) - .pushNamed(AppRoute.updateMessage, queryParams: {'messageId': message.messageId!}); + GoRouter.of(context).pushNamed(AppRoute.updateMessage, queryParams: {'messageId': message.messageId!}); } break; case 2: message.delete().then((value) { - CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Deleted'); + (context.mounted) ? CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Deleted') : null; }).onError((error, stackTrace) { - - CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Delete Error - ${error}'); - + (context.mounted) ? CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Delete Error - $error') : null; }); /// Delete Message @@ -280,40 +270,32 @@ class MessageWidget extends StatelessWidget { case 3: if (message.isFlaggedByMe) { message.unflag().then((value) { - CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Unflagged'); + (context.mounted) ? CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Unflagged') : null; }).onError((error, stackTrace) { - - CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Unflag Error - ${error}'); - + (context.mounted) ? CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Unflag Error - $error') : null; }); } else { message.flag().then((value) { - CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Flagged'); + (context.mounted) ? CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Flagged') : null; }).onError((error, stackTrace) { - - CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Flag Error - ${error}'); - + (context.mounted) ? CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Flag Error - $error') : null; }); } break; case 4: UserRepository().report(message.userId!).flag().then((value) { - CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Flagged User'); + (context.mounted) ? CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Flagged User') : null; }).onError((error, stackTrace) { - - CommonSnackbar.showPositiveSnackbar(context, 'Message', 'flagged Error - ${error}'); - + (context.mounted) ? CommonSnackbar.showPositiveSnackbar(context, 'Message', 'flagged Error - $error') : null; }); /// Delete Message break; case 5: UserRepository().report(message.userId!).unflag().then((value) { - CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Unflagged User'); + (context.mounted) ? CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Unflagged User') : null; }).onError((error, stackTrace) { - - CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Unflagged Error - ${error}'); - + (context.mounted) ? CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Unflagged Error - $error') : null; }); /// Delete Message @@ -327,12 +309,16 @@ class MessageWidget extends StatelessWidget { onPress: (value) { if (value.isNotEmpty) { message.upate().tags(value.split(',')).update().then((value) { - CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Tag Updated'); + (context.mounted) ? CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Tag Updated') : null; + }).onError((error, stackTrace) { + (context.mounted) ? CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Tag message Error - $error') : null; + }); + }else { + message.upate().tags([]).update().then((value) { + (context.mounted) ? CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Tag Updated') : null; }).onError((error, stackTrace) { - CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Tag message Error - $error'); + (context.mounted) ? CommonSnackbar.showPositiveSnackbar(context, 'Message', 'Tag message Error - $error') : null; }); - } else { - CommonSnackbar.showNagativeSnackbar(context, 'Tags', 'Please enter tags'); } }, ); @@ -350,202 +336,186 @@ class MessageWidget extends StatelessWidget { Positioned.fill( child: Align( alignment: Alignment.bottomRight, - child: Container( - // color: Colors.red, - child: InkWell( - onLongPress: () { - GoRouter.of(context) - .pushNamed(AppRoute.messageReaction, params: {'messageId': message.messageId!}); - }, - child: Stack( - // mainAxisSize: MainAxisSize.min, - children: [ - if (value.reactions!.getCount('like') > 0) - Container( - // width: 36, - // height: 36, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Colors.blue.shade100, - boxShadow: const [ - BoxShadow( - color: Colors.black12, - blurRadius: 1, - spreadRadius: 1, + child: InkWell( + onLongPress: () { + GoRouter.of(context).pushNamed(AppRoute.messageReaction, params: {'messageId': message.messageId!}); + }, + child: Stack( + // mainAxisSize: MainAxisSize.min, + children: [ + if (value.reactions!.getCount('like') > 0) + Container( + // width: 36, + // height: 36, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.blue.shade100, + boxShadow: const [ + BoxShadow( + color: Colors.black12, + blurRadius: 1, + spreadRadius: 1, + ), + ], + ), + child: IconButton( + onPressed: () { + message.react().removeReaction('like').then((value) { + (context.mounted) ? CommonSnackbar.showPositiveSnackbar(context, 'Success', 'Reaction Removed Successfully') : null; + }).onError( + (error, stackTrace) { + (context.mounted) ? CommonSnackbar.showNagativeSnackbar(context, 'Fail', 'Reaction Removed Failed ${error.toString()}') : null; + }, + ); + }, + icon: Row( + children: [ + Container( + margin: const EdgeInsets.only(top: 8), + child: Text( + value.reactions!.getCount('like').toString(), + style: themeData.textTheme.bodySmall!.copyWith(fontSize: 14), + ), + ), + const SizedBox(width: 2), + Image.asset( + 'assets/ic_liked.png', + height: 20, + width: 20, ), ], ), - child: IconButton( - onPressed: () { - message.react().removeReaction('like').then((value) { - CommonSnackbar.showPositiveSnackbar( - context, 'Success', 'Reaction Removed Successfully'); - }).onError( - (error, stackTrace) { - CommonSnackbar.showNagativeSnackbar( - context, 'Fail', 'Reaction Removed Failed ${error.toString()}'); - }, - ); - }, - icon: Row( - children: [ - Container( - margin: const EdgeInsets.only(top: 8), - child: Text( - - value.reactions!.getCount('like').toString(), - style: themeData.textTheme.bodySmall!.copyWith(fontSize: 14), - - ), - ), - const SizedBox(width: 2), - Image.asset( - 'assets/ic_liked.png', - height: 20, - width: 20, - ), - ], + ), + ), + if (value.reactions!.getCount('Like') > 0) + Container( + // width: 36, + // height: 36, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.blue.shade100, + boxShadow: const [ + BoxShadow( + color: Colors.black12, + blurRadius: 1, + spreadRadius: 1, ), - ), + ], ), - if (value.reactions!.getCount('Like') > 0) - Container( - // width: 36, - // height: 36, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Colors.blue.shade100, - boxShadow: const [ - BoxShadow( - color: Colors.black12, - blurRadius: 1, - spreadRadius: 1, + child: IconButton( + onPressed: () { + message.react().removeReaction('like').then((value) { + (context.mounted) ? CommonSnackbar.showPositiveSnackbar(context, 'Success', 'Reaction Removed Successfully') : null; + }).onError( + (error, stackTrace) { + (context.mounted) ? CommonSnackbar.showNagativeSnackbar(context, 'Fail', 'Reaction Removed Failed ${error.toString()}') : null; + }, + ); + }, + icon: Row( + children: [ + Container( + margin: const EdgeInsets.only(top: 8), + child: Text( + value.reactions!.getCount('like').toString(), + style: themeData.textTheme.bodySmall!.copyWith(fontSize: 14), + ), + ), + const SizedBox(width: 2), + Image.asset( + 'assets/ic_liked.png', + height: 20, + width: 20, ), ], ), - child: IconButton( - onPressed: () { - message.react().removeReaction('like').then((value) { - CommonSnackbar.showPositiveSnackbar( - context, 'Success', 'Reaction Removed Successfully'); - }).onError( - (error, stackTrace) { - CommonSnackbar.showNagativeSnackbar( - context, 'Fail', 'Reaction Removed Failed ${error.toString()}'); - }, - ); - }, - icon: Row( - children: [ - Container( - margin: const EdgeInsets.only(top: 8), - child: Text( - - value.reactions!.getCount('like').toString(), - style: themeData.textTheme.bodySmall!.copyWith(fontSize: 14), - - ), - ), - const SizedBox(width: 2), - Image.asset( - 'assets/ic_liked.png', - height: 20, - width: 20, - ), - ], + ), + ), + if (value.reactions!.getCount('love') > 0) + Container( + // width: 36, + // height: 36, + margin: const EdgeInsets.only(left: 30), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.blue.shade100, + boxShadow: const [ + BoxShadow( + color: Colors.black12, + blurRadius: 1, + spreadRadius: 1, ), - ), + ], ), - if (value.reactions!.getCount('love') > 0) - Container( - // width: 36, - // height: 36, - margin: const EdgeInsets.only(left: 30), - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Colors.blue.shade100, - boxShadow: const [ - BoxShadow( - color: Colors.black12, - blurRadius: 1, - spreadRadius: 1, + child: IconButton( + onPressed: () { + message.react().removeReaction('love').then((value) { + (context.mounted) ? CommonSnackbar.showPositiveSnackbar(context, 'Success', 'Reaction Removed Successfully') : null; + }).onError( + (error, stackTrace) { + (context.mounted) ? CommonSnackbar.showNagativeSnackbar(context, 'Fail', 'Reaction Removed Failed ${error.toString()}') : null; + }, + ); + }, + icon: Row( + children: [ + Text( + value.reactions!.getCount('love').toString(), + style: themeData.textTheme.bodySmall!.copyWith(fontSize: 14), + ), + const SizedBox(width: 2), + Image.asset( + 'assets/ic_heart.png', + height: 20, + width: 20, ), ], ), - child: IconButton( - onPressed: () { - message.react().removeReaction('love').then((value) { - CommonSnackbar.showPositiveSnackbar( - context, 'Success', 'Reaction Removed Successfully'); - }).onError( - (error, stackTrace) { - CommonSnackbar.showNagativeSnackbar( - context, 'Fail', 'Reaction Removed Failed ${error.toString()}'); - }, - ); - }, - icon: Row( - children: [ - Text( - value.reactions!.getCount('love').toString(), - style: themeData.textTheme.bodySmall!.copyWith(fontSize: 14), - ), - const SizedBox(width: 2), - Image.asset( - 'assets/ic_heart.png', - height: 20, - width: 20, - ), - ], + ), + ), + if (value.reactions!.getCount('Love') > 0) + Container( + // width: 36, + // height: 36, + margin: const EdgeInsets.only(left: 30), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.blue.shade100, + boxShadow: const [ + BoxShadow( + color: Colors.black12, + blurRadius: 1, + spreadRadius: 1, ), - ), + ], ), - if (value.reactions!.getCount('Love') > 0) - Container( - // width: 36, - // height: 36, - margin: const EdgeInsets.only(left: 30), - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Colors.blue.shade100, - boxShadow: const [ - BoxShadow( - color: Colors.black12, - blurRadius: 1, - spreadRadius: 1, + child: IconButton( + onPressed: () { + message.react().removeReaction('love').then((value) { + (context.mounted) ? CommonSnackbar.showPositiveSnackbar(context, 'Success', 'Reaction Removed Successfully') : null; + }).onError( + (error, stackTrace) { + (context.mounted) ? CommonSnackbar.showNagativeSnackbar(context, 'Fail', 'Reaction Removed Failed ${error.toString()}') : null; + }, + ); + }, + icon: Row( + children: [ + Text( + value.reactions!.getCount('love').toString(), + style: themeData.textTheme.bodySmall!.copyWith(fontSize: 14), + ), + const SizedBox(width: 2), + Image.asset( + 'assets/ic_heart.png', + height: 20, + width: 20, ), ], ), - child: IconButton( - onPressed: () { - message.react().removeReaction('love').then((value) { - CommonSnackbar.showPositiveSnackbar( - context, 'Success', 'Reaction Removed Successfully'); - }).onError( - (error, stackTrace) { - CommonSnackbar.showNagativeSnackbar( - context, 'Fail', 'Reaction Removed Failed ${error.toString()}'); - }, - ); - }, - icon: Row( - children: [ - Text( - value.reactions!.getCount('love').toString(), - style: themeData.textTheme.bodySmall!.copyWith(fontSize: 14), - ), - const SizedBox(width: 2), - Image.asset( - 'assets/ic_heart.png', - height: 20, - width: 20, - ), - ], - ), - ), ), - ], - ), + ), + ], ), ), ), @@ -574,16 +544,14 @@ class MessageWidget extends StatelessWidget { child: IconButton( onPressed: () { if (myReaction.contains('like')) { - CommonSnackbar.showNagativeSnackbar( - context, 'Error', 'You already have like reaction on this message'); + CommonSnackbar.showNagativeSnackbar(context, 'Error', 'You already have like reaction on this message'); } else { Navigator.of(context).pop(); message.react().addReaction('like').then((value) { - CommonSnackbar.showPositiveSnackbar(context, 'Success', 'Reaction Added Successfully - like'); + (context.mounted) ? CommonSnackbar.showPositiveSnackbar(context, 'Success', 'Reaction Added Successfully - like') : null; }).onError( (error, stackTrace) { - CommonSnackbar.showNagativeSnackbar( - context, 'Fail', 'Reaction Added Failed ${error.toString()} - like'); + (context.mounted) ? CommonSnackbar.showNagativeSnackbar(context, 'Fail', 'Reaction Added Failed ${error.toString()} - like') : null; }, ); } @@ -604,16 +572,14 @@ class MessageWidget extends StatelessWidget { child: IconButton( onPressed: () { if (myReaction.contains('love')) { - CommonSnackbar.showNagativeSnackbar( - context, 'Error', 'You already have love reaction on this message'); + CommonSnackbar.showNagativeSnackbar(context, 'Error', 'You already have love reaction on this message'); } else { Navigator.of(context).pop(); message.react().addReaction('love').then((value) { - CommonSnackbar.showPositiveSnackbar(context, 'Success', 'Reaction Added Successfully - love'); + (context.mounted) ? CommonSnackbar.showPositiveSnackbar(context, 'Success', 'Reaction Added Successfully - love') : null; }).onError( (error, stackTrace) { - CommonSnackbar.showNagativeSnackbar( - context, 'Fail', 'Reaction Added Failed ${error.toString()} - love'); + (context.mounted) ? CommonSnackbar.showNagativeSnackbar(context, 'Fail', 'Reaction Added Failed ${error.toString()} - love') : null; }, ); } @@ -651,41 +617,18 @@ class AmityMessageContentWidget extends StatelessWidget { final data = amityMessage.data; if (data is MessageTextData) { return DynamicTextHighlighting( - text: data.text!, - highlights: amityMessage.metadata == null - ? [] - : [ - ...AmityMentionMetadataGetter(metadata: amityMessage.metadata!) - .getMentionedUsers() - .map((e) => data.text!.substring(e.index, e.index + e.length + 1)) - .toList(), - ...AmityMentionMetadataGetter(metadata: amityMessage.metadata!) - .getMentionedChannels() - .map((e) => data.text!.substring(e.index, e.index + e.length + 1)) - .toList() - ], + text: data.text ?? "No Text", + highlights: amityMessage.metadata == null ? [] : [...AmityMentionMetadataGetter(metadata: amityMessage.metadata!).getMentionedUsers().map((e) => data.text!.substring(e.index, e.index + e.length + 1)).toList(), ...AmityMentionMetadataGetter(metadata: amityMessage.metadata!).getMentionedChannels().map((e) => data.text!.substring(e.index, e.index + e.length + 1)).toList()], style: themeData.textTheme.bodyMedium!.copyWith(), onHighlightClick: (value) { if (value.toLowerCase().contains('all')) { - final temp = AmityMentionMetadataGetter(metadata: amityMessage.metadata!).getMentionedUsers(); - log(AmityMentionMetadataGetter(metadata: amityMessage.metadata!) - .getMentionedUsers() - .map((e) => data.text!.substring(e.index, e.index + e.length + 1)) - .toList() - .toString()); - log(AmityMentionMetadataGetter(metadata: amityMessage.metadata!) - .getMentionedChannels() - .map((e) => data.text!.substring(e.index, e.index + e.length + 1)) - .toList() - .toString()); + // final temp = AmityMentionMetadataGetter(metadata: amityMessage.metadata!).getMentionedUsers(); + log(AmityMentionMetadataGetter(metadata: amityMessage.metadata!).getMentionedUsers().map((e) => data.text!.substring(e.index, e.index + e.length + 1)).toList().toString()); + log(AmityMentionMetadataGetter(metadata: amityMessage.metadata!).getMentionedChannels().map((e) => data.text!.substring(e.index, e.index + e.length + 1)).toList().toString()); CommonSnackbar.showPositiveSnackbar(context, 'Click', 'Click on @all show channel profile'); } else { - print(AmityMentionMetadataGetter(metadata: amityMessage.metadata!) - .getMentionedUsers() - .map((e) => data.text!.substring(e.index, e.index + e.length + 1)) - .toList()); - final amityUser = amityMessage.mentionees! - .firstWhereOrNull((element) => element.user!.displayName == value.replaceAll('@', '')); + // print(AmityMentionMetadataGetter(metadata: amityMessage.metadata!).getMentionedUsers().map((e) => data.text!.substring(e.index, e.index + e.length + 1)).toList()); + final amityUser = amityMessage.mentionees!.firstWhereOrNull((element) => element.user!.displayName == value.replaceAll('@', '')); if (amityUser != null) { GoRouter.of(context).pushNamed( AppRoute.profile, @@ -700,124 +643,133 @@ class AmityMessageContentWidget extends StatelessWidget { } if (data is MessageImageData) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - (data.image!.hasLocalPreview !=null)? - Container( - // color: Colors.red, - child: SizedBox( - width: 100, - height: 100, - child: data.image!.hasLocalPreview! - ? Image.file( - File(data.image!.getFilePath!), - fit: BoxFit.cover, - ) - : Stack( - children: [ - Positioned.fill( - child: Image.network( - data.image!.getUrl(AmityImageSize.MEDIUM), - fit: BoxFit.cover, - ), - ), - Align( - alignment: Alignment.topRight, - child: Container( - padding: const EdgeInsets.all(4), - margin: const EdgeInsets.all(4), - decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.black.withOpacity(.3)), - child: InkWell( - child: const Icon( - Icons.download, - color: Colors.white, + return data.image == null + ? const Text("Can't get Image") + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + (data.image!.hasLocalPreview != null) + ? SizedBox( + width: 100, + height: 100, + child: data.image!.hasLocalPreview! + ? Image.file( + File(data.image!.getFilePath!), + fit: BoxFit.cover, + ) + : Stack( + children: [ + Positioned.fill( + child: Image.network( + data.image!.getUrl(AmityImageSize.MEDIUM), + fit: BoxFit.cover, + ), + ), + Align( + alignment: Alignment.topRight, + child: Container( + padding: const EdgeInsets.all(4), + margin: const EdgeInsets.all(4), + decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.black.withOpacity(.3)), + child: InkWell( + child: const Icon( + Icons.download, + color: Colors.white, + ), + onTap: () async { + String fileName = await MobileDownloadService().download(url: data.image!.getUrl(AmityImageSize.MEDIUM)); + + print(fileName); + + CommonSnackbar.showPositiveSnackbar(context, 'Success', 'Image Save $fileName'); + }, + ), + ), + ) + ], ), - onTap: () async { - String fileName = await MobileDownloadService() - .download(url: data.image!.getUrl(AmityImageSize.MEDIUM)); - - print(fileName); - - CommonSnackbar.showPositiveSnackbar(context, 'Success', 'Image Save $fileName'); - }, - ), - ), - ) - ], - ), - ), - ) : Container(width: 30 , height: 30, color: Colors.amber,), - if (data.caption != null && data.caption!.isNotEmpty) - Text( - '${data.caption}', - style: themeData.textTheme.bodyMedium, - ), - ], - ); + ) + : Container( + width: 30, + height: 30, + color: Colors.amber, + ), + if (data.caption != null && data.caption!.isNotEmpty) + Text( + '${data.caption}', + style: themeData.textTheme.bodyMedium, + ), + ], + ); } if (data is MessageFileData) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - (data.file?.hasLocalPreview!=null) - ? TextButton.icon( - onPressed: () {}, - icon: const Icon(Icons.attach_file_rounded), - label: Text( - data.file?.getFilePath?.split('/').last ?? data.file?.getUrl ?? "", - ), - ) - : Container( - color: Colors.grey.shade300, - child: ListTile( - leading: const Icon(Icons.attach_file_rounded), - title: Text( - data.file!.fileName!, - ), - trailing: Container( - padding: const EdgeInsets.all(4), - margin: const EdgeInsets.all(4), - decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.black.withOpacity(.3)), - child: InkWell( - child: const Icon( - Icons.download, - color: Colors.white, + return SizedBox( + height: 100, + width: double.infinity, + child: data.file == null + ? const Text("Can't get File") + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + (data.file?.hasLocalPreview != null) + ? TextButton.icon( + onPressed: () {}, + icon: const Icon(Icons.attach_file_rounded), + label: Text( + data.file?.getFilePath?.split('/').last ?? data.file?.getUrl ?? "", + ), + ) + : Container( + width: double.infinity, + color: Colors.grey.shade300, + child: ListTile( + leading: const Icon(Icons.attach_file_rounded), + title: Text( + data.file!.fileName!, + ), + trailing: Container( + padding: const EdgeInsets.all(4), + margin: const EdgeInsets.all(4), + decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.black.withOpacity(.3)), + child: InkWell( + child: const Icon( + Icons.download, + color: Colors.white, + ), + onTap: () async { + // String fileName = await MobileDownloadService().download(url: data.file!.getUrl!); + // print(fileName); + }, + ), + ), + // tileColor: Colors.red, + // focusColor: Colors.red, + // selectedColor: Colors.red, + ), ), - onTap: () async { - String fileName = await MobileDownloadService().download(url: data.file!.getUrl!); - - print(fileName); - }, - ), + // TextButton.icon( + // onPressed: () {}, + // icon: const Icon(Icons.attach_file_rounded), + // label: Text( + // data.file.getUrl.split('/').last, + // ), + // style: TextButton.styleFrom( + // padding: const EdgeInsets.all(12), + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(12), + // ), + // primary: Colors.black, + // backgroundColor: Colors.grey.shade300, + // ), + // ), + if (data.caption != null && data.caption!.isNotEmpty) + Text( + '${data.caption}', + style: themeData.textTheme.bodyMedium, ), - // tileColor: Colors.red, - // focusColor: Colors.red, - // selectedColor: Colors.red, - ), - ), - // TextButton.icon( - // onPressed: () {}, - // icon: const Icon(Icons.attach_file_rounded), - // label: Text( - // data.file.getUrl.split('/').last, - // ), - // style: TextButton.styleFrom( - // padding: const EdgeInsets.all(12), - // shape: RoundedRectangleBorder( - // borderRadius: BorderRadius.circular(12), - // ), - // primary: Colors.black, - // backgroundColor: Colors.grey.shade300, - // ), - // ), - if (data.caption != null && data.caption!.isNotEmpty) - Text( - '${data.caption}', - style: themeData.textTheme.bodyMedium, - ), - ], + ], + ), ); } diff --git a/lib/core/widget/story_widget.dart b/lib/core/widget/story_widget.dart index f3edff3..63cb790 100644 --- a/lib/core/widget/story_widget.dart +++ b/lib/core/widget/story_widget.dart @@ -562,6 +562,7 @@ class FeedReactionActionWidget extends StatelessWidget { Widget build(BuildContext context) { final themeData = Theme.of(context); bool isFlagedByMe = amityStory.myReactions.isNotEmpty; + print("Reaction Event: isFallgedByMe $isFlagedByMe"); return Container( margin: const EdgeInsets.only(top: 8, bottom: 8), child: Row( @@ -578,7 +579,6 @@ class FeedReactionActionWidget extends StatelessWidget { referenceId: amityStory.storyId!), 'like') .then((value) { - print(value.myReactions); }); } else { AmitySocialClient.newReactionRepository() @@ -587,7 +587,6 @@ class FeedReactionActionWidget extends StatelessWidget { referenceId: amityStory.storyId!), 'like') .then((value) { - print(value.myReactions); }); } }, diff --git a/lib/core/widget/subchannel_item_widget.dart b/lib/core/widget/subchannel_item_widget.dart new file mode 100644 index 0000000..08e8e39 --- /dev/null +++ b/lib/core/widget/subchannel_item_widget.dart @@ -0,0 +1,223 @@ +import 'package:amity_sdk/amity_sdk.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_social_sample_app/core/route/app_route.dart'; +import 'package:flutter_social_sample_app/core/widget/common_snackbar.dart'; +import 'package:flutter_social_sample_app/core/widget/dialog/edit_text_dialog.dart'; +import 'package:flutter_social_sample_app/core/widget/dialog/error_dialog.dart'; +import 'package:go_router/go_router.dart'; + +class SubChannelItemWidget extends StatelessWidget { + final AmitySubChannel subChannel; + const SubChannelItemWidget({super.key, required this.subChannel}); + @override + Widget build(BuildContext context) { + final themeData = Theme.of(context); + return Container( + margin: const EdgeInsets.symmetric(horizontal: 8), + child: GestureDetector( + onTap: () { + GoRouter.of(context).pushNamed(AppRoute.chat, params: { + 'channelId': subChannel.subChannelId!, + 'channelName': subChannel.displayName!, + }); + }, + child: Card( + child: Container( + padding: const EdgeInsets.all(8), + child: Stack( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + subChannel.displayName ?? '<< No display name >>', + style: themeData.textTheme.titleLarge, + ), + PopupMenuButton( + child: const Icon(Icons.arrow_drop_down_circle_outlined), + itemBuilder: (BuildContext context) { + return [ + const PopupMenuItem( + value: 0, + child: Text('Soft Delete'), + ), + const PopupMenuItem( + value: 1, + child: Text('Hard Delete'), + ), + const PopupMenuItem( + value: 2, + child: Text('Edit SubChannel'), + ), + const PopupMenuItem( + value: 3, + child: Text('Subscribe to RTE'), + ), + const PopupMenuItem( + value: 4, + child: Text('UnSubscribe to RTE'), + ), + ]; + }, + onSelected: (value) { + switch (value) { + case 0: + AmitySocialClient.newSubChannelRepository() + .softDeleteSubChannel(subChannelId: subChannel.subChannelId!) + .then( + (value) => (context.mounted) + ? CommonSnackbar.showPositiveSnackbar( + context, + 'Success', + 'SubChannel Soft Deleted', + ) + : null, + ) + .onError( + (error, stackTrace) => (context.mounted) + ? CommonSnackbar.showNagativeSnackbar( + context, + 'Error', + error.toString(), + ) + : null, + ); + break; + case 1: + AmitySocialClient.newSubChannelRepository() + .hardDeleteSubChannel(subChannelId: subChannel.subChannelId!) + .then( + (value) => (context.mounted) + ? CommonSnackbar.showPositiveSnackbar( + context, + 'Success', + 'SubChannel Hard Deleted', + ) + : null, + ) + .onError( + (error, stackTrace) => (context.mounted) + ? CommonSnackbar.showNagativeSnackbar( + context, + 'Error', + error.toString(), + ) + : null, + ); + break; + case 2: + EditTextDialog.show( + context, + hintText: 'Enter new Display Name', + onPress: (value) { + if (value.trim().isEmpty) { + ErrorDialog.show(context, title: 'Error', message: 'Channel Name cannot be empty'); + return; + } + AmitySocialClient.newSubChannelRepository() + .updateeditSubChannelSubChannel( + subChannelId: subChannel.subChannelId!, + displayName: value, + ) + .then((value) => (context.mounted) ? CommonSnackbar.showPositiveSnackbar(context, 'Success', 'SubChannel Updated') : null) + .onError((error, stackTrace) => (context.mounted) ? CommonSnackbar.showNagativeSnackbar(context, 'Error', error.toString()) : null); + }, + ); + break; + + case 3: + subChannel + .subscription() + .subscribeTopic() + .then( + (value) => (context.mounted) + ? CommonSnackbar.showPositiveSnackbar( + context, + 'Success', + 'SubChannel Subscribed', + ) + : null, + ) + .onError( + (error, stackTrace) => (context.mounted) + ? CommonSnackbar.showNagativeSnackbar( + context, + 'Error', + error.toString(), + ) + : null, + ); + break; + case 4: + subChannel + .subscription() + .unsubscribeTopic() + .then( + (value) => (context.mounted) + ? CommonSnackbar.showPositiveSnackbar( + context, + 'Success', + 'SubChannel Unsubscribed', + ) + : null, + ) + .onError( + (error, stackTrace) => (context.mounted) + ? CommonSnackbar.showNagativeSnackbar( + context, + 'Error', + error.toString(), + ) + : null, + ); + break; + } + }), + ], + ), + Text( + 'Message Count: ${subChannel.messageCount ?? 'NaN'}', + style: themeData.textTheme.bodySmall, + ), + Text( + 'isDeleted: ${subChannel.isDeleted ?? 'NaN'}', + style: themeData.textTheme.bodySmall, + ), + Text( + 'last Activity: ${subChannel.lastActivity?.toIso8601String() ?? 'NaN'}', + style: themeData.textTheme.bodySmall, + ), + Text( + 'isDeleted: ${subChannel.isDeleted ?? 'NaN'}', + style: themeData.textTheme.bodySmall, + ), + SelectableText( + 'SubChannel ID : ${subChannel.subChannelId ?? 'NaN'}', + style: themeData.textTheme.bodySmall, + ), + ], + ), + subChannel.isDeleted ?? false + ? Positioned.fill( + child: Container( + color: Colors.grey.withOpacity(0.5), + child: Center( + child: Text( + 'Deleted', + style: themeData.textTheme.titleLarge?.copyWith(color: Colors.red), + ), + ), + ), + ) + : const SizedBox.shrink(), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/presentation/screen/channel_create/channel_create_screen.dart b/lib/presentation/screen/channel_create/channel_create_screen.dart index b54b85c..919e324 100644 --- a/lib/presentation/screen/channel_create/channel_create_screen.dart +++ b/lib/presentation/screen/channel_create/channel_create_screen.dart @@ -231,7 +231,7 @@ class _ChannelCreateScreenState extends State { } else { builder = AmityChatClient.newChannelRepository().createChannel().communityType().withDisplayName(name); - if (channeld.isEmpty) channeld = const Uuid().v4(); + // if (channeld.isEmpty) channeld = const Uuid().v4(); builder.channelId(channeld); } break; diff --git a/lib/presentation/screen/channel_profile/channel_profile_screen.dart b/lib/presentation/screen/channel_profile/channel_profile_screen.dart index fbe41fd..565ae2a 100644 --- a/lib/presentation/screen/channel_profile/channel_profile_screen.dart +++ b/lib/presentation/screen/channel_profile/channel_profile_screen.dart @@ -5,18 +5,17 @@ import 'package:flutter_social_sample_app/core/widget/dialog/edit_text_dialog.da import 'package:flutter_social_sample_app/core/widget/dialog/error_dialog.dart'; import 'package:flutter_social_sample_app/presentation/screen/channel_member/channel_member_banned_screen.dart'; import 'package:flutter_social_sample_app/presentation/screen/channel_member/channel_member_screen.dart'; +import 'package:flutter_social_sample_app/presentation/screen/sub_channel/sub_channel_list.dart'; import 'package:go_router/go_router.dart'; class ChannelProfileScreen extends StatefulWidget { - const ChannelProfileScreen({Key? key, required this.channelId}) - : super(key: key); + const ChannelProfileScreen({Key? key, required this.channelId}) : super(key: key); final String channelId; @override State createState() => _ChannelProfileScreenState(); } -class _ChannelProfileScreenState extends State - with TickerProviderStateMixin { +class _ChannelProfileScreenState extends State with TickerProviderStateMixin { late TabController _tabController; late AmityChannel _amityChannel; Future? _future; @@ -25,10 +24,9 @@ class _ChannelProfileScreenState extends State @override void initState() { - _tabController = TabController(length: 2, vsync: this); + _tabController = TabController(length: 3, vsync: this); - _future = - AmityChatClient.newChannelRepository().getChannel(widget.channelId); + _future = AmityChatClient.newChannelRepository().getChannel(widget.channelId); super.initState(); } @@ -53,23 +51,21 @@ class _ChannelProfileScreenState extends State onPressed: () { ///Mute/Unmute Channel if (_amityChannel.isMuted ?? false) { - AmityChatClient.newChannelRepository() - .unMuteChannel(widget.channelId); + AmityChatClient.newChannelRepository().unMuteChannel(widget.channelId); } else { - AmityChatClient.newChannelRepository() - .muteChannel(widget.channelId); + AmityChatClient.newChannelRepository().muteChannel(widget.channelId); } }, icon: Icon( - ((_amityChannel.isMuted ?? false) - ? Icons.volume_off_rounded - : Icons.volume_up_rounded), + ((_amityChannel.isMuted ?? false) ? Icons.volume_off_rounded : Icons.volume_up_rounded), ), ), IconButton( onPressed: () { - GoRouter.of(context).pushNamed(AppRoute.chat, - params: {'channelId': widget.channelId}); + GoRouter.of(context).pushNamed(AppRoute.chat, params: { + 'channelId': widget.channelId, + 'channelName': _amityChannel.displayName!, + }); }, icon: const Icon( Icons.mark_unread_chat_alt_outlined, @@ -89,9 +85,11 @@ class _ChannelProfileScreenState extends State ), PopupMenuItem( value: 3, - child: Text((_amityChannel.isMuted ?? false) - ? 'Unmute' - : 'Mute'), + child: Text((_amityChannel.isMuted ?? false) ? 'Unmute' : 'Mute'), + ), + const PopupMenuItem( + value: 4, + child: Text("Create Sub Channel"), ), ]; }, @@ -102,23 +100,14 @@ class _ChannelProfileScreenState extends State onSelected: (index) { if (index == 1) { //Open Edit Channel - GoRouter.of(context).pushNamed( - AppRoute.updateChannel, - queryParams: {'channelId': widget.channelId}); + GoRouter.of(context).pushNamed(AppRoute.updateChannel, queryParams: {'channelId': widget.channelId}); } if (index == 2) { - EditTextDialog.show(context, - title: - 'Check my permission in this community', - hintText: 'Enter permission name', - onPress: (value) { - final permissions = AmityPermission.values - .where((v) => v.value == value); + EditTextDialog.show(context, title: 'Check my permission in this community', hintText: 'Enter permission name', onPress: (value) { + final permissions = AmityPermission.values.where((v) => v.value == value); if (permissions.isEmpty) { - ErrorDialog.show(context, - title: 'Error', - message: 'permission does not exist'); + ErrorDialog.show(context, title: 'Error', message: 'permission does not exist'); } else { // final hasPermission = // AmityCoreClient.hasPermission(permissions.first) @@ -134,13 +123,32 @@ class _ChannelProfileScreenState extends State if (index == 3) { ///Mute/Unmute Channel if (_amityChannel.isMuted ?? false) { - AmityChatClient.newChannelRepository() - .unMuteChannel(widget.channelId); + AmityChatClient.newChannelRepository().unMuteChannel(widget.channelId); } else { - AmityChatClient.newChannelRepository() - .muteChannel(widget.channelId); + AmityChatClient.newChannelRepository().muteChannel(widget.channelId); } } + + if (index == 4) { + EditTextDialog.show(context, title: 'Create New Sub Channel', hintText: 'Enter New Channel Name', buttonText: 'Create', onPress: (value) { + if(value.trim().isEmpty){ + ErrorDialog.show(context, title: 'Error', message: 'Channel Name cannot be empty'); + return; + } + AmitySocialClient.newSubChannelRepository().createSubChannel(widget.channelId, value).then((value) { + const snackBar = SnackBar( + backgroundColor: Colors.green, + content: Text( + 'Sub Channel Created', + style: TextStyle(color: Colors.white), + ), + ); + ScaffoldMessenger.of(context).showSnackBar(snackBar); + }).onError((error, stackTrace) { + ErrorDialog.show(context, title: 'Error', message: error.toString()); + }); + }); + } }, ), ], @@ -149,8 +157,7 @@ class _ChannelProfileScreenState extends State headerSliverBuilder: (context, innerBoxIsScrolled) { return [ SliverToBoxAdapter( - child: _ChannelProfileHeaderWidget( - amityChannel: _amityChannel), + child: _ChannelProfileHeaderWidget(amityChannel: _amityChannel), ), SliverToBoxAdapter( child: DefaultTabController( @@ -163,6 +170,9 @@ class _ChannelProfileScreenState extends State ), Tab( text: 'Banned Members', + ), + Tab( + text: 'SubChannel', ) ], ), @@ -184,6 +194,7 @@ class _ChannelProfileScreenState extends State channelId: widget.channelId, showAppBar: false, ), + SubChannelList(channelId: widget.channelId), // ChannelFeedScreen( // channelId: widget.channelId, showAppBar: false), // memberScreen, @@ -201,18 +212,13 @@ class _ChannelProfileScreenState extends State floatingActionButton: FloatingActionButton( onPressed: () { //show add member action - EditTextDialog.show(context, - title: 'Add Channel Member', - hintText: 'Enter Comma seperated user Ids', onPress: (value) { - AmityChatClient.newChannelRepository() - .addMembers(_amityChannel.channelId!, value.split(',')) - .then((value) { - if (memberList.currentState != null) { - memberList.currentState!.refreshList(); - } + EditTextDialog.show(context, title: 'Add Channel Member', hintText: 'Enter Comma seperated user Ids', onPress: (value) { + AmityChatClient.newChannelRepository().addMembers(_amityChannel.channelId!, value.split(',')).then((value) { + if (memberList.currentState != null) { + memberList.currentState!.refreshList(); + } }).onError((error, stackTrace) { - ErrorDialog.show(context, - title: 'Error', message: error.toString()); + ErrorDialog.show(context, title: 'Error', message: error.toString()); }); }); }, @@ -223,8 +229,7 @@ class _ChannelProfileScreenState extends State } class _ChannelProfileHeaderWidget extends StatelessWidget { - const _ChannelProfileHeaderWidget({Key? key, required this.amityChannel}) - : super(key: key); + const _ChannelProfileHeaderWidget({Key? key, required this.amityChannel}) : super(key: key); final AmityChannel amityChannel; @override Widget build(BuildContext context) { @@ -240,16 +245,14 @@ class _ChannelProfileHeaderWidget extends StatelessWidget { Container( width: 64, height: 64, - decoration: BoxDecoration( - shape: BoxShape.circle, color: Colors.grey.withOpacity(.3)), + decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.grey.withOpacity(.3)), clipBehavior: Clip.antiAliasWithSaveLayer, - child: - amityChannel.avatar?.getUrl(AmityImageSize.MEDIUM) != null - ? Image.network( - amityChannel.avatar!.getUrl(AmityImageSize.MEDIUM), - fit: BoxFit.fill, - ) - : Image.asset('assets/user_placeholder.png'), + child: amityChannel.avatar?.getUrl(AmityImageSize.MEDIUM) != null + ? Image.network( + amityChannel.avatar!.getUrl(AmityImageSize.MEDIUM), + fit: BoxFit.fill, + ) + : Image.asset('assets/user_placeholder.png'), ), const SizedBox(width: 18), Expanded( @@ -262,12 +265,9 @@ class _ChannelProfileHeaderWidget extends StatelessWidget { children: [ TextSpan( text: '${amityChannel.messageCount}\n', - style: themeData.textTheme.titleMedium! - .copyWith(fontWeight: FontWeight.bold), + style: themeData.textTheme.titleMedium!.copyWith(fontWeight: FontWeight.bold), ), - TextSpan( - text: 'Messages', - style: themeData.textTheme.bodyMedium), + TextSpan(text: 'Messages', style: themeData.textTheme.bodyMedium), ], ), ), @@ -277,12 +277,9 @@ class _ChannelProfileHeaderWidget extends StatelessWidget { children: [ TextSpan( text: '${amityChannel.memberCount}\n', - style: themeData.textTheme.titleMedium! - .copyWith(fontWeight: FontWeight.bold), + style: themeData.textTheme.titleMedium!.copyWith(fontWeight: FontWeight.bold), ), - TextSpan( - text: 'Members', - style: themeData.textTheme.bodyMedium), + TextSpan(text: 'Members', style: themeData.textTheme.bodyMedium), ], ), ) @@ -333,9 +330,7 @@ class _ChannelProfileHeaderWidget extends StatelessWidget { ), const SizedBox(height: 18), FutureBuilder( - future: AmityChatClient.newChannelRepository() - .membership(amityChannel.channelId!) - .getMyMembership(), + future: AmityChatClient.newChannelRepository().membership(amityChannel.channelId!).getMyMembership(), builder: (context, snapshot) { final amityChannelMember = snapshot.data; if (snapshot.hasData) { @@ -344,29 +339,17 @@ class _ChannelProfileHeaderWidget extends StatelessWidget { width: 260, child: ElevatedButton( onPressed: () { - if (amityChannelMember!.membership == - AmityMembershipType.NONE) { - AmityChatClient.newChannelRepository() - .joinChannel(amityChannel.channelId!) - .then((value) {}) - .onError((error, stackTrace) { - ErrorDialog.show(context, - title: 'Error', message: error.toString()); + if (amityChannelMember!.membership == AmityMembershipType.NONE) { + AmityChatClient.newChannelRepository().joinChannel(amityChannel.channelId!).then((value) {}).onError((error, stackTrace) { + ErrorDialog.show(context, title: 'Error', message: error.toString()); }); } else { - AmityChatClient.newChannelRepository() - .leaveChannel(amityChannel.channelId!) - .then((value) {}) - .onError((error, stackTrace) { - ErrorDialog.show(context, - title: 'Error', message: error.toString()); + AmityChatClient.newChannelRepository().leaveChannel(amityChannel.channelId!).then((value) {}).onError((error, stackTrace) { + ErrorDialog.show(context, title: 'Error', message: error.toString()); }); } }, - child: Text(amityChannelMember!.membership == - AmityMembershipType.NONE - ? 'Join' - : 'Leave'), + child: Text(amityChannelMember!.membership == AmityMembershipType.NONE ? 'Join' : 'Leave'), ), ), ); diff --git a/lib/presentation/screen/chat/chat_screen.dart b/lib/presentation/screen/chat/chat_screen.dart index b93e245..5350f34 100644 --- a/lib/presentation/screen/chat/chat_screen.dart +++ b/lib/presentation/screen/chat/chat_screen.dart @@ -6,8 +6,9 @@ import 'package:flutter_social_sample_app/core/widget/dialog/edit_text_dialog.da import 'package:flutter_social_sample_app/core/widget/message_widget.dart'; class ChatScreen extends StatefulWidget { - const ChatScreen({Key? key, required this.channelId}) : super(key: key); + const ChatScreen({Key? key, required this.channelId , required this.channelName}) : super(key: key); final String channelId; + final String channelName; @override State createState() => _ChatScreenState(); } @@ -67,6 +68,9 @@ class _ChatScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( + appBar: AppBar( + title: Text( widget.channelName), + ), backgroundColor: Colors.white, body: SafeArea( child: Column( diff --git a/lib/presentation/screen/comment_query/comment_query_screen.dart b/lib/presentation/screen/comment_query/comment_query_screen.dart index 318def8..d564567 100644 --- a/lib/presentation/screen/comment_query/comment_query_screen.dart +++ b/lib/presentation/screen/comment_query/comment_query_screen.dart @@ -26,27 +26,20 @@ class CommentQueryScreen extends StatefulWidget { class _CommentQueryScreenState extends State { List amityComments = []; late CommentLiveCollection commentLiveCollection; - final scrollcontroller = ScrollController(); bool loading = false; - AmityComment? _replyToComment; - AmityCommentSortOption _sortOption = AmityCommentSortOption.LAST_CREATED; - final mentionUsers = []; - AmityCommentDataTypeFilter? dataTypes; - bool _includeDeleted = false; - @override void initState() { - if (widget.referenceType == 'post') { commentLiveCollection = AmitySocialClient.newCommentRepository() .getComments() .post(widget.referenceId) + .parentId(null) .sortBy(_sortOption) .dataTypes(dataTypes) .includeDeleted(_includeDeleted) @@ -60,7 +53,6 @@ class _CommentQueryScreenState extends State { .includeDeleted(_includeDeleted) .getLiveCollection(); } - commentLiveCollection.getStreamController().stream.listen((event) { if (mounted) { setState(() { @@ -68,19 +60,16 @@ class _CommentQueryScreenState extends State { }); } }); - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { commentLiveCollection.loadNext(); }); - scrollcontroller.addListener(pagination); - super.initState(); } void pagination() { - - if ((scrollcontroller.position.pixels == scrollcontroller.position.maxScrollExtent) && commentLiveCollection.hasNextPage()) { + if ((scrollcontroller.position.pixels == scrollcontroller.position.maxScrollExtent) + && commentLiveCollection.hasNextPage()) { setState(() { commentLiveCollection.loadNext(); diff --git a/lib/presentation/screen/dashboard/dashboard_screen.dart b/lib/presentation/screen/dashboard/dashboard_screen.dart index dcccd88..cf96c66 100644 --- a/lib/presentation/screen/dashboard/dashboard_screen.dart +++ b/lib/presentation/screen/dashboard/dashboard_screen.dart @@ -370,6 +370,25 @@ class _DashboardScreenState extends State { }, child: const Text('Get Channel'), ), + + + const SizedBox(height: 20), + TextButton( + onPressed: () { + EditTextDialog.show(context, + title: 'Get Sub Channel', + hintText: 'Enter Sub Channel ID', + // defString: 'live200', + buttonText: 'Submit', onPress: (value) { + GoRouter.of(context).pushNamed(AppRoute.subChannelProfile, + params: {'subChannelId': value}); + // AmityChatClient.newChannelRepository().getChannel(value); + }); + }, + child: const Text('Get Sub Channel'), + ), + + const SizedBox(height: 20), TextButton( onPressed: () { diff --git a/lib/presentation/screen/login/login_screen.dart b/lib/presentation/screen/login/login_screen.dart index 7a0f420..6a3a3df 100644 --- a/lib/presentation/screen/login/login_screen.dart +++ b/lib/presentation/screen/login/login_screen.dart @@ -1,8 +1,5 @@ -import 'dart:io'; - import 'package:amity_sdk/amity_sdk.dart'; import 'package:amity_video_player/amity_video_player.dart'; -import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:flutter_social_sample_app/core/preferences/preference_interface_impl.dart'; import 'package:flutter_social_sample_app/core/route/app_route.dart'; @@ -21,6 +18,7 @@ class _LoginScreenState extends State { final _displayNameTextController = TextEditingController(); final _apiKeyTextController = TextEditingController(); final _serverUrlTextController = TextEditingController(); + final _mqttUrlTextController = TextEditingController(); @override Widget build(BuildContext context) { //DEV Server @@ -32,11 +30,9 @@ class _LoginScreenState extends State { // STAGING Server _userIdTextController.text = 'victimAndroid'; _displayNameTextController.text = 'Victim Android'; - _apiKeyTextController.text = - 'b0efe90c3bdda2304d628918520c1688845889e4bc363d2c'; - _serverUrlTextController.text = - AmityRegionalHttpEndpoint.custom('https://api.staging.amity.co/') - .endpoint; + _apiKeyTextController.text = 'b0efe90c3bdda2304d628918520c1688845889e4bc363d2c'; + _serverUrlTextController.text = AmityRegionalHttpEndpoint.custom('https://api.staging.amity.co/').endpoint; + _mqttUrlTextController.text = AmityRegionalMqttEndpoint.custom('ssq.staging.amity.co').endpoint; // //STAGING Server // _userIdTextController.text = 'sara'; @@ -110,6 +106,14 @@ class _LoginScreenState extends State { label: Text('Server url'), ), ), + const SizedBox(height: 24), + TextFormField( + key: const Key('mqtt_url_txtip'), + controller: _mqttUrlTextController, + decoration: const InputDecoration( + label: Text('Mqtt url'), + ), + ), const SizedBox(height: 48), TextButton( key: const Key('Login_btn_id'), @@ -124,35 +128,28 @@ class _LoginScreenState extends State { await AmityCoreClient.setup( option: AmityCoreClientOption( apiKey: apikey, - httpEndpoint: AmityRegionalHttpEndpoint( - _serverUrlTextController.text), + httpEndpoint: AmityRegionalHttpEndpoint(_serverUrlTextController.text), + socketEndpoint: AmityRegionalSocketEndpoint(_serverUrlTextController.text), // mqttEndpoint: AmityRegionalMqttEndpoint.custom('ssq.dev.amity.co'), - mqttEndpoint: AmityRegionalMqttEndpoint.custom( - 'ssq.staging.amity.co'), + mqttEndpoint: AmityRegionalMqttEndpoint.custom(_mqttUrlTextController.text), // mqttEndpoint: AmityRegionalMqttEndpoint.SG, showLogs: true), sycInitialization: true, ); - AmityStreamPlayerClient.setup( - AmityCoreClient.getConfiguration()); + AmityStreamPlayerClient.setup(AmityCoreClient.getConfiguration()); //Login the user String userId = _userIdTextController.text.trim(); - String userDisplayName = - _displayNameTextController.text.trim(); + String userDisplayName = _displayNameTextController.text.trim(); // AmityCoreClient.isUserLoggedIn(); - await AmityCoreClient.login(userId) - .displayName(userDisplayName) - .submit(); + await AmityCoreClient.login(userId).displayName(userDisplayName).submit(); PreferenceInterfaceImpl().setLoggedIn(true); - PreferenceInterfaceImpl() - .setLoggedInUserDisplayName(userDisplayName); + PreferenceInterfaceImpl().setLoggedInUserDisplayName(userDisplayName); PreferenceInterfaceImpl().setLoggedInUserId(userId); - GoRouter.of(context).go(AppRoute.homeRoute); + (context.mounted) ? GoRouter.of(context).go(AppRoute.homeRoute) : null; } catch (error) { - CommonSnackbar.showNagativeSnackbar( - context, 'Error', error.toString()); + (context.mounted) ? CommonSnackbar.showNagativeSnackbar(context, 'Error', error.toString()) : null; } }, style: TextButton.styleFrom( diff --git a/lib/presentation/screen/sub_channel/get_sub_channel.dart b/lib/presentation/screen/sub_channel/get_sub_channel.dart new file mode 100644 index 0000000..49c25e0 --- /dev/null +++ b/lib/presentation/screen/sub_channel/get_sub_channel.dart @@ -0,0 +1,31 @@ +import 'package:amity_sdk/amity_sdk.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_social_sample_app/core/widget/subchannel_item_widget.dart'; + +class GetSubChannelScreen extends StatelessWidget { + final String subChannelId; + const GetSubChannelScreen({super.key, required this.subChannelId}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('SubChannel - $subChannelId'), + ), + body: SizedBox( + width: double.infinity, + height: double.infinity, + child: StreamBuilder( + stream: AmitySocialClient.newSubChannelRepository().live.getSubChannel(subChannelId), + builder: (context, snapshot) { + if (snapshot.hasData) { + final value = snapshot.data!; + return SubChannelItemWidget(subChannel: value); + } + return Container(); + }, + ), + ), + ); + } +} diff --git a/lib/presentation/screen/sub_channel/sub_channel_list.dart b/lib/presentation/screen/sub_channel/sub_channel_list.dart new file mode 100644 index 0000000..24be88b --- /dev/null +++ b/lib/presentation/screen/sub_channel/sub_channel_list.dart @@ -0,0 +1,134 @@ +import 'package:amity_sdk/amity_sdk.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_social_sample_app/core/widget/subchannel_item_widget.dart'; + +class SubChannelList extends StatefulWidget { + final String channelId; + const SubChannelList({super.key, required this.channelId}); + + @override + State createState() => _SubChannelListState(); +} + +class _SubChannelListState extends State { + late SubChannelLiveCollection subChannelLiveCollection; + List amitySubChannels = []; + + final scrollcontroller = ScrollController(); + bool loading = false; + bool _includeDeleted = false; + bool _excludeGeneral = false; + + @override + void initState() { + subChannelLiveCollectionInit(); + super.initState(); + } + + void subChannelLiveCollectionInit() { + subChannelLiveCollection = SubChannelLiveCollection( + request: () => AmitySocialClient.newSubChannelRepository().getSubChannels().channelId(widget.channelId).excludeMainSubChannel(_excludeGeneral).includeDeleted(_includeDeleted).build(), + ); + + subChannelLiveCollection.getStreamController().stream.listen((event) { + if (mounted) { + setState(() { + amitySubChannels = event; + }); + } + }); + + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + subChannelLiveCollection.loadNext(); + }); + + scrollcontroller.addListener(pagination); + } + + void pagination() { + if ((scrollcontroller.position.pixels == (scrollcontroller.position.maxScrollExtent)) && subChannelLiveCollection.hasNextPage()) { + subChannelLiveCollection.loadNext(); + } + } + + @override + Widget build(BuildContext context) { + return SizedBox( + width: double.infinity, + height: double.infinity, + child: Column( + children: [ + Row( + children: [ + Row( + children: [ + const Text('Exclude General'), + Checkbox( + value: _excludeGeneral, + onChanged: (bool? value) { + setState(() { + _excludeGeneral = value!; + subChannelLiveCollection.reset(); + subChannelLiveCollectionInit(); + subChannelLiveCollection.getFirstPageRequest(); + }); + }, + activeColor: Colors.green, // Change the color when checked + checkColor: Colors.white, // Change the check mark color + ), + ], + ), + + Row( + children: [ + const Text('Include Deleted'), + Checkbox( + value: _includeDeleted, + onChanged: (bool? value) { + setState(() { + _includeDeleted = value!; + subChannelLiveCollection.reset(); + subChannelLiveCollectionInit(); + subChannelLiveCollection.getFirstPageRequest(); + }); + }, + activeColor: Colors.green, // Change the color when checked + checkColor: Colors.white, // Change the check mark color + ), + ], + ) + ], + ), + Expanded( + child: amitySubChannels.isNotEmpty + ? RefreshIndicator( + onRefresh: () async { + subChannelLiveCollection.reset(); + subChannelLiveCollection.getFirstPageRequest(); + }, + child: ListView.builder( + controller: scrollcontroller, + itemCount: amitySubChannels.length, + itemBuilder: (context, index) { + final amitySubChannel = amitySubChannels[index]; + print('amitySubChannel.path: ${amitySubChannel.path}'); + + var uniqueKey = UniqueKey(); + return SubChannelItemWidget(key: uniqueKey, subChannel: amitySubChannel); + }, + ), + ) + : Container( + alignment: Alignment.center, + child: subChannelLiveCollection.isFetching ? const CircularProgressIndicator() : const Text('No Sub Channel'), + ), + ), + if (subChannelLiveCollection.isFetching && amitySubChannels.isNotEmpty) + Container( + alignment: Alignment.center, + child: const CircularProgressIndicator(), + ) + ], + )); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 4a27c30..1b81c2d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,7 @@ name: flutter_social_sample_app description: Demonstrates how to use the flutter_application_1 plugin. -version: 1.1.49+64 +version: 1.1.50+67 + @@ -23,8 +24,8 @@ dependencies: chewie: ^1.3.1 cupertino_icons: ^1.0.2 file_picker: ^5.0.1 - firebase_core: ^2.25.3 - firebase_messaging: ^14.7.14 + firebase_core: ^3.6.0 + firebase_messaging: ^15.1.3 flutter: sdk: flutter flutter_keyboard_visibility: ^5.4.0 @@ -37,7 +38,7 @@ dependencies: permission_handler: ^10.3.0 fluttertoast: ^8.2.4 universal_html: ^2.2.3 - url_launcher: ^6.2.3 + url_launcher: ^6.3.0 uuid: ^4.3.3 video_player: ^2.3.0 visibility_detector: ^0.4.0+2