diff --git a/assets/Icons/amity_ic_close_button.svg b/assets/Icons/amity_ic_close_button.svg new file mode 100644 index 00000000..a0f8ecda --- /dev/null +++ b/assets/Icons/amity_ic_close_button.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/Icons/amity_ic_create_post_button.svg b/assets/Icons/amity_ic_create_post_button.svg index e78573c9..fed7528c 100644 --- a/assets/Icons/amity_ic_create_post_button.svg +++ b/assets/Icons/amity_ic_create_post_button.svg @@ -1,4 +1,3 @@ - - - + + diff --git a/assets/Icons/amity_ic_post_creation_button.svg b/assets/Icons/amity_ic_post_creation_button.svg new file mode 100644 index 00000000..0871b07d --- /dev/null +++ b/assets/Icons/amity_ic_post_creation_button.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/Icons/amity_ic_toast_loading.svg b/assets/Icons/amity_ic_toast_loading.svg new file mode 100644 index 00000000..cd251143 --- /dev/null +++ b/assets/Icons/amity_ic_toast_loading.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index b520d1a5..5fe21f35 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,4 +1,6 @@ PODS: + - camera_avfoundation (0.0.1): + - Flutter - DKImagePickerController/Core (4.3.4): - DKImagePickerController/ImageDataManager - DKImagePickerController/Resource @@ -67,6 +69,9 @@ PODS: - FlutterMacOS - sign_in_with_apple (0.0.1): - Flutter + - sqflite (0.0.3): + - Flutter + - FlutterMacOS - SwiftyGif (5.4.4) - url_launcher_ios (0.0.1): - Flutter @@ -79,6 +84,7 @@ PODS: - Flutter DEPENDENCIES: + - camera_avfoundation (from `.symlinks/plugins/camera_avfoundation/ios`) - file_picker (from `.symlinks/plugins/file_picker/ios`) - Flutter (from `Flutter`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) @@ -89,6 +95,7 @@ DEPENDENCIES: - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - sign_in_with_apple (from `.symlinks/plugins/sign_in_with_apple/ios`) + - sqflite (from `.symlinks/plugins/sqflite/darwin`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`) - video_thumbnail (from `.symlinks/plugins/video_thumbnail/ios`) @@ -103,6 +110,8 @@ SPEC REPOS: - SwiftyGif EXTERNAL SOURCES: + camera_avfoundation: + :path: ".symlinks/plugins/camera_avfoundation/ios" file_picker: :path: ".symlinks/plugins/file_picker/ios" Flutter: @@ -123,6 +132,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" sign_in_with_apple: :path: ".symlinks/plugins/sign_in_with_apple/ios" + sqflite: + :path: ".symlinks/plugins/sqflite/darwin" url_launcher_ios: :path: ".symlinks/plugins/url_launcher_ios/ios" video_player_avfoundation: @@ -133,12 +144,13 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/wakelock_plus/ios" SPEC CHECKSUMS: + camera_avfoundation: dd002b0330f4981e1bbcb46ae9b62829237459a4 DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 file_picker: 15fd9539e4eb735dc54bae8c0534a7a9511a03de Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 image_picker_ios: 4a8aadfbb6dc30ad5141a2ce3832af9214a705b5 - integration_test: 13825b8a9334a850581300559b8839134b124670 + integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4 libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009 open_file: 02eb5cb6b21264bd3a696876f5afbfb7ca4f4b7d package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7 @@ -147,6 +159,7 @@ SPEC CHECKSUMS: SDWebImage: a81bbb3ba4ea5f810f4069c68727cb118467a04a shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126 sign_in_with_apple: f3bf75217ea4c2c8b91823f225d70230119b8440 + sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4 video_player_avfoundation: 81e49bb3d9fb63dccf9fa0f6d877dc3ddbeac126 @@ -155,4 +168,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796 -COCOAPODS: 1.15.2 +COCOAPODS: 1.15.0 diff --git a/example/pubspec.lock b/example/pubspec.lock index caf90866..aa3d814a 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: amity_sdk - sha256: "9655e950bbdc1f00f9c71fe2d31c0fee9a747d609aa99786813fc9e2d86eda47" + sha256: "02fbe4f4ecdedff7fc45a43e164200f99eec87ac819e1d1e104f8e1cf5957cfb" url: "https://pub.dev" source: hosted - version: "0.46.2" + version: "0.48.0-beta.2" amity_sdk_api: dependency: transitive description: @@ -23,7 +23,7 @@ packages: path: ".." relative: true source: path - version: "3.0.1" + version: "3.0.3" animation_wrappers: dependency: transitive description: @@ -88,6 +88,70 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" + cached_network_image: + dependency: transitive + description: + name: cached_network_image + sha256: "28ea9690a8207179c319965c13cd8df184d5ee721ae2ce60f398ced1219cea1f" + url: "https://pub.dev" + source: hosted + version: "3.3.1" + cached_network_image_platform_interface: + dependency: transitive + description: + name: cached_network_image_platform_interface + sha256: "9e90e78ae72caa874a323d78fa6301b3fb8fa7ea76a8f96dc5b5bf79f283bf2f" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + cached_network_image_web: + dependency: transitive + description: + name: cached_network_image_web + sha256: "205d6a9f1862de34b93184f22b9d2d94586b2f05c581d546695e3d8f6a805cd7" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + camera: + dependency: "direct main" + description: + name: camera + sha256: "26ff41045772153f222ffffecba711a206f670f5834d40ebf5eed3811692f167" + url: "https://pub.dev" + source: hosted + version: "0.11.0+2" + camera_android_camerax: + dependency: transitive + description: + name: camera_android_camerax + sha256: "8bd9cab67551642eb33ceb33ece7acc0890014fc90ddfae637c7e2b683657e65" + url: "https://pub.dev" + source: hosted + version: "0.6.7+2" + camera_avfoundation: + dependency: transitive + description: + name: camera_avfoundation + sha256: "7c28969a975a7eb2349bc2cb2dfe3ad218a33dba9968ecfb181ce08c87486655" + url: "https://pub.dev" + source: hosted + version: "0.9.17+3" + camera_platform_interface: + dependency: transitive + description: + name: camera_platform_interface + sha256: fdb8b7a40c3564ce2967273445707d58cbff91bc2fa129e8de80af8cc501e793 + url: "https://pub.dev" + source: hosted + version: "2.7.1" + camera_web: + dependency: transitive + description: + name: camera_web + sha256: "595f28c89d1fb62d77c73c633193755b781c6d2e0ebcd8dc25b763b514e6ba8f" + url: "https://pub.dev" + source: hosted + version: "0.3.5" carousel_slider: dependency: transitive description: @@ -317,6 +381,14 @@ packages: url: "https://pub.dev" source: hosted version: "8.1.6" + flutter_cache_manager: + dependency: transitive + description: + name: flutter_cache_manager + sha256: "8207f27539deb83732fdda03e259349046a39a4c767269285f449ade355d54ba" + url: "https://pub.dev" + source: hosted + version: "3.3.1" flutter_chat_types: dependency: transitive description: @@ -329,10 +401,10 @@ packages: dependency: "direct main" description: name: flutter_colorpicker - sha256: "458a6ed8ea480eb16ff892aedb4b7092b2804affd7e046591fb03127e8d8ef8b" + sha256: "969de5f6f9e2a570ac660fb7b501551451ea2a1ab9e2097e89475f60e07816ea" url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.1.0" flutter_driver: dependency: transitive description: flutter @@ -457,6 +529,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.5" + gradient_widgets: + dependency: transitive + description: + name: gradient_widgets + sha256: d337a8a329da9aefa50b82155d11875c1b9992982f65d15c805aa057a2670536 + url: "https://pub.dev" + source: hosted + version: "0.6.0" hive: dependency: transitive description: @@ -610,26 +690,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" linkify: dependency: transitive description: @@ -682,10 +762,10 @@ packages: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" mime: dependency: transitive description: @@ -718,6 +798,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + octo_image: + dependency: transitive + description: + name: octo_image + sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" open_file: dependency: transitive description: @@ -902,6 +990,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.5" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" + url: "https://pub.dev" + source: hosted + version: "0.27.7" shared_preferences: dependency: "direct main" description: @@ -1027,6 +1123,22 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.0" + sqflite: + dependency: transitive + description: + name: sqflite + sha256: a43e5a27235518c03ca238e7b4732cf35eabe863a369ceba6cbefa537a66f16d + url: "https://pub.dev" + source: hosted + version: "2.3.3+1" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4" + url: "https://pub.dev" + source: hosted + version: "2.5.4" stack_trace: dependency: transitive description: @@ -1043,6 +1155,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" string_scanner: dependency: transitive description: @@ -1067,6 +1187,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.1" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" + url: "https://pub.dev" + source: hosted + version: "3.1.0+1" term_glyph: dependency: transitive description: @@ -1079,10 +1207,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" typed_data: dependency: transitive description: @@ -1179,6 +1307,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.3.3" + validators: + dependency: transitive + description: + name: validators + sha256: "884515951f831a9c669a41ed6c4d3c61c2a0e8ec6bca761a4480b28e99cecf5d" + url: "https://pub.dev" + source: hosted + version: "3.0.0" value_layout_builder: dependency: transitive description: @@ -1267,14 +1403,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.3" + visibility_detector: + dependency: transitive + description: + name: visibility_detector + sha256: dd5cc11e13494f432d15939c3aa8ae76844c42b723398643ce9addb88a5ed420 + url: "https://pub.dev" + source: hosted + version: "0.4.0+2" vm_service: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.1" wakelock_plus: dependency: transitive description: @@ -1340,5 +1484,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.3.0 <4.0.0" - flutter: ">=3.13.0" + dart: ">=3.4.0 <4.0.0" + flutter: ">=3.22.0" diff --git a/lib/v4/core/theme.dart b/lib/v4/core/theme.dart index c1e1d636..21ce6f47 100644 --- a/lib/v4/core/theme.dart +++ b/lib/v4/core/theme.dart @@ -1,5 +1,7 @@ import 'dart:ui'; +import 'package:flutter/material.dart'; + class AmityTheme { final Color primaryColor; final Color secondaryColor; @@ -105,3 +107,23 @@ final darkTheme = AmityTheme( alertColor: const Color(0xFFFA4D30), backgroundColor: const Color(0xFF191919), ); + +enum ColorBlendingOption { + shade1(25), + shade2(40), + shade3(50), + shade4(75); + + final double luminance; + const ColorBlendingOption(this.luminance); +} + +extension ColorBlending on Color { + Color blend(ColorBlendingOption option) { + final hslColor = HSLColor.fromColor(this); + final blendedHslColor = hslColor.withLightness( + (hslColor.lightness + option.luminance / 100).clamp(0.0, 1.0), + ); + return blendedHslColor.toColor(); + } +} diff --git a/lib/v4/core/toast/amity_uikit_toast.dart b/lib/v4/core/toast/amity_uikit_toast.dart index 2105cad7..f9cb68c2 100644 --- a/lib/v4/core/toast/amity_uikit_toast.dart +++ b/lib/v4/core/toast/amity_uikit_toast.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; -enum AmityToastIcon { success, warning } +enum AmityToastIcon { success, warning, loading } class AmityToast extends BaseElement { AmityToast({super.key, required super.elementId}); @@ -23,13 +23,24 @@ class AmityToast extends BaseElement { ScaffoldMessenger.of(context) ..clearSnackBars() ..showSnackBar(SnackBar( - content: renderToastContent(message: state.message, icon: state.icon), + content: + renderToastContent(message: state.message, icon: state.icon), elevation: 0, backgroundColor: const Color(0x00000000), onVisible: () => Future.delayed(const Duration(seconds: 5), () { context.read().add(AmityToastDismiss()); }), )); + } else if (state.style == AmityToastStyle.loading) { + ScaffoldMessenger.of(context) + ..clearSnackBars() + ..showSnackBar(SnackBar( + content: + renderToastContent(message: state.message, icon: state.icon), + elevation: 0, + backgroundColor: const Color(0x00000000), + duration: const Duration(days: 1), + )); } else { ScaffoldMessenger.of(context).hideCurrentSnackBar(); } @@ -40,10 +51,14 @@ class AmityToast extends BaseElement { Widget renderToastContent({required String message, AmityToastIcon? icon}) { final toastIcon = icon ?? AmityToastIcon.warning; String iconAsset; + var shouldRotate = false; if (toastIcon == AmityToastIcon.success) { iconAsset = 'assets/Icons/amity_ic_toast_success.svg'; } else if (toastIcon == AmityToastIcon.warning) { iconAsset = 'assets/Icons/amity_ic_toast_warning.svg'; + } else if (toastIcon == AmityToastIcon.loading) { + iconAsset = 'assets/Icons/amity_ic_toast_loading.svg'; + shouldRotate = true; } else { iconAsset = 'assets/Icons/amity_ic_toast_warning.svg'; } @@ -95,12 +110,7 @@ class AmityToast extends BaseElement { SizedBox( width: 24, height: 24, - child: SvgPicture.asset( - iconAsset, - package: 'amity_uikit_beta_service', - width: 24, - height: 20, - ), + child: RotatingSvgPicture(iconAsset: iconAsset, shouldRotate: shouldRotate), ), ], ), @@ -140,3 +150,68 @@ class AmityToast extends BaseElement { ); } } + +class RotatingSvgPicture extends StatefulWidget { + final String iconAsset; + final bool shouldRotate; + + RotatingSvgPicture({required this.iconAsset, required this.shouldRotate}); + + @override + _RotatingSvgPictureState createState() => + _RotatingSvgPictureState(shouldRotate: shouldRotate); +} + +class _RotatingSvgPictureState extends State + with SingleTickerProviderStateMixin { + final bool shouldRotate; + + _RotatingSvgPictureState({required this.shouldRotate}); + + late AnimationController _controller; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + vsync: this, + duration: const Duration(seconds: 2), + )..repeat(); // Repeat the animation indefinitely + } + + @override + void dispose() { + _controller.dispose(); // Dispose the controller when the widget is removed + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (shouldRotate) { + return RotationTransition( + turns: _controller, + child: SizedBox( + width: 24, + height: 24, + child: SvgPicture.asset( + widget.iconAsset, + package: 'amity_uikit_beta_service', + width: 24, + height: 20, + ), + ), + ); + } else { + return SizedBox( + width: 24, + height: 24, + child: SvgPicture.asset( + widget.iconAsset, + package: 'amity_uikit_beta_service', + width: 24, + height: 20, + ), + ); + } + } +} diff --git a/lib/v4/core/toast/bloc/amity_uikit_toast_bloc.dart b/lib/v4/core/toast/bloc/amity_uikit_toast_bloc.dart index 989f4113..3ed28087 100644 --- a/lib/v4/core/toast/bloc/amity_uikit_toast_bloc.dart +++ b/lib/v4/core/toast/bloc/amity_uikit_toast_bloc.dart @@ -7,25 +7,42 @@ import 'package:flutter_bloc/flutter_bloc.dart'; part 'amity_uikit_toast_events.dart'; part 'amity_uikit_toast_state.dart'; -class AmityToastBloc - extends Bloc { +class AmityToastBloc extends Bloc { AmityToastBloc() : super(const AmityToastState( message: "", style: AmityToastStyle.hidden, )) { + on( + (event, emit) => emit( + AmityToastState( + message: event.message, + style: AmityToastStyle.short, + icon: event.icon, + key: UniqueKey(), + ), + ), + ); - on((event, emit) => emit(AmityToastState( - message: event.message, - style: AmityToastStyle.short, - icon: event.icon, - key: UniqueKey(), - ))); + on( + (event, emit) => emit( + const AmityToastState( + message: "", + style: AmityToastStyle.hidden, + key: null, + ), + ), + ); - on((event, emit) => emit(const AmityToastState( - message: "", - style: AmityToastStyle.hidden, - key: null, - ))); - } + on((event, emit) { + emit( + AmityToastState( + message: event.message, + style: AmityToastStyle.loading, + icon: event.icon, + key: UniqueKey(), + ), + ); + }); + } } diff --git a/lib/v4/core/toast/bloc/amity_uikit_toast_events.dart b/lib/v4/core/toast/bloc/amity_uikit_toast_events.dart index 44551291..635424ab 100644 --- a/lib/v4/core/toast/bloc/amity_uikit_toast_events.dart +++ b/lib/v4/core/toast/bloc/amity_uikit_toast_events.dart @@ -26,4 +26,16 @@ class AmityUIKitToastLong extends AmityToastEvent { List get props => [message]; } + +class AmityToastLoading extends AmityToastEvent { + final String message; + final AmityToastIcon? icon; + + const AmityToastLoading({required this.message, required this.icon}); + + @override + List get props => [message, icon ?? '']; +} + + class AmityToastDismiss extends AmityToastEvent {} \ No newline at end of file diff --git a/lib/v4/core/toast/bloc/amity_uikit_toast_state.dart b/lib/v4/core/toast/bloc/amity_uikit_toast_state.dart index ba02b6a0..3ae13e4c 100644 --- a/lib/v4/core/toast/bloc/amity_uikit_toast_state.dart +++ b/lib/v4/core/toast/bloc/amity_uikit_toast_state.dart @@ -1,6 +1,6 @@ part of 'amity_uikit_toast_bloc.dart'; -enum AmityToastStyle { hidden, short, long, persistent } +enum AmityToastStyle { hidden, short, long, persistent, loading } // Short is 5 seconds toast, long is 10 seconds toast, persistent is toast that will not disappear until calling dismiss it class AmityToastState extends Equatable { diff --git a/lib/v4/social/globalfeed/amity_global_feed_component.dart b/lib/v4/social/globalfeed/amity_global_feed_component.dart index 51406039..d67feaf6 100644 --- a/lib/v4/social/globalfeed/amity_global_feed_component.dart +++ b/lib/v4/social/globalfeed/amity_global_feed_component.dart @@ -79,12 +79,12 @@ class AmityGlobalFeedComponent extends NewBaseComponent { action: AmityPostAction( onAddReaction: (String) {}, onRemoveReaction: (String) {}, - onPostDeleted: (String postId) { + onPostDeleted: (AmityPost post) { context .read() .add( GlobalFeedReloadThePost( - postId: postId)); + post: post)); }, onPostUpdated: (post) {}, )), diff --git a/lib/v4/social/globalfeed/bloc/global_feed_bloc.dart b/lib/v4/social/globalfeed/bloc/global_feed_bloc.dart index 4c661dd1..be1ce880 100644 --- a/lib/v4/social/globalfeed/bloc/global_feed_bloc.dart +++ b/lib/v4/social/globalfeed/bloc/global_feed_bloc.dart @@ -9,6 +9,9 @@ part 'global_feed_state.dart'; class GlobalFeedBloc extends Bloc { late PagingController _controller; + late List posts = []; + late List localCreatedPost = []; + final int pageSize = 20; GlobalFeedBloc() : super(const GlobalFeedState( @@ -28,20 +31,39 @@ class GlobalFeedBloc extends Bloc { emit(state.copyWith(isFetching: true)); } else if (_controller.error == null) { // Distinct post list - List _distinctList = _controller.loadedItems; - final postIds = _distinctList.map((post) => post.postId).toSet(); - _distinctList.retainWhere((post) => postIds.remove(post.postId)); - emit(state.copyWith( - list: _controller.loadedItems, - hasMoreItems: _controller.hasMoreItems, - isFetching: _controller.isFetching)); + posts.addAll(_controller.loadedItems); + + add(GlobalFeedNotify(posts: [])); } }, ); + on((event, emit) async { + List allPost = []; + allPost.addAll(localCreatedPost); + + allPost.addAll(posts); + + final postIds = allPost.map((post) => post.postId).toSet(); + allPost.retainWhere((post) => postIds.remove(post.postId)); + + emit(state.copyWith( + list: allPost, + hasMoreItems: _controller.hasMoreItems, + isFetching: _controller.isFetching)); + }); + + on((event, emit) async { + final post = event.post; + localCreatedPost.insert(0, post); + add(GlobalFeedNotify(posts: [])); + }); + on((event, emit) async { _controller.reset(); _controller.fetchNextPage(); + localCreatedPost.clear(); + posts.clear(); }); on((event, emit) async { @@ -59,8 +81,7 @@ class GlobalFeedBloc extends Bloc { }); on((event, emit) async { - var updatedPost = - await AmitySocialClient.newPostRepository().getPost(event.postId); + var updatedPost = event.post; List updatedList = []; for (var element in state.list) { if (element.postId == updatedPost.postId) { @@ -69,6 +90,7 @@ class GlobalFeedBloc extends Bloc { updatedList.add(element); } } + emit(state.copyWith(list: [])); emit(state.copyWith(list: updatedList)); }); } diff --git a/lib/v4/social/globalfeed/bloc/global_feed_event.dart b/lib/v4/social/globalfeed/bloc/global_feed_event.dart index 72a4f262..591edd1c 100644 --- a/lib/v4/social/globalfeed/bloc/global_feed_event.dart +++ b/lib/v4/social/globalfeed/bloc/global_feed_event.dart @@ -5,6 +5,18 @@ abstract class GlobalFeedEvent {} class GlobalFeedInit extends GlobalFeedEvent {} +class GlobalFeedNotify extends GlobalFeedEvent { + final List posts; + + GlobalFeedNotify({required this.posts}); +} + +class GlobalFeedAddLocalPost extends GlobalFeedEvent { + final AmityPost post; + + GlobalFeedAddLocalPost({required this.post}); +} + class GlobalFeedFetch extends GlobalFeedEvent {} class GlobalFeedFetched extends GlobalFeedEvent { final List list; @@ -28,7 +40,7 @@ class GlobalFeedReactToPost extends GlobalFeedEvent { } class GlobalFeedReloadThePost extends GlobalFeedEvent { - final String postId; + final AmityPost post; - GlobalFeedReloadThePost({ required this.postId }); + GlobalFeedReloadThePost({ required this.post }); } \ No newline at end of file diff --git a/lib/v4/social/my_community/my_community_component.dart b/lib/v4/social/my_community/my_community_component.dart index cfb3a89d..aa0431c7 100644 --- a/lib/v4/social/my_community/my_community_component.dart +++ b/lib/v4/social/my_community/my_community_component.dart @@ -136,7 +136,7 @@ class AmityMyCommunitiesComponent extends NewBaseComponent { child: Text( community.displayName ?? '', style: TextStyle( - fontSize: 17, + fontSize: 15, fontWeight: FontWeight.bold, color: theme.baseColor, ), diff --git a/lib/v4/social/post/common/post_action.dart b/lib/v4/social/post/common/post_action.dart index 74692ce2..95dc17b7 100644 --- a/lib/v4/social/post/common/post_action.dart +++ b/lib/v4/social/post/common/post_action.dart @@ -3,7 +3,7 @@ import 'package:amity_sdk/amity_sdk.dart'; class AmityPostAction { final Function(String) onAddReaction; final Function(String) onRemoveReaction; - final Function(String) onPostDeleted; + final Function(AmityPost) onPostDeleted; final Function(AmityPost) onPostUpdated; const AmityPostAction({ @@ -16,7 +16,7 @@ class AmityPostAction { AmityPostAction copyWith({ Function(String)? onAddReaction, Function(String)? onRemoveReaction, - Function(String)? onPostDeleted, + Function(AmityPost)? onPostDeleted, Function(AmityPost)? onPostUpdated, }) { return AmityPostAction( diff --git a/lib/v4/social/post/common/post_display_name.dart b/lib/v4/social/post/common/post_display_name.dart index 95050d00..7117af7c 100644 --- a/lib/v4/social/post/common/post_display_name.dart +++ b/lib/v4/social/post/common/post_display_name.dart @@ -12,14 +12,17 @@ class PostDisplayName extends StatelessWidget { final AmityPost post; final AmityThemeColor theme; - const PostDisplayName({Key? key, required this.post, required this.theme}) : super(key: key); + const PostDisplayName({Key? key, required this.post, required this.theme}) + : super(key: key); @override Widget build(BuildContext context) { var isModerator = false; if (post.target is CommunityTarget) { var roles = (post.target as CommunityTarget).postedCommunityMember?.roles; - if (roles != null && (roles.contains("moderator") || roles.contains("community-moderator"))) { + if (roles != null && + (roles.contains("moderator") || + roles.contains("community-moderator"))) { isModerator = true; } } @@ -44,7 +47,8 @@ class PostDisplayName extends StatelessWidget { (post.target as UserTarget).targetUserId != post.postedUserId))) ? [ - IntrinsicWidth(child: DisplayName(context, post.postedUser)), + IntrinsicWidth( + child: DisplayName(context, post.postedUser)), Expanded(child: PostTarget(context, post.target!)), ] : [Expanded(child: DisplayName(context, post.postedUser))], @@ -54,12 +58,11 @@ class PostDisplayName extends StatelessWidget { if (isModerator) const CommunityModeratorBadge(), if (isModerator) Container( - padding: - const EdgeInsets.only(left: 2), + padding: const EdgeInsets.only(left: 2), child: Text( "• ", style: TextStyle( - color: theme.baseColor, + color: theme.baseColorShade2, fontSize: 13, fontWeight: FontWeight.w400, ), @@ -68,8 +71,8 @@ class PostDisplayName extends StatelessWidget { alignment: Alignment.topLeft, child: Text( timestampText, - style: TextStyle( - color: theme.baseColor, + style: TextStyle( + color: theme.baseColorShade2, fontSize: 13, fontWeight: FontWeight.w400, ), diff --git a/lib/v4/social/post/common/post_header.dart b/lib/v4/social/post/common/post_header.dart index 2dced207..06c88f96 100644 --- a/lib/v4/social/post/common/post_header.dart +++ b/lib/v4/social/post/common/post_header.dart @@ -4,9 +4,8 @@ import 'package:amity_uikit_beta_service/v4/core/toast/bloc/amity_uikit_toast_bl import 'package:amity_uikit_beta_service/v4/social/post/common/post_action.dart'; import 'package:amity_uikit_beta_service/v4/social/post/common/post_display_name.dart'; import 'package:amity_uikit_beta_service/v4/social/post/post_item/bloc/post_item_bloc.dart'; +import 'package:amity_uikit_beta_service/v4/social/post_composer_page/post_composer_page.dart'; import 'package:amity_uikit_beta_service/v4/utils/network_image.dart'; -import 'package:amity_uikit_beta_service/view/UIKit/social/community_setting/posts/edit_post_page.dart'; -import 'package:amity_uikit_beta_service/view/social/global_feed.dart'; import 'package:amity_uikit_beta_service/viewmodel/edit_post_viewmodel.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -83,17 +82,32 @@ class AmityPostHeader extends StatelessWidget { ); } - void showPostAction(BuildContext context, AmityPost post) { + void showPostAction(BuildContext context, AmityPost post) async { final currentUserId = AmityCoreClient.getUserId(); + var isModerator = false; + + var postTarget = post.target; + if (postTarget is CommunityTarget) { + var roles = await AmitySocialClient.newCommunityRepository() + .getCurrentUserRoles(postTarget.targetCommunityId ?? ""); + + if (roles != null && + (roles.contains("moderator") || + roles.contains("community-moderator"))) { + isModerator = true; + } + } + if (post.postedUserId == currentUserId) { - showPostModerationAction(context, post); + showPostOwnerAction(context, post, theme, isModerator); } else { - showPostGeneralAction(context, post); + showPostGeneralAction(context, post, isModerator); } } - void showPostGeneralAction(BuildContext context, AmityPost post) { + void showPostGeneralAction( + BuildContext context, AmityPost post, bool isModerator) { onReport() => { context.read().add(PostItemFlag( post: post, toastBloc: context.read())) @@ -102,6 +116,21 @@ class AmityPostHeader extends StatelessWidget { context.read().add(PostItemUnFlag( post: post, toastBloc: context.read())) }; + + onDelete() { + context + .read() + .add(PostItemDelete(post: post, action: action)); + } + + double height = 0; + double baseHeight = 80; + double itemHeight = 48; + if (isModerator) { + itemHeight += 48; + } + height = baseHeight + itemHeight; + showModalBottomSheet( context: context, shape: const RoundedRectangleBorder( @@ -112,7 +141,7 @@ class AmityPostHeader extends StatelessWidget { ), builder: (BuildContext context) { return SizedBox( - height: 140, + height: height, child: Column( children: [ Container( @@ -201,7 +230,7 @@ class AmityPostHeader extends StatelessWidget { ), const SizedBox(width: 12), const Text( - 'UnReport post', + 'Unreport post', style: TextStyle( color: Color(0xFF292B32), fontSize: 15, @@ -212,20 +241,23 @@ class AmityPostHeader extends StatelessWidget { ), ), ), + if (isModerator) _getDeletetedPost(context, post, onDelete) ], ), ); }); } - void showPostModerationAction(BuildContext context, AmityPost post) { + void showPostOwnerAction(BuildContext context, AmityPost post, + AmityThemeColor theme, bool isModerator) { + final editOption = AmityPostComposerOptions.editOptions(post: post); + onEdit() => { Navigator.of(context).push(MaterialPageRoute( + fullscreenDialog: true, builder: (context) => ChangeNotifierProvider( create: (context) => EditPostVM(), - child: AmityEditPostScreen( - amityPost: post, - )))) + child: PostComposerPage(options: editOption)))) }; onDelete() { context @@ -233,6 +265,11 @@ class AmityPostHeader extends StatelessWidget { .add(PostItemDelete(post: post, action: action)); } + double height = 0; + double baseHeight = 80; + double itemsHeight = 96; + height = baseHeight + itemsHeight; + showModalBottomSheet( context: context, shape: const RoundedRectangleBorder( @@ -243,7 +280,7 @@ class AmityPostHeader extends StatelessWidget { ), builder: (BuildContext context) { return SizedBox( - height: 196, + height: height, child: Column( children: [ Container( @@ -304,81 +341,86 @@ class AmityPostHeader extends StatelessWidget { ), ), ), - GestureDetector( - onTap: () { - Navigator.pop(context); - showDialog( - context: context, - builder: (BuildContext context) { - return CupertinoAlertDialog( - title: const Text("Delete post"), - content: const Text( - "This post will be permanently deleted."), - actions: [ - CupertinoDialogAction( - child: const Text("Cancel", - style: TextStyle( - color: Color(0xFF007AFF), - fontSize: 17, - fontWeight: FontWeight.w400, - )), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - CupertinoDialogAction( - child: const Text( - "Delete", - style: TextStyle( - color: Color(0xFFFA4D30), - fontSize: 17, - fontWeight: FontWeight.w600, - ), - ), - onPressed: () { - Navigator.of(context).pop(); - onDelete(); - }, - ), - ], - ); - }, - ); + _getDeletetedPost(context, post, onDelete), + ], + ), + ); + }); + } + + Widget _getDeletetedPost( + BuildContext context, AmityPost post, Function onDelete) { + return GestureDetector( + onTap: () { + Navigator.pop(context); + showDialog( + context: context, + builder: (BuildContext context) { + return CupertinoAlertDialog( + title: const Text("Delete post"), + content: const Text("This post will be permanently deleted."), + actions: [ + CupertinoDialogAction( + child: const Text("Cancel", + style: TextStyle( + color: Color(0xFF007AFF), + fontSize: 17, + fontWeight: FontWeight.w400, + )), + onPressed: () { + Navigator.of(context).pop(); }, - child: Container( - width: double.infinity, - padding: const EdgeInsets.symmetric( - vertical: 16, horizontal: 20), - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - padding: const EdgeInsets.only(top: 2, bottom: 2), - child: SvgPicture.asset( - 'assets/Icons/amity_ic_delete.svg', - package: 'amity_uikit_beta_service', - width: 24, - height: 20, - ), - ), - const SizedBox(width: 12), - const Text( - 'Delete post', - style: TextStyle( - color: Color(0xFF292B32), - fontSize: 15, - fontWeight: FontWeight.w600, - ), - ), - ], + ), + CupertinoDialogAction( + child: Text( + "Delete", + style: TextStyle( + color: theme.alertColor, + fontSize: 17, + fontWeight: FontWeight.w600, ), ), + onPressed: () { + Navigator.of(context).pop(); + onDelete(); + }, ), ], + ); + }, + ); + }, + child: Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 20), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + padding: const EdgeInsets.only(top: 2, bottom: 2), + child: SvgPicture.asset( + 'assets/Icons/amity_ic_delete.svg', + package: 'amity_uikit_beta_service', + width: 24, + height: 20, + colorFilter: + ColorFilter.mode(theme.alertColor, BlendMode.srcIn), + ), ), - ); - }); + const SizedBox(width: 12), + Text( + 'Delete post', + style: TextStyle( + color: theme.alertColor, + fontSize: 15, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ); } } diff --git a/lib/v4/social/post/post_item/bloc/post_item_bloc.dart b/lib/v4/social/post/post_item/bloc/post_item_bloc.dart index 81b01cb8..98060421 100644 --- a/lib/v4/social/post/post_item/bloc/post_item_bloc.dart +++ b/lib/v4/social/post/post_item/bloc/post_item_bloc.dart @@ -48,8 +48,8 @@ class PostItemBloc extends Bloc { on((event, emit) async { final flag = await event.post.report().flag(); if (flag) { - event.toastBloc.add(AmityToastShort( - message: "Post reported", icon: AmityToastIcon.success)); + event.toastBloc.add(const AmityToastShort( + message: "Post reported.", icon: AmityToastIcon.success)); var updatedPost = await AmitySocialClient.newPostRepository() .getPost(event.post.postId!); emit(PostItemStateLoaded(post: updatedPost)); @@ -59,8 +59,8 @@ class PostItemBloc extends Bloc { on((event, emit) async { final flag = await event.post.report().unflag(); if (flag) { - event.toastBloc.add(AmityToastShort( - message: "Post unreported", icon: AmityToastIcon.success)); + event.toastBloc.add(const AmityToastShort( + message: "Post unreported.", icon: AmityToastIcon.success)); var updatedPost = await AmitySocialClient.newPostRepository() .getPost(event.post.postId!); emit(PostItemStateLoaded(post: updatedPost)); @@ -70,9 +70,9 @@ class PostItemBloc extends Bloc { on((event, emit) async { final delete = await event.post.delete(); if (delete) { - event.action?.onPostDeleted(event.post.postId!); - var updatedPost = await AmitySocialClient.newPostRepository() - .getPost(event.post.postId!); + event.action?.onPostDeleted(event.post); + var updatedPost = event.post; + updatedPost.isDeleted = true; emit(PostItemStateLoaded(post: updatedPost)); } }); diff --git a/lib/v4/social/post/post_item/post_item.dart b/lib/v4/social/post/post_item/post_item.dart index fb6c1dab..952e796a 100644 --- a/lib/v4/social/post/post_item/post_item.dart +++ b/lib/v4/social/post/post_item/post_item.dart @@ -80,7 +80,7 @@ class PostItem extends NewBaseComponent { { context .read() - .add(GlobalFeedReloadThePost(postId: post.postId!)) + .add(GlobalFeedReloadThePost(post: post)) } }, ), diff --git a/lib/v4/social/post_composer_page/bloc/post_composer_bloc.dart b/lib/v4/social/post_composer_page/bloc/post_composer_bloc.dart new file mode 100644 index 00000000..47d0d3bf --- /dev/null +++ b/lib/v4/social/post_composer_page/bloc/post_composer_bloc.dart @@ -0,0 +1,16 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:equatable/equatable.dart'; + +part 'post_composer_events.dart'; +part 'post_composer_state.dart'; + + +class PostComposerBloc extends Bloc { + + PostComposerBloc() : super(const PostComposerState()) { + + on((event, emit) { + emit(PostComposerTextChangeState(text: event.text)); + }); + } +} diff --git a/lib/v4/social/post_composer_page/bloc/post_composer_events.dart b/lib/v4/social/post_composer_page/bloc/post_composer_events.dart new file mode 100644 index 00000000..260e3b5c --- /dev/null +++ b/lib/v4/social/post_composer_page/bloc/post_composer_events.dart @@ -0,0 +1,16 @@ +part of 'post_composer_bloc.dart'; + +abstract class PostComposerEvent extends Equatable { + const PostComposerEvent(); + + @override + List get props => []; +} + +class PostComposerTextChangeEvent extends PostComposerEvent { + final String text; + + const PostComposerTextChangeEvent({ + required this.text, + }); +} diff --git a/lib/v4/social/post_composer_page/bloc/post_composer_state.dart b/lib/v4/social/post_composer_page/bloc/post_composer_state.dart new file mode 100644 index 00000000..55eb305b --- /dev/null +++ b/lib/v4/social/post_composer_page/bloc/post_composer_state.dart @@ -0,0 +1,17 @@ +part of 'post_composer_bloc.dart'; + +class PostComposerState extends Equatable { + const PostComposerState(); + + @override + List get props => []; +} + +class PostComposerTextChangeState extends PostComposerState { + PostComposerTextChangeState({required this.text}); + + final String text; + + @override + List get props => [text]; +} \ No newline at end of file diff --git a/lib/v4/social/post_composer_page/post_composer_page.dart b/lib/v4/social/post_composer_page/post_composer_page.dart new file mode 100644 index 00000000..f8b15474 --- /dev/null +++ b/lib/v4/social/post_composer_page/post_composer_page.dart @@ -0,0 +1,247 @@ +import 'package:amity_sdk/amity_sdk.dart'; +import 'package:amity_uikit_beta_service/v4/core/base_page.dart'; +import 'package:amity_uikit_beta_service/v4/core/theme.dart'; +import 'package:amity_uikit_beta_service/v4/core/toast/amity_uikit_toast.dart'; +import 'package:amity_uikit_beta_service/v4/core/toast/bloc/amity_uikit_toast_bloc.dart'; +import 'package:amity_uikit_beta_service/v4/social/globalfeed/bloc/global_feed_bloc.dart'; +import 'package:amity_uikit_beta_service/v4/social/post_composer_page/bloc/post_composer_bloc.dart'; +import 'package:amity_uikit_beta_service/v4/utils/amity_dialog.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; + +class PostComposerPage extends NewBasePage { + final AmityPostComposerOptions options; + final TextEditingController _controller = TextEditingController(text: ''); + final void Function(bool shouldPopCaller)? onPopRequested; + late String currentPostText = ''; + + PostComposerPage({Key? key, required this.options, this.onPopRequested}) + : super(key: key, pageId: ''); + + @override + Widget buildPage(BuildContext context) { + bool isButtonEnabled = false; + if (options.mode == AmityPostComposerMode.edit && + options.post?.data is TextData) { + currentPostText = (options.post!.data as TextData).text ?? ''; + } + return BlocProvider( + create: (context) => PostComposerBloc(), + child: Builder(builder: (context) { + _initializeController(context); + + return BlocBuilder( + builder: (context, state) { + if (state is PostComposerTextChangeState) { + _controller.text = state.text; + if (currentPostText != state.text) { + isButtonEnabled = true; + } else { + isButtonEnabled = false; + } + } + return Scaffold( + backgroundColor: theme.backgroundColor, + appBar: _buildAppBar(context, isButtonEnabled), + body: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + children: [ + const SizedBox(height: 20), + Flexible( + child: _buildTextField(), + ), + ], + ), + ), + ); + }, + ); + }), + ); + } + + void _initializeController(BuildContext context) { + if (options.mode == AmityPostComposerMode.edit && + options.post?.data is TextData) { + _controller.text = (options.post!.data as TextData).text ?? ''; + } + _controller.addListener(() { + context + .read() + .add(PostComposerTextChangeEvent(text: _controller.text)); + }); + } + + AppBar _buildAppBar(BuildContext context, bool isButtonEnabled) { + return AppBar( + backgroundColor: theme.backgroundColor, + title: Text( + _getPageTitle(), + style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 17), + ), + leading: IconButton( + icon: SvgPicture.asset( + 'assets/Icons/amity_ic_close_button.svg', + package: 'amity_uikit_beta_service', + width: 24, + height: 24, + ), + onPressed: () => _handleClose(context), + ), + centerTitle: true, + actions: [_buildActionButton(context, isButtonEnabled)], + ); + } + + String _getPageTitle() { + if (options.mode == AmityPostComposerMode.edit) { + return "Edit Post"; + } else if (options.targetType == AmityPostTargetType.USER) { + return "My Timeline"; + } else { + return options.community?.displayName ?? ''; + } + } + + Widget _buildActionButton(BuildContext context, bool isButtonEnabled) { + return TextButton( + onPressed: isButtonEnabled ? () => _handleAction(context) : null, + child: Text( + options.mode == AmityPostComposerMode.edit ? "Save" : "Post", + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.w400, + color: isButtonEnabled + ? theme.primaryColor + : theme.primaryColor.blend(ColorBlendingOption.shade2), + ), + ), + ); + } + + void _handleClose(BuildContext context) { + ConfirmationV4Dialog().show( + context: context, + title: 'Discard this post?', + detailText: 'The post will be permanently deleted. It cannot be undone.', + leftButtonText: 'Keep editing', + rightButtonText: 'Discard', + onConfirm: () { + Navigator.pop(context); + onPopRequested?.call(true); + }, + ); + } + + void _handleAction(BuildContext context) { + if (options.mode == AmityPostComposerMode.edit) { + _editPost(context); + } else { + _createPost(context); + } + } + + void _editPost(BuildContext context) { + options.post?.edit().text(_controller.text).build().update().then((post) { + context + .read() + .add(GlobalFeedReloadThePost(post: post)); + Navigator.pop(context); + }).onError((error, stackTrace) { + _showToast(context, "Failed to edit a post", AmityToastIcon.warning); + }); + } + + void _createPost(BuildContext context) { + final targetId = options.targetId; + final postRepo = AmitySocialClient.newPostRepository(); + context.read().add(const AmityToastLoading( + message: "Posting", icon: AmityToastIcon.loading)); + + if (options.targetType == AmityPostTargetType.COMMUNITY && + targetId != null) { + postRepo + .createPost() + .targetCommunity(targetId) + .text(_controller.text) + .post() + .then((post) { + _onPostSuccess(context, post); + }).onError((error, stackTrace) { + _showToast(context, "Failed to create post. Please try again.", + AmityToastIcon.warning); + }); + } else if (targetId == null) { + postRepo + .createPost() + .targetMe() + .text(_controller.text) + .post() + .then((post) { + _onPostSuccess(context, post); + }).onError((error, stackTrace) { + _showToast(context, "Failed to edit post. Please try again.", + AmityToastIcon.warning); + }); + } + } + + void _onPostSuccess(BuildContext context, AmityPost post) { + context.read().add(AmityToastDismiss()); + Future.delayed(const Duration(milliseconds: 500), () { + context.read().add(GlobalFeedAddLocalPost(post: post)); + Navigator.pop(context); + onPopRequested?.call(true); + }); + } + + void _showToast(BuildContext context, String message, AmityToastIcon icon) { + context + .read() + .add(AmityToastShort(message: message, icon: icon)); + } + + Widget _buildTextField() { + return TextFormField( + controller: _controller, + maxLines: null, + textCapitalization: TextCapitalization.sentences, + decoration: InputDecoration( + hintText: "What’s going on...", + border: InputBorder.none, + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + hintStyle: TextStyle(color: theme.baseColorShade3), + ), + ); + } +} + +class AmityPostComposerOptions { + final AmityPostComposerMode mode; + final AmityPost? post; + final String? targetId; + final AmityPostTargetType? targetType; + final AmityCommunity? community; + + AmityPostComposerOptions.editOptions({ + this.mode = AmityPostComposerMode.edit, + required this.post, + }) : targetId = null, + targetType = null, + community = null; + + AmityPostComposerOptions.createOptions({ + this.mode = AmityPostComposerMode.create, + this.targetId, + required this.targetType, + this.community, + }) : post = null; +} + +enum AmityPostComposerMode { + create, + edit, +} diff --git a/lib/v4/social/post_target_selection_page/bloc/post_target_selection_bloc.dart b/lib/v4/social/post_target_selection_page/bloc/post_target_selection_bloc.dart new file mode 100644 index 00000000..259a15ad --- /dev/null +++ b/lib/v4/social/post_target_selection_page/bloc/post_target_selection_bloc.dart @@ -0,0 +1,71 @@ +import 'dart:async'; + +import 'package:amity_sdk/amity_sdk.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:equatable/equatable.dart'; + +part 'post_target_selection_events.dart'; +part 'post_target_selection_state.dart'; + +class PostTargetSelectionBloc + extends Bloc { + final int pageSize = 20; + late CommunityLiveCollection communityLiveCollection; + late StreamSubscription> _subscription; + + PostTargetSelectionBloc() : super(const PostTargetSelectionState()) { + communityLiveCollection = AmitySocialClient.newCommunityRepository() + .getCommunities() + .filter(AmityCommunityFilter.MEMBER) + .sortBy(AmityCommunitySortOption.DISPLAY_NAME) + .getLiveCollection(pageSize: 20); + + _subscription = communityLiveCollection + .getStreamController() + .stream + .listen((communities) async { + if (communityLiveCollection.isFetching == true && communities.isEmpty) { + emit(PostTargetSelectionLoading()); + } else if (communities.isNotEmpty) { + // var state = PostTargetSelectionLoaded( + // list: communities, + // hasMoreItems: communityLiveCollection.hasNextPage(), + // isFetching: communityLiveCollection.isFetching, + // ); + // print("BlocLength ${state.list.length}"); + // add(PostTargetSelectionEventLoaded()); + add(CommunitiesLoadedEvent( + communities: communities, + hasMoreItems: communityLiveCollection.hasNextPage(), + isFetching: communityLiveCollection.isFetching, + )); + // emit(state); + } + }); + + on((event, emit) async { + emit(PostTargetSelectionLoaded( + list: event.communities, + hasMoreItems: event.hasMoreItems, + isFetching: event.isFetching, + )); + }); + + on((event, emit) async { + communityLiveCollection.reset(); + communityLiveCollection.loadNext(); + }); + + on((event, emit) async { + if (communityLiveCollection.hasNextPage()) { + communityLiveCollection.loadNext(); + } + }); + } + + @override + Future close() { + _subscription.cancel(); + return super.close(); + } +} diff --git a/lib/v4/social/post_target_selection_page/bloc/post_target_selection_events.dart b/lib/v4/social/post_target_selection_page/bloc/post_target_selection_events.dart new file mode 100644 index 00000000..3f30e20d --- /dev/null +++ b/lib/v4/social/post_target_selection_page/bloc/post_target_selection_events.dart @@ -0,0 +1,24 @@ +part of 'post_target_selection_bloc.dart'; + +abstract class PostTargetSelectionEvent extends Equatable { + const PostTargetSelectionEvent(); + + @override + List get props => []; +} + +class PostTargetSelectionEventInitial extends PostTargetSelectionEvent {} + +class PostTargetSelectionEventLoadMore extends PostTargetSelectionEvent {} + +class CommunitiesLoadedEvent extends PostTargetSelectionEvent { + final List communities; + final bool hasMoreItems; + final bool isFetching; + + const CommunitiesLoadedEvent({ + required this.communities, + required this.hasMoreItems, + required this.isFetching, + }); +} diff --git a/lib/v4/social/post_target_selection_page/bloc/post_target_selection_state.dart b/lib/v4/social/post_target_selection_page/bloc/post_target_selection_state.dart new file mode 100644 index 00000000..d95696ea --- /dev/null +++ b/lib/v4/social/post_target_selection_page/bloc/post_target_selection_state.dart @@ -0,0 +1,38 @@ +part of 'post_target_selection_bloc.dart'; + +class PostTargetSelectionState extends Equatable { + + const PostTargetSelectionState(); + + @override + List get props => []; + +} + +class PostTargetSelectionLoading extends PostTargetSelectionState {} + +class PostTargetSelectionLoaded extends PostTargetSelectionState { + final List list; + final bool hasMoreItems; + final bool isFetching; + + const PostTargetSelectionLoaded( + {required this.list, + required this.hasMoreItems, + required this.isFetching}); + + PostTargetSelectionLoaded copyWith({ + List? list, + bool? hasMoreItems, + bool? isFetching, + }) { + return PostTargetSelectionLoaded( + list: list ?? this.list, + hasMoreItems: hasMoreItems ?? this.hasMoreItems, + isFetching: isFetching ?? this.isFetching, + ); + } + + @override + List get props => [list, hasMoreItems, isFetching]; +} \ No newline at end of file diff --git a/lib/v4/social/post_target_selection_page/post_target_selection_page.dart b/lib/v4/social/post_target_selection_page/post_target_selection_page.dart new file mode 100644 index 00000000..ccc80fef --- /dev/null +++ b/lib/v4/social/post_target_selection_page/post_target_selection_page.dart @@ -0,0 +1,367 @@ +import 'package:amity_sdk/amity_sdk.dart'; +import 'package:amity_uikit_beta_service/v4/core/base_page.dart'; +import 'package:amity_uikit_beta_service/v4/social/my_community/my_community_component.dart'; +import 'package:amity_uikit_beta_service/v4/social/post_composer_page/post_composer_page.dart'; +import 'package:amity_uikit_beta_service/v4/social/post_target_selection_page/bloc/post_target_selection_bloc.dart'; +import 'package:amity_uikit_beta_service/v4/utils/Shimmer.dart'; +import 'package:amity_uikit_beta_service/v4/utils/network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; + +class PostTargetSelectionPage extends NewBasePage { + PostTargetSelectionPage({super.key, required super.pageId}); + final ScrollController scrollController = ScrollController(); + + @override + Widget buildPage(BuildContext context) { + return BlocProvider( + create: (context) => PostTargetSelectionBloc(), + child: Builder(builder: (context) { + context + .read() + .add(PostTargetSelectionEventInitial()); + + scrollController.addListener( + () { + if (scrollController.position.pixels == + scrollController.position.maxScrollExtent) { + context + .read() + .add(PostTargetSelectionEventLoadMore()); + } + }, + ); + + return BlocBuilder( + builder: (context, state) { + return Scaffold( + backgroundColor: theme.backgroundColor, + appBar: AppBar( + backgroundColor: theme.backgroundColor, + title: const Text( + 'Post to', + style: TextStyle(fontWeight: FontWeight.w600, fontSize: 17), + ), + leading: IconButton( + icon: SvgPicture.asset( + 'assets/Icons/amity_ic_close_button.svg', + package: 'amity_uikit_beta_service', + width: 24, + height: 24, + ), + onPressed: () { + Navigator.pop(context); + // Handle the close action + }, + ), + centerTitle: true, + ), + body: SingleChildScrollView( + controller: scrollController, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListTile( + onTap: () { + final createOptions = + AmityPostComposerOptions.createOptions( + targetType: AmityPostTargetType.USER); + // Navigate or perform action based on 'Global Feed' tap + Navigator.of(context).push( + PageRouteBuilder( + transitionsBuilder: (context, animation, + secondaryAnimation, child) { + const begin = Offset(1.0, 0.0); + const end = Offset.zero; + const curve = Curves.ease; + + final tween = Tween(begin: begin, end: end); + final curvedAnimation = CurvedAnimation( + parent: animation, + curve: curve, + ); + + return SlideTransition( + position: tween.animate(curvedAnimation), + child: child, + ); + }, + reverseTransitionDuration: Duration.zero, + pageBuilder: + (context, animation, secondaryAnimation) => + PopScope( + canPop: true, + child: PostComposerPage( + options: createOptions, + onPopRequested: (shouldPopCaller) { + if (shouldPopCaller) { + Navigator.of(context).pop(); + } + }, + ), + ), + ), + ); + }, + leading: SizedBox( + width: 40, + height: 40, + child: ClipRRect( + borderRadius: BorderRadius.circular(40), + child: AmityNetworkImage( + imageUrl: + AmityCoreClient.getCurrentUser().avatarUrl, + placeHolderPath: + "assets/Icons/amity_ic_user_avatar_placeholder.svg"), + ), + ), + title: const Text('My timeline', + style: TextStyle( + fontWeight: FontWeight.w600, fontSize: 15)), + ), + const SizedBox( + height: 8, + ), + Divider( + color: theme.baseColorShade4, + thickness: 1, + indent: 16, + endIndent: 16, + height: 0, + ), + const SizedBox( + height: 23, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Text( + 'My Communities', + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.w400, + color: theme.baseColorShade3, + ), + ), + ), + communityRow(context, state), + ], + ), + ), + ); + }, + ); + }), + ); + } + + Widget communityRow(BuildContext context, PostTargetSelectionState state) { + if (state is PostTargetSelectionLoading) { + return skeletonList(); + } else if (state is PostTargetSelectionLoaded) { + final communities = state.list; + + return ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + padding: const EdgeInsets.symmetric(vertical: 16), + itemCount: communities.length, + itemBuilder: (context, index) { + return SizedBox( + width: double.infinity, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + getCommunityRow( + context, + communities[index], + ) + ], + ), + ); + }, + ); + } else { + return Container(); + } + } + + Widget getCommunityRow(BuildContext context, AmityCommunity community) { + return ListTile( + onTap: () { + final createOptions = AmityPostComposerOptions.createOptions( + targetId: community.communityId, + community: community, + targetType: AmityPostTargetType.COMMUNITY); + // Navigate or perform action based on 'Global Feed' tap + Navigator.of(context).push( + PageRouteBuilder( + transitionsBuilder: + (context, animation, secondaryAnimation, child) { + const begin = Offset(1.0, 0.0); + const end = Offset.zero; + const curve = Curves.ease; + + final tween = Tween(begin: begin, end: end); + final curvedAnimation = CurvedAnimation( + parent: animation, + curve: curve, + ); + + return SlideTransition( + position: tween.animate(curvedAnimation), + child: child, + ); + }, + reverseTransitionDuration: Duration.zero, + pageBuilder: (context, animation, secondaryAnimation) => PopScope( + canPop: true, + child: PostComposerPage( + options: createOptions, + onPopRequested: (shouldPopCaller) { + if (shouldPopCaller) { + Navigator.of(context).pop(); + } + }, + ), + ), + ), + ); + }, + leading: ClipRRect( + borderRadius: BorderRadius.circular(40), + child: SizedBox( + width: 40, + height: 40, + child: CommunityImageAvatarElement( + avatarUrl: community.avatarImage?.fileUrl, elementId: ''), + ), + ), + title: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + if (!(community.isPublic ?? false)) + SizedBox( + width: 20, + height: 20, + child: AmityPrivateBadgeElement(), + ), + Flexible( + child: Text( + community.displayName ?? '', + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + ), + overflow: TextOverflow.ellipsis, + ), + ), + if (community.isOfficial ?? true) + SizedBox( + width: 20, + height: 20, + child: AmityOfficialBadgeElement(), + ), + const SizedBox(width: 16), + ], + ), + ], + ), + ), + ], + ), + ); + } + + Widget skeletonList() { + return Container( + decoration: BoxDecoration(color: theme.backgroundColor), + child: Column(children: [ + Container( + alignment: Alignment.topCenter, + child: Shimmer( + linearGradient: configProvider.getShimmerGradient(), + child: ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + separatorBuilder: (context, index) { + return Divider( + color: theme.baseColorShade4, + thickness: 0.5, + indent: 16, + endIndent: 16, + height: 25, + ); + }, + itemBuilder: (context, index) { + return SizedBox( + width: double.infinity, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ShimmerLoading( + isLoading: true, + child: skeletonRow(), + ), + ], + ), + ); + }, + itemCount: 5, + ), + ), + ), + ]), + ); + } + + Widget skeletonRow() { + return SizedBox( + height: 56, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + width: 64, + height: 56, + padding: + const EdgeInsets.only(top: 8, left: 16, right: 8, bottom: 8), + child: Container( + width: 40, + height: 40, + decoration: ShapeDecoration( + color: Colors.black, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(40), + ), + ), + ), + ), + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + const SizedBox(height: 14.0), + Container( + width: 180, + height: 8, + decoration: BoxDecoration( + color: Colors.black, + borderRadius: BorderRadius.circular(16), + ), + ), + ]), + ], + ), + ); + } +} diff --git a/lib/v4/social/social_home_page/create_post_menu_component.dart b/lib/v4/social/social_home_page/create_post_menu_component.dart new file mode 100644 index 00000000..7fc8e854 --- /dev/null +++ b/lib/v4/social/social_home_page/create_post_menu_component.dart @@ -0,0 +1,88 @@ +// Define the PopupMenu class +import 'package:amity_uikit_beta_service/v4/core/base_component.dart'; +import 'package:amity_uikit_beta_service/v4/social/post_target_selection_page/post_target_selection_page.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; + +class AmityCreatePostMenuComponent extends NewBaseComponent { + AmityCreatePostMenuComponent({Key? key, String? pageId}) + : super(key: key, pageId: pageId, componentId: 'componentId'); + + @override + Widget buildComponent(BuildContext context) { + return Row( + children: [ + Container( + width: 32, + height: 32, + decoration: BoxDecoration( + color: theme.baseColorShade4, + shape: BoxShape.circle, // Makes the container a circle + ), + child: PopupMenuButton( + color: theme.backgroundColor, + surfaceTintColor: theme.backgroundColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 3, + offset: const Offset(-18, 40), + icon: SvgPicture.asset( + "assets/Icons/amity_ic_post_creation_button.svg", + package: 'amity_uikit_beta_service'), + padding: const EdgeInsets.all(5), + onSelected: (int result) { + Navigator.of(context).push( + MaterialPageRoute( + fullscreenDialog: true, + builder: (context) => PopScope( + canPop: true, + child: PostTargetSelectionPage( + pageId: '', + ), + ), + ), + ); + }, + itemBuilder: (BuildContext context) => >[ + PopupMenuItem( + value: 1, + child: getMenu( + text: "Post", iconPath: "amity_ic_create_post_button.svg"), + ), + // PopupMenuItem( + // value: 2, + // child: getMenu( + // text: "Story", iconPath: "amity_ic_create_post_button.svg"), + // ), + ], + ), + ), + const SizedBox( + width: 16, + ), + ], + ); + } + + Widget getMenu({required String text, required String iconPath}) { + return SizedBox( + width: 200, + child: Row( + children: [ + const SizedBox( + width: 8, + ), + SvgPicture.asset("assets/Icons/$iconPath", + package: 'amity_uikit_beta_service'), + const SizedBox( + width: 8, + ), + Expanded( + child: Text(text), + ), + ], + ), + ); + } +} diff --git a/lib/v4/social/social_home_page/social_home_top_navigation_component.dart b/lib/v4/social/social_home_page/social_home_top_navigation_component.dart index 1d5f9546..1e67d30d 100644 --- a/lib/v4/social/social_home_page/social_home_top_navigation_component.dart +++ b/lib/v4/social/social_home_page/social_home_top_navigation_component.dart @@ -1,8 +1,6 @@ -import 'package:amity_uikit_beta_service/v4/utils/config_provider.dart'; import 'package:amity_uikit_beta_service/v4/core/base_component.dart'; -import 'package:amity_uikit_beta_service/v4/social/story/create/amity_create_story_page.dart'; +import 'package:amity_uikit_beta_service/v4/social/social_home_page/create_post_menu_component.dart'; import 'package:flutter/material.dart'; -import 'package:amity_uikit_beta_service/view/UIKit/social/post_target_page.dart'; import 'package:amity_uikit_beta_service/view/UIKit/social/search_communities.dart'; import 'package:flutter_svg/svg.dart'; @@ -44,22 +42,8 @@ class AmitySocialHomeTopNavigationComponent extends NewBaseComponent { ); }, ), - IconButton( - icon: SvgPicture.asset( - 'assets/Icons/amity_ic_create_post_button.svg', - package: 'amity_uikit_beta_service', - width: 32, - height: 32, - ), - onPressed: () { - // V3 action - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => const Scaffold(body: PostToPage()), - ), - ); - }, - ), + + AmityCreatePostMenuComponent(), ], iconTheme: const IconThemeData(color: Colors.black), ); diff --git a/lib/v4/utils/amity_dialog.dart b/lib/v4/utils/amity_dialog.dart new file mode 100644 index 00000000..7bf58b41 --- /dev/null +++ b/lib/v4/utils/amity_dialog.dart @@ -0,0 +1,161 @@ +import 'dart:io'; + +import 'package:amity_uikit_beta_service/components/custom_dialog.dart'; +import 'package:amity_uikit_beta_service/utils/navigation_key.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; + +class AmityV4Dialog { + var isShowDialog = true; + + Future showAlertErrorDialog({ + required String title, + required String message, + }) async { + bool isBarrierDismissible() { + return title.toLowerCase().contains("error"); + } + + if (isShowDialog) { + final BuildContext? context = + NavigationService.navigatorKey.currentContext; + if (context != null) { + if (Platform.isIOS) { + // Use CupertinoAlertDialog for iOS + await showCupertinoDialog( + barrierDismissible: isBarrierDismissible(), + context: context, + builder: (BuildContext context) { + return CupertinoAlertDialog( + title: Text(title), + content: Text(message), + actions: [ + CupertinoDialogAction( + child: const Text('OK'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); + } else { + // Use AlertDialog for Android and other platforms + await showDialog( + barrierDismissible: isBarrierDismissible(), + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text(title), + content: Text(message), + actions: [ + TextButton( + child: const Text('OK'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); + } + } + } + } +} + +class AmityV4SuccessDialog { + static Future showTimedDialog(String text, + {BuildContext? context}) async { + + showCupertinoDialog( + context: context ?? NavigationService.navigatorKey.currentContext!, + barrierDismissible: false, + builder: (BuildContext context) { + return TimedDialog( + text: text, + ); + }, + ); + } +} + +class ConfirmationV4Dialog { + Future show({ + required BuildContext context, + required String title, + required String detailText, + String leftButtonText = 'Cancel', + String rightButtonText = 'Confirm', + required Function onConfirm, + }) async { + // Check the platform + if (Platform.isAndroid) { + // Android-specific code + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text(title), + content: Text(detailText), + actions: [ + TextButton( + child: Text(leftButtonText), + onPressed: () { + Navigator.of(context).pop(); // Close the dialog + }, + ), + TextButton( + onPressed: () { + Navigator.of(context).pop(); + onConfirm(); + }, + style: TextButton.styleFrom( + foregroundColor: Colors.red, // Set the text color + ), + child: Text(rightButtonText), + ), + ], + ); + }, + ); + } else if (Platform.isIOS) { + // iOS-specific code + final systemBrightness = + SchedulerBinding.instance.platformDispatcher.platformBrightness; + + return showCupertinoDialog( + context: context, + builder: (BuildContext context) { + return CupertinoTheme( + data: CupertinoThemeData(brightness: systemBrightness), + child: CupertinoAlertDialog( + title: Text(title), + content: Text(detailText), + actions: [ + CupertinoDialogAction( + child: Text(leftButtonText), + onPressed: () { + Navigator.of(context).pop(); // Close the dialog + }, + ), + CupertinoDialogAction( + textStyle: const TextStyle(color: Colors.red), + onPressed: () { + Navigator.of(context).pop(); + onConfirm(); + }, + isDefaultAction: true, + child: Text(rightButtonText), + ), + ], + ), + ); + }, + ); + } + } +} diff --git a/lib/v4/utils/config_provider_widget.dart b/lib/v4/utils/config_provider_widget.dart index 7baddc3f..7c6bb9f4 100644 --- a/lib/v4/utils/config_provider_widget.dart +++ b/lib/v4/utils/config_provider_widget.dart @@ -14,27 +14,30 @@ class SocialHomePageConfigProviderWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return ChangeNotifierProvider( - key: const ValueKey("social_home_page"), - create: (_) { - var configProvider = ConfigProvider(); - configProvider.loadConfig(); - return configProvider; - }, - child: Consumer( - builder: (context, configProvider, child) { - return SocialHomePage(pageId: "social_home_page"); + return Scaffold( + appBar: AppBar(), + body: ChangeNotifierProvider( + key: const ValueKey("social_home_page"), + create: (_) { + var configProvider = ConfigProvider(); + configProvider.loadConfig(); + return configProvider; }, + child: Consumer( + builder: (context, configProvider, child) { + return SocialHomePage(pageId: "social_home_page"); + }, + ), ), ); } } - class NewsFeedComponentConfigProviderWidget extends StatelessWidget { final String pageId; - const NewsFeedComponentConfigProviderWidget({super.key, required this.pageId}); + const NewsFeedComponentConfigProviderWidget( + {super.key, required this.pageId}); @override Widget build(BuildContext context) { diff --git a/lib/view/UIKit/social/community_setting/posts/edit_post_page.dart b/lib/view/UIKit/social/community_setting/posts/edit_post_page.dart index cd7453cb..97dcaa92 100644 --- a/lib/view/UIKit/social/community_setting/posts/edit_post_page.dart +++ b/lib/view/UIKit/social/community_setting/posts/edit_post_page.dart @@ -84,7 +84,7 @@ class _AmityEditPostScreenState extends State { context: context, callback: () { Navigator.of(context).pop(); - context.read().add(GlobalFeedReloadThePost(postId: widget.amityPost.postId!)); + context.read().add(GlobalFeedReloadThePost(post: widget.amityPost)); }); } : null, diff --git a/pubspec.yaml b/pubspec.yaml index 0c20d841..77f8d493 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: amity_uikit_beta_service description: Amity UIkit opensource developed by SLE team to enable social feature in Flutter. -version: 3.0.2 +version: 3.0.3 homepage: https://github.com/ThanakornThanom/ThanakornThanom--flutter_amity_uikit_beta_service environment: @@ -20,7 +20,7 @@ dependencies: flutter_phoenix: ^1.0.0 flutter_launcher_icons: ^0.13.1 modal_bottom_sheet: ^3.0.0 - amity_sdk: ^0.47.0 + amity_sdk: ^0.48.0 provider: ^6.0.3 carousel_slider: ^4.1.1 chewie: ^1.3.4