From 528e06c91effdc1e5f99d76e2a8587918b4bc563 Mon Sep 17 00:00:00 2001 From: Yoga Pratama Date: Sat, 22 May 2021 09:28:52 +0700 Subject: [PATCH 01/19] [RED] Implement test for fcm repository --- .../cloud_messaging_repository.dart | 10 +++++++ test/cloud_messaging_test.dart | 26 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 lib/repository/cloud_messaging_repository.dart create mode 100644 test/cloud_messaging_test.dart diff --git a/lib/repository/cloud_messaging_repository.dart b/lib/repository/cloud_messaging_repository.dart new file mode 100644 index 0000000..1e8d160 --- /dev/null +++ b/lib/repository/cloud_messaging_repository.dart @@ -0,0 +1,10 @@ +abstract class BaseCloudMessagingRepository { + Future sendFCMToken(String fcmToken, String token); +} + +class CloudMessagingRepository implements BaseCloudMessagingRepository { + @override + Future sendFCMToken(String fcmToken, String token) async { + return false; + } +} diff --git a/test/cloud_messaging_test.dart b/test/cloud_messaging_test.dart new file mode 100644 index 0000000..0e99bce --- /dev/null +++ b/test/cloud_messaging_test.dart @@ -0,0 +1,26 @@ +import 'package:bisaGo/repository/cloud_messaging_repository.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:get_it/get_it.dart'; +import 'package:mockito/mockito.dart'; + +class MockCloudMessagingRepository extends Fake + implements CloudMessagingRepository { + @override + Future sendFCMToken(String fcmToken, String token) async { + return Future.value(true); + } +} + +void main() { + setUpAll(() { + final _getIt = GetIt.instance; + _getIt.registerLazySingleton( + () => MockCloudMessagingRepository()); + }); + testWidgets('Generate fcm token', (WidgetTester tester) async { + final generatedUrl = + await MockCloudMessagingRepository().sendFCMToken('fcmToken', 'token'); + + expect(generatedUrl, true); + }); +} -- GitLab From 982883b5c224439a2f2fe1f4ceeb6e2c10f67944 Mon Sep 17 00:00:00 2001 From: Yoga Pratama Date: Sat, 22 May 2021 10:18:20 +0700 Subject: [PATCH 02/19] [GREEN] Implement fcm getToken() --- android/app/build.gradle | 4 +- android/app/src/main/AndroidManifest.xml | 4 ++ lib/get_it.dart | 9 ++- lib/main_dev.dart | 11 ++++ lib/page/dashboard/dashboard.dart | 59 ++++++++++++++----- .../cloud_messaging_repository.dart | 16 ++++- pubspec.yaml | 1 + 7 files changed, 86 insertions(+), 18 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 949d735..13a3598 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -83,7 +83,9 @@ dependencies { testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' - implementation 'com.google.firebase:firebase-analytics:17.2.2' + implementation 'com.google.firebase:firebase-analytics:' + implementation 'com.google.firebase:firebase-messaging:' + implementation 'com.google.firebase:firebase-bom:27.0.0' } apply plugin: 'com.google.gms.google-services' \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 7e0e18f..9520df8 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -33,6 +33,10 @@ + + + + diff --git a/lib/get_it.dart b/lib/get_it.dart index aec9e26..0aa444d 100644 --- a/lib/get_it.dart +++ b/lib/get_it.dart @@ -1,3 +1,4 @@ +import 'package:bisaGo/repository/cloud_messaging_repository.dart'; import 'package:bisaGo/repository/kegiatan_repository.dart'; import 'package:bisaGo/repository/kegiatan_terdekat_repository.dart'; import 'package:bisaGo/repository/komentar_posting_kegiatan_repository.dart'; @@ -26,8 +27,8 @@ class AppGetIt { () => KomentarPostingRepository()); _getIt.registerLazySingleton( () => KomentarPostingKegiatanRepository()); - _getIt.registerLazySingleton( - () => LokasiRepository()); + _getIt + .registerLazySingleton(() => LokasiRepository()); _getIt.registerLazySingleton( () => LayananRepository()); _getIt.registerLazySingleton( @@ -36,5 +37,9 @@ class AppGetIt { () => KegiatanTerdekatRepository()); _getIt.registerLazySingleton( () => DynamicLinksServiceRepository()); + _getIt.registerLazySingleton( + () => DynamicLinksServiceRepository()); + _getIt.registerLazySingleton( + () => CloudMessagingRepository()); } } diff --git a/lib/main_dev.dart b/lib/main_dev.dart index 564a455..aa484a7 100644 --- a/lib/main_dev.dart +++ b/lib/main_dev.dart @@ -1,3 +1,5 @@ +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:bisaGo/app.dart'; import 'package:intl/date_symbol_data_local.dart'; @@ -7,11 +9,20 @@ import 'flavor/flavor.dart'; import 'globalnetwork.dart'; import 'package:bisaGo/get_it.dart'; +Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { + // If you're going to use other Firebase services in the background, such as Firestore, + // make sure you call `initializeApp` before using other Firebase services. + await Firebase.initializeApp(); + + print('Handling a background message: ${message.messageId}'); +} + Future main() async { AppGetIt().initialize(); await DotEnv().load('.env'); getDioInstance('build'); await initializeDateFormatting('id_ID', null); + FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); Intl.defaultLocale = 'id_ID'; dio.options.receiveTimeout = 15000; ApiFlavor.flavor = BuildFlavor.development.toString(); diff --git a/lib/page/dashboard/dashboard.dart b/lib/page/dashboard/dashboard.dart index 8e70ad4..5d527da 100644 --- a/lib/page/dashboard/dashboard.dart +++ b/lib/page/dashboard/dashboard.dart @@ -11,7 +11,9 @@ import 'package:bisaGo/page/filter_fasilitas/postingan/detail_post.dart'; import 'package:bisaGo/repository/komentar_repository.dart'; import 'package:bisaGo/utils/custom_dashboard_location_button.dart'; import 'package:bisaGo/utils/location_turn_on_dialog.dart'; +import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_dynamic_links/firebase_dynamic_links.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:geolocator/geolocator.dart'; @@ -38,6 +40,10 @@ class DashboardState extends State { LokasiResponseBloc bloc = LokasiResponseBloc(); KegiatanTerdekatBloc blocKegiatanTerdekat = KegiatanTerdekatBloc(); + FirebaseMessaging _firebaseMessaging; + + bool _initialized = false; + @override void initState() { super.initState(); @@ -45,6 +51,31 @@ class DashboardState extends State { geolocator = Geolocator()..forceAndroidLocationManager; initDynamicLinks(); setInitialLocation(); + + if (!_initialized) { + _initialized = true; + _setupFirebase(); + } + } + + void _setupFirebase() async { + await Firebase.initializeApp(); + _firebaseMessaging = FirebaseMessaging.instance; + + FirebaseMessaging.onMessage.listen((RemoteMessage message) { + print('Got a message whilst in the foreground!'); + print('Message data: ${message.data}'); + + if (message.notification != null) { + print('Message also contained a notification: ${message.notification}'); + } + }); + _requestFCMToken(); + } + + void _requestFCMToken() async { + final token = await _firebaseMessaging.getToken(); + print(token); } void _navigateToPencarianPage(BuildContext context) { @@ -368,20 +399,20 @@ class DashboardState extends State { ..name = namaLokasi; final fasilitasRoute = MaterialPageRoute( builder: (BuildContext context) => DetailPostKegiatanPage( - lokasi: lokasi, - kegiatan: KegiatanModel( - id: kegiatan.id, - placeId: lokasi.placeId, - creator: kegiatan.creator, - namaKegiatan: kegiatan.namaKegiatan, - penyelenggara: kegiatan.penyelenggara, - narahubung: kegiatan.narahubung, - deskripsi: kegiatan.deskripsi, - timeStart: kegiatan.timeStart, - timeEnd: kegiatan.timeEnd, - image: kegiatan.image, - ), - )); + lokasi: lokasi, + kegiatan: KegiatanModel( + id: kegiatan.id, + placeId: lokasi.placeId, + creator: kegiatan.creator, + namaKegiatan: kegiatan.namaKegiatan, + penyelenggara: kegiatan.penyelenggara, + narahubung: kegiatan.narahubung, + deskripsi: kegiatan.deskripsi, + timeStart: kegiatan.timeStart, + timeEnd: kegiatan.timeEnd, + image: kegiatan.image, + ), + )); await Navigator.of(context).push(fasilitasRoute); } diff --git a/lib/repository/cloud_messaging_repository.dart b/lib/repository/cloud_messaging_repository.dart index 1e8d160..e45573b 100644 --- a/lib/repository/cloud_messaging_repository.dart +++ b/lib/repository/cloud_messaging_repository.dart @@ -1,3 +1,8 @@ +import 'dart:convert'; + +import 'package:http/http.dart' as http; +import 'package:bisaGo/flavor/flavor.dart'; + abstract class BaseCloudMessagingRepository { Future sendFCMToken(String fcmToken, String token); } @@ -5,6 +10,15 @@ abstract class BaseCloudMessagingRepository { class CloudMessagingRepository implements BaseCloudMessagingRepository { @override Future sendFCMToken(String fcmToken, String token) async { - return false; + try { + await http.post( + '${ApiFlavor.getBaseUrl()}/', + headers: {'Authorization': token, 'content-type': 'application/json'}, + body: json.encode({'token': fcmToken}), + ); + return true; + } catch (_) { + return false; + } } } diff --git a/pubspec.yaml b/pubspec.yaml index 59f1b2b..ab35153 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -55,6 +55,7 @@ dependencies: firebase_core: ^0.7.0 firebase_core_platform_interface: ^3.0.1 firebase_dynamic_links: ^0.7.0+1 + firebase_messaging: ^8.0.0-dev.15 carousel_slider: ^3.0.0 dev_dependencies: -- GitLab From e7f00baf3e91a4084c632f8a56e6b46355b74a13 Mon Sep 17 00:00:00 2001 From: Yoga Pratama Date: Sat, 22 May 2021 10:52:39 +0700 Subject: [PATCH 03/19] [GREEN] Implement fcm bloc --- lib/bloc/cloud_messaging_bloc.dart | 26 +++++++++++++++++++ .../cloud_messaging_repository.dart | 12 ++++++--- 2 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 lib/bloc/cloud_messaging_bloc.dart diff --git a/lib/bloc/cloud_messaging_bloc.dart b/lib/bloc/cloud_messaging_bloc.dart new file mode 100644 index 0000000..b91d380 --- /dev/null +++ b/lib/bloc/cloud_messaging_bloc.dart @@ -0,0 +1,26 @@ +import 'package:bisaGo/repository/cloud_messaging_repository.dart'; +import 'package:get_it/get_it.dart'; +import 'package:http/http.dart'; + +class CloudMessagingBloc { + CloudMessagingRepository _cloudMessagingRepository; + + CloudMessagingBloc() { + _cloudMessagingRepository = + GetIt.instance.get(); + } + + Future sendFCMToken( + String fcmToken, + String token, + ) async { + try { + return await _cloudMessagingRepository.sendFCMToken( + fcmToken, + token, + ); + } catch (e) { + return Response('Failed to add komentar', 400); + } + } +} diff --git a/lib/repository/cloud_messaging_repository.dart b/lib/repository/cloud_messaging_repository.dart index e45573b..49ea45c 100644 --- a/lib/repository/cloud_messaging_repository.dart +++ b/lib/repository/cloud_messaging_repository.dart @@ -12,9 +12,15 @@ class CloudMessagingRepository implements BaseCloudMessagingRepository { Future sendFCMToken(String fcmToken, String token) async { try { await http.post( - '${ApiFlavor.getBaseUrl()}/', - headers: {'Authorization': token, 'content-type': 'application/json'}, - body: json.encode({'token': fcmToken}), + '${ApiFlavor.getBaseUrl()}/notification/', + headers: { + 'Authorization': 'token $token', + 'content-type': 'application/json' + }, + body: json.encode({ + 'token': fcmToken, + 'type': 'android', + }), ); return true; } catch (_) { -- GitLab From 061effe878e2d175dae09f5c21aa1649ede8cba9 Mon Sep 17 00:00:00 2001 From: Yoga Pratama Date: Sat, 22 May 2021 10:53:19 +0700 Subject: [PATCH 04/19] [CHORES] Delete duplicate line --- lib/get_it.dart | 2 -- lib/page/dashboard/dashboard.dart | 12 ++++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/get_it.dart b/lib/get_it.dart index 0aa444d..18eee1b 100644 --- a/lib/get_it.dart +++ b/lib/get_it.dart @@ -37,8 +37,6 @@ class AppGetIt { () => KegiatanTerdekatRepository()); _getIt.registerLazySingleton( () => DynamicLinksServiceRepository()); - _getIt.registerLazySingleton( - () => DynamicLinksServiceRepository()); _getIt.registerLazySingleton( () => CloudMessagingRepository()); } diff --git a/lib/page/dashboard/dashboard.dart b/lib/page/dashboard/dashboard.dart index 5d527da..4de3408 100644 --- a/lib/page/dashboard/dashboard.dart +++ b/lib/page/dashboard/dashboard.dart @@ -1,3 +1,4 @@ +import 'package:bisaGo/bloc/cloud_messaging_bloc.dart'; import 'package:bisaGo/bloc/lokasi_response_bloc.dart'; import 'package:bisaGo/model/komentar.dart'; import 'package:bisaGo/model/lokasi.dart'; @@ -23,6 +24,7 @@ import 'package:bisaGo/component/bisago_drawer.dart'; import 'package:bisaGo/config/styles.dart'; import 'package:bisaGo/page/pencarian/pencarian.dart'; import 'package:google_maps_webservice/places.dart'; +import 'package:shared_preferences/shared_preferences.dart'; class Dashboard extends StatefulWidget { const Dashboard({Key key}) : super(key: key); @@ -39,6 +41,7 @@ class DashboardState extends State { LokasiResponseBloc bloc = LokasiResponseBloc(); KegiatanTerdekatBloc blocKegiatanTerdekat = KegiatanTerdekatBloc(); + CloudMessagingBloc cloudMessagingBloc = CloudMessagingBloc(); FirebaseMessaging _firebaseMessaging; @@ -74,8 +77,13 @@ class DashboardState extends State { } void _requestFCMToken() async { - final token = await _firebaseMessaging.getToken(); - print(token); + final fcmToken = await _firebaseMessaging.getToken(); + final sharedPreferences = await SharedPreferences.getInstance(); + final token = sharedPreferences.getString('token'); + + if (token != null) { + await cloudMessagingBloc.sendFCMToken(fcmToken, token); + } } void _navigateToPencarianPage(BuildContext context) { -- GitLab From 61e51b86b5981e96dbdc7226a2eaff3236197746 Mon Sep 17 00:00:00 2001 From: Yoga Pratama Date: Sat, 22 May 2021 11:10:26 +0700 Subject: [PATCH 05/19] [CHORES] Add mock fcm repository to several files --- test/cloud_messaging_test.dart | 4 ++-- test/custom_kegiatan_terdekat_button_test.dart | 11 +++++++++++ test/mock_test.dart | 11 +++++++++++ test/widget_test.dart | 11 +++++++++++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/test/cloud_messaging_test.dart b/test/cloud_messaging_test.dart index 0e99bce..0f41541 100644 --- a/test/cloud_messaging_test.dart +++ b/test/cloud_messaging_test.dart @@ -18,9 +18,9 @@ void main() { () => MockCloudMessagingRepository()); }); testWidgets('Generate fcm token', (WidgetTester tester) async { - final generatedUrl = + final result = await MockCloudMessagingRepository().sendFCMToken('fcmToken', 'token'); - expect(generatedUrl, true); + expect(result, true); }); } diff --git a/test/custom_kegiatan_terdekat_button_test.dart b/test/custom_kegiatan_terdekat_button_test.dart index b59a76e..c8b5511 100644 --- a/test/custom_kegiatan_terdekat_button_test.dart +++ b/test/custom_kegiatan_terdekat_button_test.dart @@ -1,6 +1,7 @@ import 'package:bisaGo/model/kegiatan.dart'; import 'package:bisaGo/model/lokasi.dart'; import 'package:bisaGo/page/dashboard/dashboard.dart'; +import 'package:bisaGo/repository/cloud_messaging_repository.dart'; import 'package:bisaGo/repository/kegiatan_terdekat_repository.dart'; import 'package:bisaGo/repository/lokasi_repository.dart'; import 'package:flutter/material.dart'; @@ -41,6 +42,14 @@ class MockLokasi extends Fake implements LokasiRepository { } } +class MockCloudMessagingRepository extends Fake + implements CloudMessagingRepository { + @override + Future sendFCMToken(String fcmToken, String token) async { + return Future.value(true); + } +} + void main() { // final mockLokasi = { // 'name': 'Margo City', @@ -68,6 +77,8 @@ void main() { _getIt.registerLazySingleton( () => MockKegiatanTerdekat()); _getIt.registerLazySingleton(() => MockLokasi()); + _getIt.registerLazySingleton( + () => MockCloudMessagingRepository()); }); testWidgets('Detail Post Kegiatan Page - Positive Test', diff --git a/test/mock_test.dart b/test/mock_test.dart index 032df7c..32c7466 100644 --- a/test/mock_test.dart +++ b/test/mock_test.dart @@ -1,4 +1,5 @@ import 'package:bisaGo/model/kegiatan.dart'; +import 'package:bisaGo/repository/cloud_messaging_repository.dart'; import 'package:bisaGo/repository/kegiatan_terdekat_repository.dart'; import 'package:bisaGo/repository/lokasi_repository.dart'; import 'package:flutter/material.dart'; @@ -45,6 +46,14 @@ class MockKegiatanTerdekatRepository extends Fake } } +class MockCloudMessagingRepository extends Fake + implements CloudMessagingRepository { + @override + Future sendFCMToken(String fcmToken, String token) async { + return Future.value(true); + } +} + void main() { group('Dashboard navigation tests', () { NavigatorObserver mockObserver; @@ -74,6 +83,8 @@ void main() { () => MockLokasiRepository()); _getIt.registerLazySingleton( () => MockKegiatanTerdekatRepository()); + _getIt.registerLazySingleton( + () => MockCloudMessagingRepository()); }); Future _buildDashboardPage(WidgetTester tester) async { diff --git a/test/widget_test.dart b/test/widget_test.dart index bac647f..69c7052 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -2,6 +2,7 @@ import 'package:bisaGo/model/kegiatan.dart'; import 'package:bisaGo/model/komunitas.dart'; import 'package:bisaGo/model/lokasi.dart'; import 'package:bisaGo/model/sekolah.dart'; +import 'package:bisaGo/repository/cloud_messaging_repository.dart'; import 'package:bisaGo/repository/kegiatan_terdekat_repository.dart'; import 'package:bisaGo/repository/komunitas_repository.dart'; import 'package:bisaGo/repository/lokasi_repository.dart'; @@ -88,6 +89,14 @@ class MockKegiatanTerdekatRepository extends Fake } } +class MockCloudMessagingRepository extends Fake + implements CloudMessagingRepository { + @override + Future sendFCMToken(String fcmToken, String token) async { + return Future.value(true); + } +} + void main() { setUpAll(() { final _getIt = GetIt.instance; @@ -99,6 +108,8 @@ void main() { () => MockLokasiRepository()); _getIt.registerLazySingleton( () => MockKegiatanTerdekatRepository()); + _getIt.registerLazySingleton( + () => MockCloudMessagingRepository()); }); testWidgets('finds a text field in dashboard', (WidgetTester tester) async { final containerTextField = Key('Container Text Field'); -- GitLab From 81889682d70081632d52b63fc33a1f9345b4b31e Mon Sep 17 00:00:00 2001 From: Yoga Pratama Date: Sun, 23 May 2021 05:13:19 +0700 Subject: [PATCH 06/19] [GREEN] Implement receive notification logic --- lib/page/dashboard/dashboard.dart | 49 +++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/lib/page/dashboard/dashboard.dart b/lib/page/dashboard/dashboard.dart index 4de3408..e1daa0f 100644 --- a/lib/page/dashboard/dashboard.dart +++ b/lib/page/dashboard/dashboard.dart @@ -12,6 +12,7 @@ import 'package:bisaGo/page/filter_fasilitas/postingan/detail_post.dart'; import 'package:bisaGo/repository/komentar_repository.dart'; import 'package:bisaGo/utils/custom_dashboard_location_button.dart'; import 'package:bisaGo/utils/location_turn_on_dialog.dart'; +import 'package:dropdown_banner/dropdown_banner.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_dynamic_links/firebase_dynamic_links.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; @@ -26,6 +27,8 @@ import 'package:bisaGo/page/pencarian/pencarian.dart'; import 'package:google_maps_webservice/places.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import '../filter_fasilitas/postingan/detail_post.dart'; + class Dashboard extends StatefulWidget { const Dashboard({Key key}) : super(key: key); @override @@ -47,6 +50,8 @@ class DashboardState extends State { bool _initialized = false; + DateTime _lastNotification; + @override void initState() { super.initState(); @@ -66,13 +71,48 @@ class DashboardState extends State { _firebaseMessaging = FirebaseMessaging.instance; FirebaseMessaging.onMessage.listen((RemoteMessage message) { - print('Got a message whilst in the foreground!'); - print('Message data: ${message.data}'); + final now = DateTime.now(); - if (message.notification != null) { - print('Message also contained a notification: ${message.notification}'); + if (_lastNotification != null && + _lastNotification.add(Duration(seconds: 10)).isAfter(now)) return; + + _lastNotification = now; + final data = message.data; + final String msg = data['message']; + DropdownBanner.showBanner( + text: msg, + color: Color(0xFF003566), + textStyle: TextStyle( + color: Colors.white, + height: 2.1, + ), + duration: Duration(seconds: 8), + tapCallback: () { + if (data['type'] == 'fasilitas') { + final String placeId = data['place_id']; + final id = int.parse(data['id']); + _navigateToDetailFasilitasPage(context, placeId, id); + } else if (data['type'] == 'kegiatan') { + final String placeId = data['place_id']; + final id = int.parse(data['id']); + _navigateToDetailKegiatanPage(context, placeId, id); + } + }); + }); + + FirebaseMessaging.onMessageOpenedApp.listen((message) { + final data = message.data; + if (data['type'] == 'fasilitas') { + final String placeId = data['place_id']; + final id = int.parse(data['id']); + _navigateToDetailFasilitasPage(context, placeId, id); + } else if (data['type'] == 'kegiatan') { + final String placeId = data['place_id']; + final id = int.parse(data['id']); + _navigateToDetailKegiatanPage(context, placeId, id); } }); + _requestFCMToken(); } @@ -80,7 +120,6 @@ class DashboardState extends State { final fcmToken = await _firebaseMessaging.getToken(); final sharedPreferences = await SharedPreferences.getInstance(); final token = sharedPreferences.getString('token'); - if (token != null) { await cloudMessagingBloc.sendFCMToken(fcmToken, token); } -- GitLab From ea73f9b5848e6a1e097394e55b550d15baa6d55a Mon Sep 17 00:00:00 2001 From: Yoga Pratama Date: Sun, 23 May 2021 05:15:54 +0700 Subject: [PATCH 07/19] [GREEN] Implement banner for foreground notification --- lib/app.dart | 8 +++++++- pubspec.yaml | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/app.dart b/lib/app.dart index 11dd997..c572bb9 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -1,3 +1,4 @@ +import 'package:dropdown_banner/dropdown_banner.dart'; import 'package:flutter/material.dart'; import 'package:bisaGo/config/styles.dart'; import 'package:bisaGo/page/dashboard/dashboard.dart'; @@ -7,6 +8,8 @@ class BisaGo extends StatelessWidget { @override Widget build(BuildContext context) { + final navigatorKey = GlobalKey(); + return MaterialApp( title: 'bisaGo', theme: ThemeData( @@ -14,7 +17,10 @@ class BisaGo extends StatelessWidget { primaryColor: greenPrimary, backgroundColor: Colors.white, ), - home: Dashboard(), + home: DropdownBanner( + navigatorKey: navigatorKey, + child: Dashboard(), + ), ); } } diff --git a/pubspec.yaml b/pubspec.yaml index ab35153..c7a2c05 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -57,6 +57,7 @@ dependencies: firebase_dynamic_links: ^0.7.0+1 firebase_messaging: ^8.0.0-dev.15 carousel_slider: ^3.0.0 + dropdown_banner: ^1.4.0 dev_dependencies: flutter_test: -- GitLab From 562c36363bd4bee788a7cddcb3b75ab7485f2da8 Mon Sep 17 00:00:00 2001 From: Yoga Pratama Date: Sun, 23 May 2021 05:16:25 +0700 Subject: [PATCH 08/19] [CHORES] Remove WillPopScope --- lib/page/filter_fasilitas/kegiatan.dart | 147 ++-- lib/page/filter_fasilitas/komentar.dart | 2 +- .../postingan/detail_post.dart | 637 +++++++------- .../postingan/detail_post_kegiatan.dart | 795 +++++++++--------- 4 files changed, 778 insertions(+), 803 deletions(-) diff --git a/lib/page/filter_fasilitas/kegiatan.dart b/lib/page/filter_fasilitas/kegiatan.dart index d668443..ef3c723 100644 --- a/lib/page/filter_fasilitas/kegiatan.dart +++ b/lib/page/filter_fasilitas/kegiatan.dart @@ -17,29 +17,27 @@ class Kegiatan extends StatefulWidget { } class _KegiatanState extends State { - @override Widget build(BuildContext context) { return InkWell( key: Key(widget.kegiatan.namaKegiatan), onTap: () { - Navigator.of(context).pushReplacement(MaterialPageRoute( + Navigator.of(context).push(MaterialPageRoute( builder: (BuildContext context) => DetailPostKegiatanPage( - lokasi: widget.lokasi, - kegiatan: KegiatanModel( - id: widget.kegiatan.id, - placeId: widget.kegiatan.placeId, - creator: widget.kegiatan.creator, - namaKegiatan: widget.kegiatan.namaKegiatan, - penyelenggara: widget.kegiatan.penyelenggara, - narahubung: widget.kegiatan.narahubung, - deskripsi: widget.kegiatan.deskripsi, - timeStart: widget.kegiatan.timeStart, - timeEnd: widget.kegiatan.timeEnd, - image: widget.kegiatan.image, - ), - ) - )); + lokasi: widget.lokasi, + kegiatan: KegiatanModel( + id: widget.kegiatan.id, + placeId: widget.kegiatan.placeId, + creator: widget.kegiatan.creator, + namaKegiatan: widget.kegiatan.namaKegiatan, + penyelenggara: widget.kegiatan.penyelenggara, + narahubung: widget.kegiatan.narahubung, + deskripsi: widget.kegiatan.deskripsi, + timeStart: widget.kegiatan.timeStart, + timeEnd: widget.kegiatan.timeEnd, + image: widget.kegiatan.image, + ), + ))); }, child: Container( margin: const EdgeInsets.only(bottom: regularSpace), @@ -72,59 +70,68 @@ class _KegiatanState extends State { ), ), Container( - margin: const EdgeInsets.only(bottom: regularSpace), - child: SizedBox( - width: MediaQuery.of(context).size.width, - height: 160, - child: CarouselSlider( - options: CarouselOptions( - aspectRatio: 1.0, - enlargeCenterPage: true, - enableInfiniteScroll: false, - initialPage: 0, - autoPlay: true, - ), - items: widget.kegiatan.image.map((item) => Container( - child: Container( - child: ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(20)), - child: Stack( - children: [ - Image.network(item, fit: BoxFit.cover, width: 1000.0), - Positioned( - bottom: 0.0, - left: 0.0, - right: 0.0, + margin: const EdgeInsets.only(bottom: regularSpace), + child: SizedBox( + width: MediaQuery.of(context).size.width, + height: 160, + child: CarouselSlider( + options: CarouselOptions( + aspectRatio: 1.0, + enlargeCenterPage: true, + enableInfiniteScroll: false, + initialPage: 0, + autoPlay: true, + ), + items: widget.kegiatan.image + .map((item) => Container( child: Container( - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [ - Color.fromARGB(200, 0, 0, 0), - Color.fromARGB(0, 0, 0, 0) - ], - begin: Alignment.bottomCenter, - end: Alignment.topCenter, - ), - ), - padding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0), - child: Text( - '#${widget.kegiatan.image.indexOf(item)+1}', - style: TextStyle( - color: Colors.white, - fontSize: 20.0, - fontWeight: FontWeight.bold, - ), - ), + child: ClipRRect( + borderRadius: BorderRadius.all( + Radius.circular(20)), + child: Stack( + children: [ + Image.network(item, + fit: BoxFit.cover, + width: 1000.0), + Positioned( + bottom: 0.0, + left: 0.0, + right: 0.0, + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + Color.fromARGB( + 200, 0, 0, 0), + Color.fromARGB( + 0, 0, 0, 0) + ], + begin: Alignment + .bottomCenter, + end: Alignment.topCenter, + ), + ), + padding: EdgeInsets.symmetric( + vertical: 10.0, + horizontal: 20.0), + child: Text( + '#${widget.kegiatan.image.indexOf(item) + 1}', + style: TextStyle( + color: Colors.white, + fontSize: 20.0, + fontWeight: + FontWeight.bold, + ), + ), + ), + ), + ], + )), ), - ), - ], - ) - ), - ), - )).toList(), - ), - ) - ), + )) + .toList(), + ), + )), Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.end, @@ -148,5 +155,5 @@ class _KegiatanState extends State { ], ), )); - } -} \ No newline at end of file + } +} diff --git a/lib/page/filter_fasilitas/komentar.dart b/lib/page/filter_fasilitas/komentar.dart index dda5b47..d7c53f2 100644 --- a/lib/page/filter_fasilitas/komentar.dart +++ b/lib/page/filter_fasilitas/komentar.dart @@ -37,7 +37,7 @@ class _KomentarState extends State { return InkWell( key: Key('Fasilitas ${fasilitas[widget.komentar.tag]}'), onTap: () { - Navigator.of(context).pushReplacement(MaterialPageRoute( + Navigator.of(context).push(MaterialPageRoute( builder: (BuildContext context) => DetailPostPage( lokasi: widget.lokasi, komentar: KomentarModel( diff --git a/lib/page/filter_fasilitas/postingan/detail_post.dart b/lib/page/filter_fasilitas/postingan/detail_post.dart index af1d54d..39ccab1 100644 --- a/lib/page/filter_fasilitas/postingan/detail_post.dart +++ b/lib/page/filter_fasilitas/postingan/detail_post.dart @@ -54,357 +54,345 @@ class _DetailPostPageState extends State { @override Widget build(BuildContext context) { var namaLokasi = widget.komentar.namaLokasi ?? 'Invalid Lokasi Name'; - return WillPopScope( - onWillPop: () => Navigator.of(context).pushReplacement(MaterialPageRoute( - builder: (BuildContext context) => Fasilitas( - lokasi: widget.lokasi, - ))), - child: Scaffold( - appBar: BisaGoAppBar( - title: namaLokasi, - key: Key('appbar-text-$namaLokasi'), - actions: [ - InkWell( - onTap: () async { - final link = await DynamicLinksServiceRepository() - .createDynamicLinkForFasilitas( - widget.komentar.id, - widget.lokasi.placeId, - ); - await Share.share(ShareUtils.getFormattedMessageFasilitas( - widget.komentar, widget.lokasi, link)); - }, - child: const Padding( - padding: EdgeInsets.all(doubleSpace), - child: Icon(Icons.share), - ), + return Scaffold( + appBar: BisaGoAppBar( + title: namaLokasi, + key: Key('appbar-text-$namaLokasi'), + actions: [ + InkWell( + onTap: () async { + final link = await DynamicLinksServiceRepository() + .createDynamicLinkForFasilitas( + widget.komentar.id, + widget.lokasi.placeId, + ); + await Share.share(ShareUtils.getFormattedMessageFasilitas( + widget.komentar, widget.lokasi, link)); + }, + child: const Padding( + padding: EdgeInsets.all(doubleSpace), + child: Icon(Icons.share), ), - ], - ), - body: SingleChildScrollView( - child: Column( - children: [ - Container( - key: const Key('Text Jenis Fasilitas'), - margin: const EdgeInsets.symmetric( - vertical: 10.0, horizontal: 15.0), - alignment: Alignment.centerLeft, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Container( - width: MediaQuery.of(context).size.width * 0.6, - child: Text( - fasilitas[widget.komentar.tag], - style: const TextStyle( - fontSize: 28, - fontWeight: FontWeight.w800, - letterSpacing: -0.3, - color: Colors.black, - fontFamily: 'Comfortaa', - ), + ), + ], + ), + body: SingleChildScrollView( + child: Column( + children: [ + Container( + key: const Key('Text Jenis Fasilitas'), + margin: + const EdgeInsets.symmetric(vertical: 10.0, horizontal: 15.0), + alignment: Alignment.centerLeft, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.6, + child: Text( + fasilitas[widget.komentar.tag], + style: const TextStyle( + fontSize: 28, + fontWeight: FontWeight.w800, + letterSpacing: -0.3, + color: Colors.black, + fontFamily: 'Comfortaa', ), ), - PopupMenuButton( - key: const Key('Button Ubah Informasi'), - elevation: 4.0, - offset: const Offset(0.0, 40.0), - itemBuilder: (BuildContext context) { - final choices = ['Ubah Informasi']; - return choices.map((String choice) { - return PopupMenuItem( - key: Key(choice), - child: ElevatedButton( - style: ButtonStyle( - padding: MaterialStateProperty.all( - EdgeInsets.symmetric( - vertical: 0, horizontal: 0)), - backgroundColor: - MaterialStateProperty.all(Colors.white), - foregroundColor: - MaterialStateProperty.all(Colors.black), - elevation: MaterialStateProperty.all(0)), - onPressed: _updateInformasi, - child: SizedBox( - width: double.infinity, - child: Text(choice), - ), + ), + PopupMenuButton( + key: const Key('Button Ubah Informasi'), + elevation: 4.0, + offset: const Offset(0.0, 40.0), + itemBuilder: (BuildContext context) { + final choices = ['Ubah Informasi']; + return choices.map((String choice) { + return PopupMenuItem( + key: Key(choice), + child: ElevatedButton( + style: ButtonStyle( + padding: MaterialStateProperty.all( + EdgeInsets.symmetric( + vertical: 0, horizontal: 0)), + backgroundColor: + MaterialStateProperty.all(Colors.white), + foregroundColor: + MaterialStateProperty.all(Colors.black), + elevation: MaterialStateProperty.all(0)), + onPressed: _updateInformasi, + child: SizedBox( + width: double.infinity, + child: Text(choice), ), - ); - }).toList(); - }, - ), - ], - ), + ), + ); + }).toList(); + }, + ), + ], ), - Container( - key: const Key('Text Jumlah'), - width: MediaQuery.of(context).size.width, - color: red, - padding: const EdgeInsets.symmetric( - vertical: regularSpace, horizontal: doubleSpace), - child: Text( - 'Tersedia sebanyak ${widget.komentar.jumlah} ' - 'unit fasilitas.', - style: const TextStyle( - fontSize: 16, - color: Colors.white, - fontFamily: 'Comfortaa', - ), - )), - Container( - margin: const EdgeInsets.all(doubleSpace), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox( - height: 150, - child: ImageHolder( - url: widget.komentar.image, - fasilitas: widget.komentar.tag)), - const SizedBox( - height: 10, - ), - // Wrap( - // alignment: WrapAlignment.start, - // direction: Axis.horizontal, - // crossAxisAlignment: WrapCrossAlignment.start, - // children: widget.komentar.tag - // .map((tag) => - // _createTagContainer(getTag(tag))) - // .toList(), - // ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + ), + Container( + key: const Key('Text Jumlah'), + width: MediaQuery.of(context).size.width, + color: red, + padding: const EdgeInsets.symmetric( + vertical: regularSpace, horizontal: doubleSpace), + child: Text( + 'Tersedia sebanyak ${widget.komentar.jumlah} ' + 'unit fasilitas.', + style: const TextStyle( + fontSize: 16, + color: Colors.white, + fontFamily: 'Comfortaa', + ), + )), + Container( + margin: const EdgeInsets.all(doubleSpace), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: 150, + child: ImageHolder( + url: widget.komentar.image, + fasilitas: widget.komentar.tag)), + const SizedBox( + height: 10, + ), + // Wrap( + // alignment: WrapAlignment.start, + // direction: Axis.horizontal, + // crossAxisAlignment: WrapCrossAlignment.start, + // children: widget.komentar.tag + // .map((tag) => + // _createTagContainer(getTag(tag))) + // .toList(), + // ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + 'Dapat digunakan oleh', + style: TextStyle(fontSize: 16), + textAlign: TextAlign.left, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _showIfContains('DF'), + _showIfContains('DI'), + _showIfContains('DM'), + _showIfContains('DS'), + ], + ) + ], + ), + Container( + key: const Key('desc'), + decoration: BoxDecoration( + color: gray, + boxShadow: regularShadow, + borderRadius: regularBorderRadius), + padding: const EdgeInsets.all(doubleSpace), + margin: const EdgeInsets.symmetric(vertical: doubleSpace), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - 'Dapat digunakan oleh', - style: TextStyle(fontSize: 16), - textAlign: TextAlign.left, - ), Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - _showIfContains('DF'), - _showIfContains('DI'), - _showIfContains('DM'), - _showIfContains('DS'), - ], - ) - ], - ), - Container( - key: const Key('desc'), - decoration: BoxDecoration( - color: gray, - boxShadow: regularShadow, - borderRadius: regularBorderRadius), - padding: const EdgeInsets.all(doubleSpace), - margin: const EdgeInsets.symmetric(vertical: doubleSpace), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - const Expanded( - child: Text( - 'Cara menggunakan', - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.w800), - ), + const Expanded( + child: Text( + 'Cara menggunakan', + style: TextStyle( + fontSize: 20, fontWeight: FontWeight.w800), ), - ], - ), - Container( - margin: const EdgeInsets.symmetric( - vertical: regularSpace), - child: Text( - widget.komentar.deskripsi, - key: const Key('Text Cara Menggunakan'), - style: const TextStyle(fontSize: 16), ), - ), - ], - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - const Text( - 'informasi ditambahkan oleh ', - style: TextStyle( - fontSize: 12, - fontStyle: FontStyle.italic, - fontWeight: FontWeight.w200, - ), + ], ), Container( - padding: EdgeInsets.zero, - constraints: BoxConstraints( - maxWidth: - MediaQuery.of(context).size.width * 0.3), + margin: const EdgeInsets.symmetric( + vertical: regularSpace), child: Text( - '${widget.komentar.creator} ', - key: Key('creator-${widget.komentar.creator}'), - overflow: TextOverflow.fade, - softWrap: false, - style: const TextStyle( - fontSize: 12, - fontStyle: FontStyle.italic, - ), + widget.komentar.deskripsi, + key: const Key('Text Cara Menggunakan'), + style: const TextStyle(fontSize: 16), ), ), - Text( - '(${DateFormat('dd MMM yyy').format(widget.komentar.dateTime)})', - key: const Key('timestamp'), + ], + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + const Text( + 'informasi ditambahkan oleh ', + style: TextStyle( + fontSize: 12, + fontStyle: FontStyle.italic, + fontWeight: FontWeight.w200, + ), + ), + Container( + padding: EdgeInsets.zero, + constraints: BoxConstraints( + maxWidth: MediaQuery.of(context).size.width * 0.3), + child: Text( + '${widget.komentar.creator} ', + key: Key('creator-${widget.komentar.creator}'), + overflow: TextOverflow.fade, + softWrap: false, style: const TextStyle( fontSize: 12, fontStyle: FontStyle.italic, ), ), - ], - ), - const SizedBox( - height: regularSpace, - ), - const Divider( - color: grayPrimary, - thickness: 1.0, - ), - Container( - key: const Key('Komentar'), - padding: - const EdgeInsets.symmetric(vertical: regularSpace), - child: const Text( - 'Komentar', - style: TextStyle( - fontSize: 20, fontWeight: FontWeight.w800), - )), - StreamBuilder( - stream: _bloc.komentarPostingListStream, - builder: (context, snapshot) { - if (snapshot.hasData) { - switch (snapshot.data.status) { - case Status.loading: + ), + Text( + '(${DateFormat('dd MMM yyy').format(widget.komentar.dateTime)})', + key: const Key('timestamp'), + style: const TextStyle( + fontSize: 12, + fontStyle: FontStyle.italic, + ), + ), + ], + ), + const SizedBox( + height: regularSpace, + ), + const Divider( + color: grayPrimary, + thickness: 1.0, + ), + Container( + key: const Key('Komentar'), + padding: + const EdgeInsets.symmetric(vertical: regularSpace), + child: const Text( + 'Komentar', + style: TextStyle( + fontSize: 20, fontWeight: FontWeight.w800), + )), + StreamBuilder( + stream: _bloc.komentarPostingListStream, + builder: (context, snapshot) { + if (snapshot.hasData) { + switch (snapshot.data.status) { + case Status.loading: + return const Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + greenPrimary), + ), + ); + break; + case Status.completed: + allKomentarPostingFromApi = + snapshot.data.data.allKomentar; + if (allKomentarPostingFromApi.isEmpty) { return const Center( - child: CircularProgressIndicator( - valueColor: AlwaysStoppedAnimation( - greenPrimary), - ), - ); - break; - case Status.completed: - allKomentarPostingFromApi = - snapshot.data.data.allKomentar; - if (allKomentarPostingFromApi.isEmpty) { - return const Center( - child: Text('Tidak ada Komentar')); - } else { - return Column( - children: allKomentarPostingFromApi - .map((k) => - komentarPlaceHolder(k.creator, - k.dateTime, k.deskripsi)) - .toList()); - } - break; - case Status.error: - return Center( - child: Text(snapshot.data.data.toString()), - ); - break; - } + child: Text('Tidak ada Komentar')); + } else { + return Column( + children: allKomentarPostingFromApi + .map((k) => komentarPlaceHolder( + k.creator, k.dateTime, k.deskripsi)) + .toList()); + } + break; + case Status.error: + return Center( + child: Text(snapshot.data.data.toString()), + ); + break; } - return Container(); - }), - const SizedBox(height: regularSpace), - Form( - key: _formKey, - child: Column( - children: [ - TextFormField( - key: const Key('Text Field Komentar'), - keyboardType: TextInputType.multiline, - maxLines: null, - minLines: 3, - validator: FieldValidator.validateInfo, - controller: komentarController, - style: const TextStyle( - fontSize: 18, - ), - decoration: InputDecoration( - hintStyle: const TextStyle( - fontWeight: FontWeight.bold, fontSize: 15), - hintText: 'Tulis komentar...', - contentPadding: const EdgeInsets.all(8.0), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide( - color: Theme.of(context).primaryColor, - ), + } + return Container(); + }), + const SizedBox(height: regularSpace), + Form( + key: _formKey, + child: Column( + children: [ + TextFormField( + key: const Key('Text Field Komentar'), + keyboardType: TextInputType.multiline, + maxLines: null, + minLines: 3, + validator: FieldValidator.validateInfo, + controller: komentarController, + style: const TextStyle( + fontSize: 18, + ), + decoration: InputDecoration( + hintStyle: const TextStyle( + fontWeight: FontWeight.bold, fontSize: 15), + hintText: 'Tulis komentar...', + contentPadding: const EdgeInsets.all(8.0), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide( + color: Theme.of(context).primaryColor, ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide( - color: Theme.of(context).primaryColor, - ), + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide( + color: Theme.of(context).primaryColor, ), ), ), - Container( - key: const Key('tambah komentar'), - padding: - const EdgeInsets.only(top: doubleSpace), - alignment: Alignment.center, - child: ButtonTheme( - minWidth: double.infinity, - height: 40, - child: ElevatedButton( - key: const Key('Button Tambah Komentar'), - style: ButtonStyle( - padding: MaterialStateProperty.all( - EdgeInsets.symmetric(vertical: 13)), - elevation: MaterialStateProperty.all(0.0), - backgroundColor: - MaterialStateProperty.all( - greenPrimary), - shape: MaterialStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(10)))), - ), - onPressed: () { - _checkLoginStatus(); - }, - child: Row( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - const Icon( - Icons.add, - size: 30, - color: Colors.white, - ), - const SizedBox(width: 5.0), - const Text( - 'Tambah Komentar', - style: TextStyle( - fontSize: 20, - color: Colors.white, - fontWeight: FontWeight.bold), - ), - ], - ), + ), + Container( + key: const Key('tambah komentar'), + padding: const EdgeInsets.only(top: doubleSpace), + alignment: Alignment.center, + child: ButtonTheme( + minWidth: double.infinity, + height: 40, + child: ElevatedButton( + key: const Key('Button Tambah Komentar'), + style: ButtonStyle( + padding: MaterialStateProperty.all( + EdgeInsets.symmetric(vertical: 13)), + elevation: MaterialStateProperty.all(0.0), + backgroundColor: + MaterialStateProperty.all(greenPrimary), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(10)))), ), - )), - ], - )), - ], - ), + onPressed: () { + _checkLoginStatus(); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon( + Icons.add, + size: 30, + color: Colors.white, + ), + const SizedBox(width: 5.0), + const Text( + 'Tambah Komentar', + style: TextStyle( + fontSize: 20, + color: Colors.white, + fontWeight: FontWeight.bold), + ), + ], + ), + ), + )), + ], + )), + ], ), - ], - ), + ), + ], ), ), ); @@ -440,9 +428,6 @@ class _DetailPostPageState extends State { newKomentarPostingData, _namaLokasi, widget.komentar.id); if (response['response'] == 'komentar added') { successDialog(context); - Timer(const Duration(seconds: 2), () { - Navigator.pop(context); - }); await _bloc.fetchKomentarPostingList(_namaLokasi, widget.komentar.id); komentarController.clear(); } else { diff --git a/lib/page/filter_fasilitas/postingan/detail_post_kegiatan.dart b/lib/page/filter_fasilitas/postingan/detail_post_kegiatan.dart index fbc07c0..691db2d 100644 --- a/lib/page/filter_fasilitas/postingan/detail_post_kegiatan.dart +++ b/lib/page/filter_fasilitas/postingan/detail_post_kegiatan.dart @@ -48,436 +48,422 @@ class _DetailPostKegiatanPageState extends State { @override Widget build(BuildContext context) { - return WillPopScope( - onWillPop: () => Navigator.of(context).pushReplacement(MaterialPageRoute( - builder: (BuildContext context) => Fasilitas( - lokasi: widget.lokasi, - ))), - child: Scaffold( - appBar: BisaGoAppBar( - title: widget.lokasi.name, - key: Key('appbar-text-${widget.kegiatan.placeId}'), - actions: [ - InkWell( - onTap: () async { - final link = await DynamicLinksServiceRepository() - .createDynamicLinkForKegiatan( - widget.kegiatan.id, - widget.lokasi.placeId, - ); - await Share.share(ShareUtils.getFormattedMessageKegiatan( - widget.kegiatan, widget.lokasi, link)); - }, - child: const Padding( - padding: EdgeInsets.all(doubleSpace), - child: Icon(Icons.share), - ), + return Scaffold( + appBar: BisaGoAppBar( + title: widget.lokasi.name, + key: Key('appbar-text-${widget.kegiatan.placeId}'), + actions: [ + InkWell( + onTap: () async { + final link = await DynamicLinksServiceRepository() + .createDynamicLinkForKegiatan( + widget.kegiatan.id, + widget.lokasi.placeId, + ); + await Share.share(ShareUtils.getFormattedMessageKegiatan( + widget.kegiatan, widget.lokasi, link)); + }, + child: const Padding( + padding: EdgeInsets.all(doubleSpace), + child: Icon(Icons.share), ), - ], - ), - body: SingleChildScrollView( - child: Column( - children: [ - Container( - key: const Key('Text Judul Kegiatan'), - margin: const EdgeInsets.symmetric( - vertical: 10.0, horizontal: 15.0), - alignment: Alignment.centerLeft, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Container( - width: MediaQuery.of(context).size.width * 0.6, - // ganti alias tambahan - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: regularSpace), - Text( - widget.kegiatan - .namaKegiatan, // ganti nama kegiatan // sudah - style: const TextStyle( - fontSize: 30, - fontWeight: FontWeight.w800, - letterSpacing: 0.3, - fontFamily: 'Muli', - // color: Colors.black, - // fontFamily: 'Comfortaa', - ), + ), + ], + ), + body: SingleChildScrollView( + child: Column( + children: [ + Container( + key: const Key('Text Judul Kegiatan'), + margin: + const EdgeInsets.symmetric(vertical: 10.0, horizontal: 15.0), + alignment: Alignment.centerLeft, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.6, + // ganti alias tambahan + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: regularSpace), + Text( + widget.kegiatan + .namaKegiatan, // ganti nama kegiatan // sudah + style: const TextStyle( + fontSize: 30, + fontWeight: FontWeight.w800, + letterSpacing: 0.3, + fontFamily: 'Muli', + // color: Colors.black, + // fontFamily: 'Comfortaa', ), - Text( - 'oleh ${widget.kegiatan.penyelenggara}', // ganti format ${nama penyelenggara} // sudah - style: TextStyle(fontSize: 16), - textAlign: TextAlign.left, - ) - ], - ), + ), + Text( + 'oleh ${widget.kegiatan.penyelenggara}', // ganti format ${nama penyelenggara} // sudah + style: TextStyle(fontSize: 16), + textAlign: TextAlign.left, + ) + ], ), - // PopupMenuButton( - // key: const Key('Button Ubah Informasi'), - // elevation: 4.0, - // offset: const Offset(0.0, 40.0), - // itemBuilder: (BuildContext context) { - // final choices = ['Ubah Informasi']; - // return choices.map((String choice) { - // return PopupMenuItem( - // key: Key(choice), - // child: ElevatedButton( - // style: ButtonStyle( - // padding: MaterialStateProperty.all( - // EdgeInsets.symmetric( - // vertical: 0, horizontal: 0)), - // backgroundColor: - // MaterialStateProperty.all(Colors.white), - // foregroundColor: - // MaterialStateProperty.all(Colors.black), - // elevation: MaterialStateProperty.all(0)), - // onPressed: _updateInformasi, - // child: SizedBox( - // width: double.infinity, - // child: Text(choice), - // ), - // ), - // ); - // }).toList(); - // }, - // ), - ], - ), + ), + // PopupMenuButton( + // key: const Key('Button Ubah Informasi'), + // elevation: 4.0, + // offset: const Offset(0.0, 40.0), + // itemBuilder: (BuildContext context) { + // final choices = ['Ubah Informasi']; + // return choices.map((String choice) { + // return PopupMenuItem( + // key: Key(choice), + // child: ElevatedButton( + // style: ButtonStyle( + // padding: MaterialStateProperty.all( + // EdgeInsets.symmetric( + // vertical: 0, horizontal: 0)), + // backgroundColor: + // MaterialStateProperty.all(Colors.white), + // foregroundColor: + // MaterialStateProperty.all(Colors.black), + // elevation: MaterialStateProperty.all(0)), + // onPressed: _updateInformasi, + // child: SizedBox( + // width: double.infinity, + // child: Text(choice), + // ), + // ), + // ); + // }).toList(); + // }, + // ), + ], ), - Container( - margin: const EdgeInsets.all(doubleSpace), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox( - width: MediaQuery.of(context).size.width, - height: 160, - child: CarouselSlider( - options: CarouselOptions( - aspectRatio: 1.0, - enlargeCenterPage: true, - enableInfiniteScroll: false, - initialPage: 0, - autoPlay: true, - ), - items: widget.kegiatan.image - .map((item) => Container( - child: Container( - child: ClipRRect( - borderRadius: BorderRadius.all( - Radius.circular(20)), - child: Stack( - children: [ - Image.network(item, - fit: BoxFit.cover, - width: 1000.0), - Positioned( - bottom: 0.0, - left: 0.0, - right: 0.0, - child: Container( - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [ - Color.fromARGB( - 200, 0, 0, 0), - Color.fromARGB(0, 0, 0, 0) - ], - begin: - Alignment.bottomCenter, - end: Alignment.topCenter, - ), + ), + Container( + margin: const EdgeInsets.all(doubleSpace), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: MediaQuery.of(context).size.width, + height: 160, + child: CarouselSlider( + options: CarouselOptions( + aspectRatio: 1.0, + enlargeCenterPage: true, + enableInfiniteScroll: false, + initialPage: 0, + autoPlay: true, + ), + items: widget.kegiatan.image + .map((item) => Container( + child: Container( + child: ClipRRect( + borderRadius: + BorderRadius.all(Radius.circular(20)), + child: Stack( + children: [ + Image.network(item, + fit: BoxFit.cover, width: 1000.0), + Positioned( + bottom: 0.0, + left: 0.0, + right: 0.0, + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + Color.fromARGB( + 200, 0, 0, 0), + Color.fromARGB(0, 0, 0, 0) + ], + begin: Alignment.bottomCenter, + end: Alignment.topCenter, ), - padding: EdgeInsets.symmetric( - vertical: 10.0, - horizontal: 20.0), - child: Text( - '#${widget.kegiatan.image.indexOf(item) + 1}', - style: TextStyle( - color: Colors.white, - fontSize: 20.0, - fontWeight: FontWeight.bold, - ), + ), + padding: EdgeInsets.symmetric( + vertical: 10.0, + horizontal: 20.0), + child: Text( + '#${widget.kegiatan.image.indexOf(item) + 1}', + style: TextStyle( + color: Colors.white, + fontSize: 20.0, + fontWeight: FontWeight.bold, ), ), ), - ], - )), - ), - )) - .toList(), - ), - ), - const SizedBox( - height: 10, - ), - Container( - key: const Key('desc'), - // decoration: BoxDecoration( - // color: gray, - // boxShadow: regularShadow, - // borderRadius: regularBorderRadius), - padding: const EdgeInsets.all(doubleSpace), - margin: const EdgeInsets.symmetric(vertical: doubleSpace), - // child: Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // Row( - // children: [ - // const Expanded( - // child: Text( - // 'Cara menggunakan', - // style: TextStyle( - // fontSize: 20, - // fontWeight: FontWeight.w800), - // ), - // ), - // ], - // ), - // Container( - // margin: const EdgeInsets.symmetric( - // vertical: regularSpace - // ), - // ganti alias tambahan - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - // mainAxisAlignment: - // MainAxisAlignment.center, - children: [ - const Icon( - Icons.access_time, - size: 20, - color: Colors.green, - ), - const SizedBox(width: 7.0), - const Text( - 'Senin, 3 Mei 2021', // ganti format widget.x.tanggalpelaksanaan - style: TextStyle(fontSize: 16), - key: Key('Text Waktu Pelaksanaan'), - ), - ], - ), - SizedBox(height: 40), - Text( - widget.kegiatan - .deskripsi, // ganti format widget.x.deskripsi kegiatan // sudah - style: const TextStyle(fontSize: 16), - key: const Key('Text Deskripsi Kegiatan'), - ), - SizedBox(height: 40), - Row( - // mainAxisAlignment: - // MainAxisAlignment.center, - children: [ - const Icon( - Icons.call, - size: 20, - color: Colors.green, - ), - const SizedBox(width: 7.0), - Text( - widget.kegiatan - .narahubung, // ganti format narahubung // sudah - style: TextStyle(fontSize: 16), - key: Key('Text Narahubung'), - ), - ], - ), - ], - ), - // ), - // ], - // ), + ), + ], + )), + ), + )) + .toList(), ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - const Text( - 'informasi ditambahkan oleh ', - style: TextStyle( - fontSize: 12, - fontStyle: FontStyle.italic, - fontWeight: FontWeight.w200, - ), - ), - Container( - padding: EdgeInsets.zero, - constraints: BoxConstraints( - maxWidth: - MediaQuery.of(context).size.width * 0.3), - child: Text( - widget.kegiatan - .creator, // ganti format ${widget.x.creator} // sudah - key: Key( - 'Creator info kegiatan'), // ganti format key creator-${widget.x.creator} - overflow: TextOverflow.fade, - softWrap: false, - style: const TextStyle( - fontSize: 12, - fontStyle: FontStyle.italic, + ), + const SizedBox( + height: 10, + ), + Container( + key: const Key('desc'), + // decoration: BoxDecoration( + // color: gray, + // boxShadow: regularShadow, + // borderRadius: regularBorderRadius), + padding: const EdgeInsets.all(doubleSpace), + margin: const EdgeInsets.symmetric(vertical: doubleSpace), + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Row( + // children: [ + // const Expanded( + // child: Text( + // 'Cara menggunakan', + // style: TextStyle( + // fontSize: 20, + // fontWeight: FontWeight.w800), + // ), + // ), + // ], + // ), + // Container( + // margin: const EdgeInsets.symmetric( + // vertical: regularSpace + // ), + // ganti alias tambahan + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + // mainAxisAlignment: + // MainAxisAlignment.center, + children: [ + const Icon( + Icons.access_time, + size: 20, + color: Colors.green, ), - ), + const SizedBox(width: 7.0), + const Text( + 'Senin, 3 Mei 2021', // ganti format widget.x.tanggalpelaksanaan + style: TextStyle(fontSize: 16), + key: Key('Text Waktu Pelaksanaan'), + ), + ], ), + SizedBox(height: 40), Text( - '22 April 2021', - // '(${DateFormat('dd MMM yyy').format(widget.kegiatan.dateTime)})', // ganti format (${DateFormat('dd MMM yyy').format(widget.x.dateTime)}) - key: const Key('timestamp'), + widget.kegiatan + .deskripsi, // ganti format widget.x.deskripsi kegiatan // sudah + style: const TextStyle(fontSize: 16), + key: const Key('Text Deskripsi Kegiatan'), + ), + SizedBox(height: 40), + Row( + // mainAxisAlignment: + // MainAxisAlignment.center, + children: [ + const Icon( + Icons.call, + size: 20, + color: Colors.green, + ), + const SizedBox(width: 7.0), + Text( + widget.kegiatan + .narahubung, // ganti format narahubung // sudah + style: TextStyle(fontSize: 16), + key: Key('Text Narahubung'), + ), + ], + ), + ], + ), + // ), + // ], + // ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + const Text( + 'informasi ditambahkan oleh ', + style: TextStyle( + fontSize: 12, + fontStyle: FontStyle.italic, + fontWeight: FontWeight.w200, + ), + ), + Container( + padding: EdgeInsets.zero, + constraints: BoxConstraints( + maxWidth: MediaQuery.of(context).size.width * 0.3), + child: Text( + widget.kegiatan + .creator, // ganti format ${widget.x.creator} // sudah + key: Key( + 'Creator info kegiatan'), // ganti format key creator-${widget.x.creator} + overflow: TextOverflow.fade, + softWrap: false, style: const TextStyle( fontSize: 12, fontStyle: FontStyle.italic, ), ), - ], - ), - const SizedBox( - height: regularSpace, - ), - const Divider( - color: grayPrimary, - thickness: 1.0, - ), - Container( - key: const Key('Komentar'), - padding: - const EdgeInsets.symmetric(vertical: regularSpace), - child: const Text( - 'Komentar', - style: TextStyle( - fontSize: 20, fontWeight: FontWeight.w800), ), + Text( + '22 April 2021', + // '(${DateFormat('dd MMM yyy').format(widget.kegiatan.dateTime)})', // ganti format (${DateFormat('dd MMM yyy').format(widget.x.dateTime)}) + key: const Key('timestamp'), + style: const TextStyle( + fontSize: 12, + fontStyle: FontStyle.italic, + ), + ), + ], + ), + const SizedBox( + height: regularSpace, + ), + const Divider( + color: grayPrimary, + thickness: 1.0, + ), + Container( + key: const Key('Komentar'), + padding: const EdgeInsets.symmetric(vertical: regularSpace), + child: const Text( + 'Komentar', + style: + TextStyle(fontSize: 20, fontWeight: FontWeight.w800), ), - StreamBuilder( - stream: _bloc.komentarPostingKegiatanListStream, - builder: (context, snapshot) { - if (snapshot.hasData) { - switch (snapshot.data.status) { - case Status.loading: + ), + StreamBuilder( + stream: _bloc.komentarPostingKegiatanListStream, + builder: (context, snapshot) { + if (snapshot.hasData) { + switch (snapshot.data.status) { + case Status.loading: + return const Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + greenPrimary), + ), + ); + break; + case Status.completed: + allKomentarPositngKegiatanFromApi = snapshot + .data + .data + .allKomentarKegiatan; // kalo error, brarti allKegiatan + if (allKomentarPositngKegiatanFromApi.isEmpty) { return const Center( - child: CircularProgressIndicator( - valueColor: AlwaysStoppedAnimation( - greenPrimary), - ), - ); - break; - case Status.completed: - allKomentarPositngKegiatanFromApi = snapshot - .data - .data - .allKomentarKegiatan; // kalo error, brarti allKegiatan - if (allKomentarPositngKegiatanFromApi.isEmpty) { - return const Center( - child: Text('Tidak ada komentar')); - } else { - return Column( - children: - allKomentarPositngKegiatanFromApi - .map((k) => - komentarKegiatanPlaceHolder( - k.creator, - k.created, - k.deskripsi)) - .toList()); - } - break; - case Status.error: - return Center( - child: Text(snapshot.data.data.toString()), - ); - break; - } + child: Text('Tidak ada komentar')); + } else { + return Column( + children: allKomentarPositngKegiatanFromApi + .map((k) => + komentarKegiatanPlaceHolder( + k.creator, + k.created, + k.deskripsi)) + .toList()); + } + break; + case Status.error: + return Center( + child: Text(snapshot.data.data.toString()), + ); + break; } - return Container(); - }), - const SizedBox(height: regularSpace), - Form( - key: _formKey, - child: Column( - children: [ - TextFormField( - key: const Key('Text Field Komentar'), - keyboardType: TextInputType.multiline, - maxLines: null, - minLines: 3, - validator: FieldValidator.validateInfo, - controller: komentarKegiatanController, - style: const TextStyle( - fontSize: 18, - ), - decoration: InputDecoration( - hintStyle: const TextStyle( - fontWeight: FontWeight.bold, fontSize: 15), - hintText: 'Tulis komentar...', - contentPadding: const EdgeInsets.all(8.0), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide( - color: Theme.of(context).primaryColor, - ), + } + return Container(); + }), + const SizedBox(height: regularSpace), + Form( + key: _formKey, + child: Column( + children: [ + TextFormField( + key: const Key('Text Field Komentar'), + keyboardType: TextInputType.multiline, + maxLines: null, + minLines: 3, + validator: FieldValidator.validateInfo, + controller: komentarKegiatanController, + style: const TextStyle( + fontSize: 18, + ), + decoration: InputDecoration( + hintStyle: const TextStyle( + fontWeight: FontWeight.bold, fontSize: 15), + hintText: 'Tulis komentar...', + contentPadding: const EdgeInsets.all(8.0), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide( + color: Theme.of(context).primaryColor, ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide( - color: Theme.of(context).primaryColor, - ), + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide( + color: Theme.of(context).primaryColor, ), ), ), - Container( - key: const Key('Tambah Komentar'), - padding: - const EdgeInsets.only(top: doubleSpace), - alignment: Alignment.center, - child: ButtonTheme( - minWidth: double.infinity, - height: 40, - child: ElevatedButton( - key: const Key('Button Tambah Komentar'), - style: ButtonStyle( - padding: MaterialStateProperty.all( - EdgeInsets.symmetric(vertical: 13)), - elevation: MaterialStateProperty.all(0.0), - backgroundColor: - MaterialStateProperty.all( - greenPrimary), - shape: MaterialStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(10)))), - ), - onPressed: () { - _checkLoginStatus(); - }, - child: Row( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - const Icon( - Icons.add, - size: 30, - color: Colors.white, - ), - const SizedBox(width: 5.0), - const Text( - 'Tambah Komentar', - style: TextStyle( - fontSize: 20, - color: Colors.white, - fontWeight: FontWeight.bold), - ), - ], - ), + ), + Container( + key: const Key('Tambah Komentar'), + padding: const EdgeInsets.only(top: doubleSpace), + alignment: Alignment.center, + child: ButtonTheme( + minWidth: double.infinity, + height: 40, + child: ElevatedButton( + key: const Key('Button Tambah Komentar'), + style: ButtonStyle( + padding: MaterialStateProperty.all( + EdgeInsets.symmetric(vertical: 13)), + elevation: MaterialStateProperty.all(0.0), + backgroundColor: + MaterialStateProperty.all(greenPrimary), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(10)))), ), - )), - ], - )), - ], - ), + onPressed: () { + _checkLoginStatus(); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon( + Icons.add, + size: 30, + color: Colors.white, + ), + const SizedBox(width: 5.0), + const Text( + 'Tambah Komentar', + style: TextStyle( + fontSize: 20, + color: Colors.white, + fontWeight: FontWeight.bold), + ), + ], + ), + ), + )), + ], + )), + ], ), - ], - ), + ), + ], ), ), ); @@ -513,9 +499,6 @@ class _DetailPostKegiatanPageState extends State { if (response['response'] == 'komentar kegiatan added') { successDialog(context); - Timer(const Duration(seconds: 2), () { - Navigator.pop(context); - }); await _bloc.fetchKomentarPostingKegiatanList( _placeId, widget.kegiatan.id); // ganti widget.x.id // sudah komentarKegiatanController.clear(); -- GitLab From f66e1d9ed1c6bac6fe4f0fc60e94cd7f7e520c83 Mon Sep 17 00:00:00 2001 From: Yoga Pratama Date: Sun, 23 May 2021 18:04:56 +0700 Subject: [PATCH 09/19] [CHORES] Remove unused import --- lib/page/filter_fasilitas/postingan/detail_post.dart | 1 - lib/page/filter_fasilitas/postingan/detail_post_kegiatan.dart | 2 -- 2 files changed, 3 deletions(-) diff --git a/lib/page/filter_fasilitas/postingan/detail_post.dart b/lib/page/filter_fasilitas/postingan/detail_post.dart index 39ccab1..e17b8e5 100644 --- a/lib/page/filter_fasilitas/postingan/detail_post.dart +++ b/lib/page/filter_fasilitas/postingan/detail_post.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:bisaGo/config/strings.dart'; import 'package:bisaGo/model/lokasi.dart'; -import 'package:bisaGo/page/filter_fasilitas/fasilitas.dart'; import 'package:bisaGo/page/updateInformasi/update_informasi.dart'; import 'package:bisaGo/repository/dynamic_links_service_repository.dart'; import 'package:bisaGo/utils/share_utils.dart'; diff --git a/lib/page/filter_fasilitas/postingan/detail_post_kegiatan.dart b/lib/page/filter_fasilitas/postingan/detail_post_kegiatan.dart index 691db2d..2054000 100644 --- a/lib/page/filter_fasilitas/postingan/detail_post_kegiatan.dart +++ b/lib/page/filter_fasilitas/postingan/detail_post_kegiatan.dart @@ -19,8 +19,6 @@ import 'package:bisaGo/page/login/login.dart'; import 'package:share/share.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import '../fasilitas.dart'; - class DetailPostKegiatanPage extends StatefulWidget { final Lokasi lokasi; final KegiatanModel kegiatan; // ganti model x // sudah -- GitLab From 7cfb743dfb4bee18afb5ba5476674a4e6bbb8f16 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Mon, 24 May 2021 20:50:03 +0700 Subject: [PATCH 10/19] [CHORE] fix can't show notification icon set default firebase messaging default notification icon --- android/app/src/main/AndroidManifest.xml | 8 ++++++++ .../main/res/mipmap-hdpi/transparent_icon.png | Bin 0 -> 1960 bytes android/app/src/main/res/values/colors.xml | 1 + 3 files changed, 9 insertions(+) create mode 100644 android/app/src/main/res/mipmap-hdpi/transparent_icon.png diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 7e0e18f..4207a35 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -41,5 +41,13 @@ android:value="2" /> + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/transparent_icon.png b/android/app/src/main/res/mipmap-hdpi/transparent_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b68a7d08ba59bd73f753fe67c2cdebc9b4bb3689 GIT binary patch literal 1960 zcmV;Z2UqxsP)!+u3R_%QsGN=)I?9KY^vWrsWZQl+#>(O+`=4^ij#ix zs5;Rsu0DSG_!Y-RhbN{W=2we7huW^*c&7Ud?#i zjKZHD{ycBxV>uIdB*BsJ%>6YJZx8h2CBFUZr|<|=qFMmZI<)l%|9%jc{&5M{{nv5g z>IrBHH3tH9a;rRH_u1Vi-*&xyWP@YF_Xr_Go-cpR>b2f&A85z5!D}eKT#UHFxK9Cm zn-D@oqnMWDOhR>6HGD%poWFG*Ii4Kw5`X`hm*h;s%-js(ZSk8QXI_n6gObZ7=p5`s z`#?J!7aWK#hz2#%aPtG8{A|1MN-CWi6Dhy2yA;F$t2wb+T zv+Qp1)#96~0@d?!t308otq2_h9nh8OkajW+?vLH*9qk1*(K@+VzAU{W9aFPYINW*| zub+4w*|pi|8R>zi+XGJGKnNju4SDePcp;j_)xmR76I~Tq5E&^q%jJSi_$eWTXdT+I zvAbiayjBTanGWd{={WnzSqx4LLS3jvsx9^DCl-*)=K^QqT!au3bu{Wn)I=>6ofU9( zxsdEkUcwvszm#>9;e)FmOmT%=n3ylFZ@txu98V6Yk*22FQlF+0ZAd(t2wko2DZwOQ zcxo8gwb`I1+Cj*de@CTN3IC)Y+fQ!?FY#L)7aXYQtoTB-h;f0Llm$vYuQ9LN)NE=r zH=9rCEA$7bMAdbl*1>tn3C_sf5NyIyLI~ka{GiL{f~(5~&d6mfen9!+dH_f%PMHi| z*OqF3ka9ewVsd5@ssh#X)JSv7I?7OdxfoGWRD{B$2-R2VF*G@ZW0#I$XU$Hmwyy?n zpuYvBCYZ8&slw?=`-Mrq7y2BMTxUg92J(Z?z8>wG}m= zyLryn?r&d^o8|I5Gk2glqS>J>)qXd)XG(DjCTAu=jr8)O2PCt|*32!;L3>ndet7TY zqL+`&&d-7xX$)uNq(DG2lT0Aw%Qu_as>Z&WeW<)xiQE3$ur^rnqVdJFH_P8d?`ZG; z1VjiShZ+vS+v^3DsQ1-lueMFkO+#bTs2-RrU#1bvLi8@rt{t6&oqYSDt$nW6M+}aGKLD?-c$k21E2l1Bp2l{dnhvuGUczeCz zjeLz@65`L@Ja@t6bCq(1T-d|&89au)9RnTXyu>TJKkr6XWmd7=EMFE}lUtwL)aCDb zG6Ai!t@=gA>5MNXW+tGk(9KhcmU9w!YSV|Cnnv%ABE2%*M+hODi96{$??hfj9>O!j zv%|B(M@wo;ur_xsLi8cEA(Y$$pfcDTN|OffajKv{EC5 z)oaDf+zhNzB7!CCpK=2lA2c(^a|Tp))pLXpvV8aQ>OIAKU^r?(XlCfP(9F;yTP#~p-&_xOqZ`%r)o5&K zL|T3tBJ>ff2q7dqD;#@E_M)Y&1y%J`XuH~m+KyV7o6Kmu*$6=r3Ij7}iZqdRH|kvT z|DK=i8|&-u9qs*meqkOR{*IC4JmY?^0P`Kq5EY&iJ0iRlUSPxg=br~LOu?vB7v zW7y3b`I;vZP5?F17i9UeZOP8$+*e9o*(aEVHU0Jg^p? z75;vhJ`BGNIYx!GBNx37q ze=x%-!!x>a-O9x3#AgX1L|dx;eqay4w+JCb@6<0zElvF)HPRR2EOEbF>^vBTVHk#C u7=~dOhG7_nVHk#C7=~dOhG7`y8|*(S8W3s>-LtL$0000 #3A903A + #00FF00 -- GitLab From b9ccf68b7a22ee403059ea7f9ac8ab3482ee869c Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Mon, 24 May 2021 22:08:35 +0700 Subject: [PATCH 11/19] [CHORE] replaced default transparent icon with new icon --- .../main/res/mipmap-hdpi/transparent_icon.png | Bin 1960 -> 20706 bytes .../main/res/mipmap-mdpi/transparent_icon.png | Bin 0 -> 20706 bytes .../res/mipmap-xhdpi/transparent_icon.png | Bin 0 -> 20706 bytes .../res/mipmap-xxhdpi/transparent_icon.png | Bin 0 -> 20706 bytes .../res/mipmap-xxxhdpi/transparent_icon.png | Bin 0 -> 20706 bytes 5 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 android/app/src/main/res/mipmap-mdpi/transparent_icon.png create mode 100644 android/app/src/main/res/mipmap-xhdpi/transparent_icon.png create mode 100644 android/app/src/main/res/mipmap-xxhdpi/transparent_icon.png create mode 100644 android/app/src/main/res/mipmap-xxxhdpi/transparent_icon.png diff --git a/android/app/src/main/res/mipmap-hdpi/transparent_icon.png b/android/app/src/main/res/mipmap-hdpi/transparent_icon.png index b68a7d08ba59bd73f753fe67c2cdebc9b4bb3689..f8fddc349c3a2e79088ca668cc9455ba3f3ae59e 100644 GIT binary patch literal 20706 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 Date: Mon, 24 May 2021 22:19:23 +0700 Subject: [PATCH 12/19] [CHORE] change accent color same as icon color --- android/app/src/main/res/values/colors.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml index a3ce57c..368aefd 100644 --- a/android/app/src/main/res/values/colors.xml +++ b/android/app/src/main/res/values/colors.xml @@ -1,4 +1,4 @@ #3A903A - #00FF00 + #3A903A -- GitLab From 1117de4f9175457a85042d5387af8e2daba00613 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Mon, 24 May 2021 23:39:13 +0700 Subject: [PATCH 13/19] [CHORE] changelogs.txt 3.4.0 - Notification when new komentar is posted --- android/fastlane/metadata/android/id/changelogs/changelogs.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/android/fastlane/metadata/android/id/changelogs/changelogs.txt b/android/fastlane/metadata/android/id/changelogs/changelogs.txt index 659bd95..6e0cd42 100644 --- a/android/fastlane/metadata/android/id/changelogs/changelogs.txt +++ b/android/fastlane/metadata/android/id/changelogs/changelogs.txt @@ -1,3 +1,6 @@ +3.4.0: +- Notification when new komentar is posted + 3.3.0: - New feature for add kegiatan - Fix bugs -- GitLab From 7ea7884efb53c46301c654e5be301c61f8f220a8 Mon Sep 17 00:00:00 2001 From: Yoga Pratama Date: Thu, 27 May 2021 13:38:53 +0700 Subject: [PATCH 14/19] [CHORES] Change main and main_staging --- lib/main.dart | 15 +++++++++++++-- lib/main_staging.dart | 15 +++++++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 1d165ee..aa484a7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ -import 'package:bisaGo/get_it.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:bisaGo/app.dart'; import 'package:intl/date_symbol_data_local.dart'; @@ -6,14 +7,24 @@ import 'package:intl/intl.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'flavor/flavor.dart'; import 'globalnetwork.dart'; +import 'package:bisaGo/get_it.dart'; + +Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { + // If you're going to use other Firebase services in the background, such as Firestore, + // make sure you call `initializeApp` before using other Firebase services. + await Firebase.initializeApp(); + + print('Handling a background message: ${message.messageId}'); +} Future main() async { AppGetIt().initialize(); await DotEnv().load('.env'); getDioInstance('build'); await initializeDateFormatting('id_ID', null); + FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); Intl.defaultLocale = 'id_ID'; dio.options.receiveTimeout = 15000; - ApiFlavor.flavor = BuildFlavor.production.toString(); + ApiFlavor.flavor = BuildFlavor.development.toString(); runApp(BisaGo()); } diff --git a/lib/main_staging.dart b/lib/main_staging.dart index 564412c..aa484a7 100644 --- a/lib/main_staging.dart +++ b/lib/main_staging.dart @@ -1,3 +1,5 @@ +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:bisaGo/app.dart'; import 'package:intl/date_symbol_data_local.dart'; @@ -7,13 +9,22 @@ import 'flavor/flavor.dart'; import 'globalnetwork.dart'; import 'package:bisaGo/get_it.dart'; +Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { + // If you're going to use other Firebase services in the background, such as Firestore, + // make sure you call `initializeApp` before using other Firebase services. + await Firebase.initializeApp(); + + print('Handling a background message: ${message.messageId}'); +} + Future main() async { AppGetIt().initialize(); await DotEnv().load('.env'); getDioInstance('build'); await initializeDateFormatting('id_ID', null); + FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); Intl.defaultLocale = 'id_ID'; dio.options.receiveTimeout = 15000; - ApiFlavor.flavor = BuildFlavor.staging.toString(); + ApiFlavor.flavor = BuildFlavor.development.toString(); runApp(BisaGo()); -} \ No newline at end of file +} -- GitLab From f7061b7b92196782d2758d8e58e31165fb714ff8 Mon Sep 17 00:00:00 2001 From: Yoga Pratama Date: Mon, 31 May 2021 15:12:09 +0700 Subject: [PATCH 15/19] [CHORES] Trigger request fcm token on login --- lib/page/login/login.dart | 46 ++++++++++++++++++++++------- lib/repository/user_repository.dart | 3 +- test/login_test.dart | 12 ++++++++ 3 files changed, 49 insertions(+), 12 deletions(-) diff --git a/lib/page/login/login.dart b/lib/page/login/login.dart index 5749677..fcd2dfd 100644 --- a/lib/page/login/login.dart +++ b/lib/page/login/login.dart @@ -1,10 +1,13 @@ import 'dart:async'; import 'dart:convert'; +import 'package:bisaGo/bloc/cloud_messaging_bloc.dart'; import 'package:bisaGo/bloc/new_user_bloc.dart'; import 'package:bisaGo/bloc/user_bloc.dart'; import 'package:bisaGo/model/new_user.dart'; import 'package:bisaGo/page/dashboard/dashboard.dart'; import 'package:bisaGo/page/login/pilih_disabilitas.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -35,6 +38,10 @@ class LoginState extends State { GoogleSignInAccount _currentUser; SharedPreferences sharedPreferences; + FirebaseMessaging _firebaseMessaging; + + CloudMessagingBloc cloudMessagingBloc = CloudMessagingBloc(); + @override void initState() { super.initState(); @@ -159,12 +166,12 @@ class LoginState extends State { height: 64.0, child: TextButton( style: TextButton.styleFrom( - backgroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(5.0), - side: const BorderSide(color: greenPrimary)), - padding: const EdgeInsets.symmetric(vertical: 10.0) - ), + backgroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5.0), + side: const BorderSide(color: greenPrimary)), + padding: + const EdgeInsets.symmetric(vertical: 10.0)), onPressed: () async { Navigator.of(context).pop(true); await _updateUser(newUser); @@ -189,11 +196,11 @@ class LoginState extends State { height: 64.0, child: TextButton( style: TextButton.styleFrom( - backgroundColor: greenPrimary, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(5.0)), - padding: const EdgeInsets.symmetric(vertical: 10.0) - ), + backgroundColor: greenPrimary, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5.0)), + padding: + const EdgeInsets.symmetric(vertical: 10.0)), onPressed: () { Navigator.of(context).pop(true); _navigateToPilihDisabilitas(context); @@ -286,12 +293,15 @@ class LoginState extends State { final response = await http .post('${ApiFlavor.getBaseUrl()}/api-token-auth/', body: data); if (response.statusCode == 200) { + print('here'); final tokenMap = jsonDecode(response.body); setState(() { sharedPreferences ..setString('token', tokenMap['token']) ..setString('email', email); }); + await _requestFCMToken(); + successDialog(context); _navigateToDashboard(context); } else { @@ -372,6 +382,7 @@ class LoginState extends State { await googleSignInAccount.authentication; final token = googleSignInAuthentication.accessToken; + print(token); await login( _currentUser.email, '', 'true', token, _currentUser.displayName); sharedPreferences = await SharedPreferences.getInstance(); @@ -384,6 +395,19 @@ class LoginState extends State { } } + Future _requestFCMToken() async { + await Firebase.initializeApp(); + + _firebaseMessaging = FirebaseMessaging.instance; + + final fcmToken = await _firebaseMessaging.getToken(); + final sharedPreferences = await SharedPreferences.getInstance(); + final token = sharedPreferences.getString('token'); + if (token != null) { + await cloudMessagingBloc.sendFCMToken(fcmToken, token); + } + } + Future _handleSignOut() async { await _googleSignIn.signOut(); } diff --git a/lib/repository/user_repository.dart b/lib/repository/user_repository.dart index 4ced6b3..dd7caf0 100644 --- a/lib/repository/user_repository.dart +++ b/lib/repository/user_repository.dart @@ -9,13 +9,14 @@ abstract class BaseUserRepository { Future createUser(NewUser newUser); Future updateUser(NewUser newUser); } + class UserRepository implements BaseUserRepository { final NetworkInterface _network = NetworkInterface(); @override Future fetchUserDetail(String email) async { final response = - await _network.get(url: '/api/user-detail/$email', isLogin: true); + await _network.get(url: '/api/user/$email/', isLogin: true); final data = [response]; return User( data.map((user) => UserModel.fromJson(user)).toList()); diff --git a/test/login_test.dart b/test/login_test.dart index dbea2e8..2573a52 100644 --- a/test/login_test.dart +++ b/test/login_test.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:bisaGo/model/user.dart'; +import 'package:bisaGo/repository/cloud_messaging_repository.dart'; import 'package:bisaGo/repository/user_repository.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -13,6 +14,14 @@ import 'package:shared_preferences/shared_preferences.dart'; class MockNetwork extends Mock implements NetworkInterface {} +class MockCloudMessagingRepository extends Fake + implements CloudMessagingRepository { + @override + Future sendFCMToken(String fcmToken, String token) async { + return Future.value(true); + } +} + class MockUserRepository extends Fake implements UserRepository { final userData = { 'is_login': true, @@ -41,6 +50,9 @@ void main() { .registerLazySingleton(() => MockUserRepository()); SharedPreferences.setMockInitialValues( {'email': 'test@gmail.com', 'token': 'token'}); + + _getIt.registerLazySingleton( + () => MockCloudMessagingRepository()); // mockNetwork = MockNetwork(); // when(mockNetwork.get(isLogin: false, url: anyNamed('url'))) // .thenAnswer((_) async { -- GitLab From bcb608274d50de21ccb653f883e6b43451148ae8 Mon Sep 17 00:00:00 2001 From: Yoga Pratama Date: Mon, 31 May 2021 15:44:29 +0700 Subject: [PATCH 16/19] [CHORES] Change date format --- lib/config/custom_serializer.dart | 2 +- lib/page/filter_fasilitas/postingan/detail_post.dart | 2 +- lib/page/login/login.dart | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/config/custom_serializer.dart b/lib/config/custom_serializer.dart index 00ed551..3f2f901 100644 --- a/lib/config/custom_serializer.dart +++ b/lib/config/custom_serializer.dart @@ -2,6 +2,6 @@ import 'package:intl/intl.dart'; class CustomSerializer { static DateTime stringToDateTime(String date) { - return DateFormat('yyyy-MM-dd hh:mm').parse(date); + return DateFormat('dd-MM-yyyy hh:mm:ss').parse(date); } } diff --git a/lib/page/filter_fasilitas/postingan/detail_post.dart b/lib/page/filter_fasilitas/postingan/detail_post.dart index e17b8e5..c5dac12 100644 --- a/lib/page/filter_fasilitas/postingan/detail_post.dart +++ b/lib/page/filter_fasilitas/postingan/detail_post.dart @@ -482,7 +482,7 @@ class _DetailPostPageState extends State { name, style: const TextStyle(fontSize: 18), ), - Text('${DateFormat('dd MMMM yyy hh:mm').format(date)}', + Text('${DateFormat('dd MMMM yyyy hh:mm').format(date)}', style: const TextStyle(color: grayPrimary, fontSize: 14)) ], ), diff --git a/lib/page/login/login.dart b/lib/page/login/login.dart index fcd2dfd..67aaa2e 100644 --- a/lib/page/login/login.dart +++ b/lib/page/login/login.dart @@ -293,7 +293,6 @@ class LoginState extends State { final response = await http .post('${ApiFlavor.getBaseUrl()}/api-token-auth/', body: data); if (response.statusCode == 200) { - print('here'); final tokenMap = jsonDecode(response.body); setState(() { sharedPreferences @@ -382,7 +381,6 @@ class LoginState extends State { await googleSignInAccount.authentication; final token = googleSignInAuthentication.accessToken; - print(token); await login( _currentUser.email, '', 'true', token, _currentUser.displayName); sharedPreferences = await SharedPreferences.getInstance(); -- GitLab From 3f4ddbbd075d937464f903b0e88bf9ed131bb3eb Mon Sep 17 00:00:00 2001 From: Yoga Pratama Date: Mon, 31 May 2021 16:26:56 +0700 Subject: [PATCH 17/19] [CHORES] Change dropdown banner to flushbar --- lib/app.dart | 8 +----- lib/page/dashboard/dashboard.dart | 41 ++++++++++++++++--------------- pubspec.yaml | 2 +- 3 files changed, 23 insertions(+), 28 deletions(-) diff --git a/lib/app.dart b/lib/app.dart index c572bb9..11dd997 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -1,4 +1,3 @@ -import 'package:dropdown_banner/dropdown_banner.dart'; import 'package:flutter/material.dart'; import 'package:bisaGo/config/styles.dart'; import 'package:bisaGo/page/dashboard/dashboard.dart'; @@ -8,8 +7,6 @@ class BisaGo extends StatelessWidget { @override Widget build(BuildContext context) { - final navigatorKey = GlobalKey(); - return MaterialApp( title: 'bisaGo', theme: ThemeData( @@ -17,10 +14,7 @@ class BisaGo extends StatelessWidget { primaryColor: greenPrimary, backgroundColor: Colors.white, ), - home: DropdownBanner( - navigatorKey: navigatorKey, - child: Dashboard(), - ), + home: Dashboard(), ); } } diff --git a/lib/page/dashboard/dashboard.dart b/lib/page/dashboard/dashboard.dart index e1daa0f..4376297 100644 --- a/lib/page/dashboard/dashboard.dart +++ b/lib/page/dashboard/dashboard.dart @@ -12,10 +12,10 @@ import 'package:bisaGo/page/filter_fasilitas/postingan/detail_post.dart'; import 'package:bisaGo/repository/komentar_repository.dart'; import 'package:bisaGo/utils/custom_dashboard_location_button.dart'; import 'package:bisaGo/utils/location_turn_on_dialog.dart'; -import 'package:dropdown_banner/dropdown_banner.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_dynamic_links/firebase_dynamic_links.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flushbar/flushbar.dart'; import 'package:flutter/material.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:geolocator/geolocator.dart'; @@ -79,25 +79,26 @@ class DashboardState extends State { _lastNotification = now; final data = message.data; final String msg = data['message']; - DropdownBanner.showBanner( - text: msg, - color: Color(0xFF003566), - textStyle: TextStyle( - color: Colors.white, - height: 2.1, - ), - duration: Duration(seconds: 8), - tapCallback: () { - if (data['type'] == 'fasilitas') { - final String placeId = data['place_id']; - final id = int.parse(data['id']); - _navigateToDetailFasilitasPage(context, placeId, id); - } else if (data['type'] == 'kegiatan') { - final String placeId = data['place_id']; - final id = int.parse(data['id']); - _navigateToDetailKegiatanPage(context, placeId, id); - } - }); + final String title = data['title']; + Flushbar( + title: title, + message: '"$msg"', + duration: Duration(seconds: 8), + backgroundColor: Color(0xFF003566), + onTap: (_) { + if (data['type'] == 'fasilitas') { + final String placeId = data['place_id']; + final id = int.parse(data['id']); + _navigateToDetailFasilitasPage(context, placeId, id); + } else if (data['type'] == 'kegiatan') { + final String placeId = data['place_id']; + final id = int.parse(data['id']); + _navigateToDetailKegiatanPage(context, placeId, id); + } + }, + flushbarStyle: FlushbarStyle.GROUNDED, + flushbarPosition: FlushbarPosition.TOP, + ).show(context); }); FirebaseMessaging.onMessageOpenedApp.listen((message) { diff --git a/pubspec.yaml b/pubspec.yaml index c7a2c05..14d76cf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -57,7 +57,7 @@ dependencies: firebase_dynamic_links: ^0.7.0+1 firebase_messaging: ^8.0.0-dev.15 carousel_slider: ^3.0.0 - dropdown_banner: ^1.4.0 + flushbar: ^1.10.4 dev_dependencies: flutter_test: -- GitLab From 1bc264eee520e1634cca0508ae4732222a92c326 Mon Sep 17 00:00:00 2001 From: Yoga Pratama Date: Mon, 31 May 2021 17:40:48 +0700 Subject: [PATCH 18/19] [CHORES] Change date_time field to created --- lib/config/custom_serializer.dart | 2 +- lib/model/komentar_posting.dart | 11 ++++++++--- lib/model/komentar_posting.g.dart | 4 ++-- lib/model/komentar_posting_kegiatan.dart | 13 +++++++------ lib/model/komentar_posting_kegiatan.g.dart | 2 +- lib/page/dashboard/dashboard.dart | 2 +- .../filter_fasilitas/postingan/detail_post.dart | 2 +- lib/repository/kegiatan_repository.dart | 8 +++++--- test/model_test.dart | 8 ++++---- 9 files changed, 30 insertions(+), 22 deletions(-) diff --git a/lib/config/custom_serializer.dart b/lib/config/custom_serializer.dart index 3f2f901..458c5a4 100644 --- a/lib/config/custom_serializer.dart +++ b/lib/config/custom_serializer.dart @@ -2,6 +2,6 @@ import 'package:intl/intl.dart'; class CustomSerializer { static DateTime stringToDateTime(String date) { - return DateFormat('dd-MM-yyyy hh:mm:ss').parse(date); + return DateFormat('dd-MM-yyyy hh:mm').parse(date); } } diff --git a/lib/model/komentar_posting.dart b/lib/model/komentar_posting.dart index 04ffea6..465d069 100644 --- a/lib/model/komentar_posting.dart +++ b/lib/model/komentar_posting.dart @@ -14,10 +14,15 @@ class KomentarPostingModel { final int id; final String deskripsi; final String creator; - @JsonKey(name: 'date_time', fromJson: CustomSerializer.stringToDateTime) - final DateTime dateTime; + @JsonKey(fromJson: CustomSerializer.stringToDateTime) + final DateTime created; - KomentarPostingModel({this.id, this.deskripsi, this.creator, this.dateTime}); + KomentarPostingModel({ + this.id, + this.deskripsi, + this.creator, + this.created, + }); factory KomentarPostingModel.fromJson(Map json) => _$KomentarPostingModelFromJson(json); diff --git a/lib/model/komentar_posting.g.dart b/lib/model/komentar_posting.g.dart index 565bcd3..0670572 100644 --- a/lib/model/komentar_posting.g.dart +++ b/lib/model/komentar_posting.g.dart @@ -27,7 +27,7 @@ KomentarPostingModel _$KomentarPostingModelFromJson(Map json) { id: json['id'] as int, deskripsi: json['deskripsi'] as String, creator: json['creator'] as String, - dateTime: CustomSerializer.stringToDateTime(json['date_time'] as String), + created: CustomSerializer.stringToDateTime(json['created'] as String), ); } @@ -37,5 +37,5 @@ Map _$KomentarPostingModelToJson( 'id': instance.id, 'deskripsi': instance.deskripsi, 'creator': instance.creator, - 'date_time': instance.dateTime?.toIso8601String(), + 'created': instance.created?.toIso8601String(), }; diff --git a/lib/model/komentar_posting_kegiatan.dart b/lib/model/komentar_posting_kegiatan.dart index 78e18d7..1269bd4 100644 --- a/lib/model/komentar_posting_kegiatan.dart +++ b/lib/model/komentar_posting_kegiatan.dart @@ -1,3 +1,4 @@ +import 'package:intl/intl.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:bisaGo/config/custom_serializer.dart'; @@ -14,18 +15,18 @@ class KomentarPostingKegiatanModel { final int id; final String creator; final String deskripsi; - @JsonKey(name: 'created', fromJson: CustomSerializer.stringToDateTime) + @JsonKey(name: 'created', fromJson: _stringToDateTime) final DateTime created; KomentarPostingKegiatanModel( - {this.id, - this.creator, - this.deskripsi, - this.created} - ); + {this.id, this.creator, this.deskripsi, this.created}); factory KomentarPostingKegiatanModel.fromJson(Map json) => _$KomentarPostingKegiatanModelFromJson(json); Map toJson() => _$KomentarPostingKegiatanModelToJson(this); } + +DateTime _stringToDateTime(String date) { + return DateFormat('yyy-MM-dd hh:mm').parse(date); +} diff --git a/lib/model/komentar_posting_kegiatan.g.dart b/lib/model/komentar_posting_kegiatan.g.dart index 3b73f1a..4959621 100644 --- a/lib/model/komentar_posting_kegiatan.g.dart +++ b/lib/model/komentar_posting_kegiatan.g.dart @@ -29,7 +29,7 @@ KomentarPostingKegiatanModel _$KomentarPostingKegiatanModelFromJson( id: json['id'] as int, creator: json['creator'] as String, deskripsi: json['deskripsi'] as String, - created: CustomSerializer.stringToDateTime(json['created'] as String), + created: _stringToDateTime(json['created'] as String), ); } diff --git a/lib/page/dashboard/dashboard.dart b/lib/page/dashboard/dashboard.dart index 4376297..f6915b4 100644 --- a/lib/page/dashboard/dashboard.dart +++ b/lib/page/dashboard/dashboard.dart @@ -82,7 +82,7 @@ class DashboardState extends State { final String title = data['title']; Flushbar( title: title, - message: '"$msg"', + message: msg, duration: Duration(seconds: 8), backgroundColor: Color(0xFF003566), onTap: (_) { diff --git a/lib/page/filter_fasilitas/postingan/detail_post.dart b/lib/page/filter_fasilitas/postingan/detail_post.dart index c5dac12..c8abdb5 100644 --- a/lib/page/filter_fasilitas/postingan/detail_post.dart +++ b/lib/page/filter_fasilitas/postingan/detail_post.dart @@ -296,7 +296,7 @@ class _DetailPostPageState extends State { return Column( children: allKomentarPostingFromApi .map((k) => komentarPlaceHolder( - k.creator, k.dateTime, k.deskripsi)) + k.creator, k.created, k.deskripsi)) .toList()); } break; diff --git a/lib/repository/kegiatan_repository.dart b/lib/repository/kegiatan_repository.dart index e8fdb53..b1f969a 100644 --- a/lib/repository/kegiatan_repository.dart +++ b/lib/repository/kegiatan_repository.dart @@ -55,10 +55,12 @@ class KegiatanRepository implements BaseKegiatanRepository { ); return response; } - + @override - Future fetchDetailKegiatan(String placeId, int kegiatanId) async { - final url = '/informasi-fasilitas/lokasi/detail-kegiatan/$placeId/$kegiatanId/'; + Future fetchDetailKegiatan( + String placeId, int kegiatanId) async { + final url = + '/informasi-fasilitas/lokasi/detail-kegiatan/$placeId/$kegiatanId/'; final response = await _network.get(url: url, isLogin: false); var kegiatan = KegiatanModel.fromJson(response); kegiatan.image = await fetchImages(kegiatan.placeId, kegiatan.id); diff --git a/test/model_test.dart b/test/model_test.dart index 9978873..cece1d5 100644 --- a/test/model_test.dart +++ b/test/model_test.dart @@ -9,13 +9,13 @@ void main() { 'id': 1, 'deskripsi': 'This is a test', 'creator': 'Test', - 'date_time': '2020-11-18 00:13:52.939668' + 'created': '18-11-2020 00:13:52' }; final returnKomentarPostingData = { 'id': 1, 'deskripsi': 'This is a test', 'creator': 'Test', - 'date_time': '2020-11-18T00:13:00.000' + 'created': '2020-11-18T00:13:00.000' }; final userData = { 'is_login': true, @@ -36,7 +36,7 @@ void main() { 'deskripsi': 'Ada toilet khusus disabilitas terletak di lantai 2 dekat kintan', 'creator': '', - 'date_time': '2020-11-18 00:13:52.939668', + 'date_time': '18-11-2020 00:13:52', 'rating': 3, 'tag': 'KR', 'disabilitas': ['DF'], @@ -77,7 +77,7 @@ void main() { id: 2, deskripsi: 'This is a test', creator: 'Test', - dateTime: DateTime.now()); + created: DateTime.now()); expect(komentarPostingModel, isInstanceOf()); expect( komentarPostingWithConstructor, isInstanceOf()); -- GitLab From 210142da1e678a23cbfba6ffd18a6515cba7d427 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Mon, 31 May 2021 20:57:38 +0700 Subject: [PATCH 19/19] [CHORES] fix linter and add changelogs 3.4.0 --- .../android/id/changelogs/changelogs.txt | 22 +++++++++---------- lib/model/komentar_posting_kegiatan.dart | 1 - 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/android/fastlane/metadata/android/id/changelogs/changelogs.txt b/android/fastlane/metadata/android/id/changelogs/changelogs.txt index 659bd95..6264239 100644 --- a/android/fastlane/metadata/android/id/changelogs/changelogs.txt +++ b/android/fastlane/metadata/android/id/changelogs/changelogs.txt @@ -1,20 +1,20 @@ +3.4.0: +- Notifikasi pengguna untuk komentar baru + 3.3.0: -- New feature for add kegiatan -- Fix bugs +- Kegiatan disabilitas di suatu lokasi +- Perbaikan bugs 3.2.1: -- Fix cant update fasilitas +- Perbaikan masalah update fasilitas 3.2.0: -- New feature share fasilitas as a link +- Tersedia fitur membagikan informasi fasilitas/kegiatan disabilitas kepada orang lain 3.1.2: -- Fix google oauth +- Perbaikan masuk dengan Google 3.1.1: -- Add dialog to turn on Location services upon starting the app -- New feature for layanan search -- Add feature to upload image for a fasilitas straight from the camera -- Add default image for facilities -- Add search history feature -- Now all fields are required in edit profile +- Tersedia fitur pencarian layanan +- Tersedia pilihan kamera ketika menambahkan gambar fasilitas +- Tersedia fitur riwayat pencarian diff --git a/lib/model/komentar_posting_kegiatan.dart b/lib/model/komentar_posting_kegiatan.dart index 1269bd4..4f668c1 100644 --- a/lib/model/komentar_posting_kegiatan.dart +++ b/lib/model/komentar_posting_kegiatan.dart @@ -1,6 +1,5 @@ import 'package:intl/intl.dart'; import 'package:json_annotation/json_annotation.dart'; -import 'package:bisaGo/config/custom_serializer.dart'; part 'komentar_posting_kegiatan.g.dart'; -- GitLab