Fakultas Ilmu Komputer UI

Commit 864e89e1 authored by Muhammad Ariq Basyar's avatar Muhammad Ariq Basyar
Browse files

[GREEN] implemented new komentar model and finalize PBI 10

- new attribute on komentar model 'creator_picture'
- implemented viewable profile picture at profile and komentar
parent bb9eda55
......@@ -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
......@@ -6,4 +6,9 @@
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
</manifest>
......@@ -8,7 +8,13 @@
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:requestLegacyExternalStorage="true"
android:name="io.flutter.app.FlutterApplication"
android:label="bisaGo"
android:icon="@mipmap/launcher_icon">
......@@ -33,6 +39,10 @@
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="FLUTTER_NOTIFICATION_CLICK"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
......@@ -41,5 +51,13 @@
android:value="2" />
<meta-data android:name="com.google.android.geo.API_KEY"
android:value="${MAPS_API_KEY}"/>
<!-- Set custom default icon. This is used when no icon is set for incoming notification messages. -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@mipmap/transparent_icon" />
<!-- Set color used with incoming notification messages. This is used when no color is set for the incoming notification message. -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/color_accent" />
</application>
</manifest>
<resources>
<color name="splash_color">#3A903A</color>
<color name="color_accent">#3A903A</color>
</resources>
......@@ -6,4 +6,9 @@
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
</manifest>
3.3.0:
- New feature for add kegiatan
- Fix bugs
3.2.1:
- Fix cant update fasilitas
3.6.0:
- Tersedia nama dan foto profil pada drawer
- Tersedia daftar agenda kegiatan
- Pengguna dapat mengganti foto profil
- Pengguna dapat mengubah informasi kegiatan yang sudah ada
3.2.0:
- New feature share fasilitas as a link
3.4.0:
- Notifikasi pengguna untuk komentar baru
3.1.2:
- Fix google oauth
3.3.0:
- Kegiatan disabilitas di suatu lokasi
- Perbaikan bugs
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
3.2.0:
- Tersedia fitur membagikan informasi fasilitas/kegiatan disabilitas kepada orang lain
\ No newline at end of file
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<BaseCloudMessagingRepository>();
}
Future<dynamic> sendFCMToken(
String fcmToken,
String token,
) async {
try {
return await _cloudMessagingRepository.sendFCMToken(
fcmToken,
token,
);
} catch (e) {
return Response('Failed to add komentar', 400);
}
}
}
......@@ -36,14 +36,38 @@ class KegiatanBloc {
}
Future<dynamic> addNewKegiatan(
Map<String, dynamic> newKegiatanData, String placeId) async {
Map<String, dynamic> newKegiatanData) async {
try {
return await _kegiatanRepository.createKegiatan(newKegiatanData, placeId);
return await _kegiatanRepository.createKegiatan(newKegiatanData);
} catch (e) {
return Response('Failed to register komentar', 400);
}
}
Future<dynamic> updateKegiatan(
Map<String, dynamic> newKegiatanData,
String placeId,
int id
) async {
try {
return await _kegiatanRepository.updateKegiatan(
newKegiatanData, placeId, id);
} catch (e) {
return {'response': 'FAILED'};
}
}
Future<KegiatanModel> fetchDetailKegiatan(String placeId, int kegiatanId) async {
try {
return await _kegiatanRepository.fetchDetailKegiatan(
placeId,
kegiatanId
);
} catch (e) {
return KegiatanModel();
}
}
void resetKegiatanList() {
kegiatanListSink
.add(NetworkModel.completed(KegiatanList(allKegiatanFromApi)));
......
import 'dart:async';
import 'package:bisaGo/network/data/network_model.dart';
import 'package:bisaGo/repository/kegiatan_repository.dart';
import 'package:get_it/get_it.dart';
class KegiatanSearchBloc {
StreamController _recentSearchController;
KegiatanRepository _kegiatanRepository;
StreamController _kegiatanSearchListController;
StreamController<NetworkModel<List<dynamic>>> get recentSearchSink =>
_recentSearchController.sink;
Stream<NetworkModel<List<dynamic>>> get recentSearchStream =>
_recentSearchController.stream;
StreamSink<NetworkModel<List<dynamic>>> get kegiatanSearchListSink =>
_kegiatanSearchListController.sink;
Stream<NetworkModel<List<dynamic>>> get layananListStream =>
_kegiatanSearchListController.stream;
KegiatanSearchBloc() {
_kegiatanSearchListController =
StreamController<NetworkModel<List<dynamic>>>();
_recentSearchController =
StreamController<NetworkModel<List<dynamic>>>();
_kegiatanRepository = GetIt.instance.get<BaseKegiatanRepository>();
}
Future<void> fetchKegiatanSearchList(String search, String sortConfig) async {
kegiatanSearchListSink.add(NetworkModel.loading('Getting Kegiatan'));
try {
final kegiatanSearchListResponse = await _kegiatanRepository.fetchKegiatanSearch(search, sortConfig);
kegiatanSearchListSink.add(NetworkModel.completed(kegiatanSearchListResponse));
} catch (e) {
kegiatanSearchListSink.add(NetworkModel.error(e.toString()));
}
}
void dispose() {
_recentSearchController?.close();
_kegiatanSearchListController?.close();
}
}
\ No newline at end of file
......@@ -42,6 +42,15 @@ class LokasiResponseBloc {
}
}
Future<Lokasi> fetchLokasiListById(String placeId) async {
lokasiListSink.add(NetworkModel.loading('Getting Location by Id'));
try {
return await _lokasiRepository.fetchLokasiById(placeId);
} catch (e) {
return Lokasi();
}
}
Future<void> fetchRecentSearch() async {
recentSearchSink.add(NetworkModel.loading('Getting Recent Search'));
try {
......
import 'dart:async';
import 'package:bisaGo/model/new_user.dart';
import 'package:bisaGo/network/data/network_model.dart';
import 'package:bisaGo/repository/user_repository.dart';
import 'package:get_it/get_it.dart';
import 'package:http/http.dart';
class NewUserBloc {
UserRepository _userRepository;
StreamController _userController;
NewUserBloc() {
_userController = StreamController<NetworkModel<String>>();
_userRepository = GetIt.instance.get<BaseUserRepository>();
}
Future<Response> registerNewUser(NewUser newUser) async {
try {
return await _userRepository.createUser(newUser);
} catch (_) {
return Response('Failed to register user', 400);
}
}
Future<Response> updateUser(NewUser newUser) async {
try {
return await _userRepository.updateUser(newUser);
} catch (_) {
return Response('Failed to update user', 400);
}
}
void dispose() {
_userController?.close();
}
}
......@@ -8,39 +8,54 @@ import 'package:get_it/get_it.dart';
class UserBloc {
UserRepository _userRepository;
StreamController _userController;
List<UserModel> userFromApi;
String email;
StreamSink<NetworkModel<User>> get userSink => _userController.sink;
Stream<NetworkModel<User>> get userStream => _userController.stream;
StreamSink<NetworkModel<DetailUserModel>> get userSink =>
_userController.sink;
UserBloc(String email) {
_userController = StreamController<NetworkModel<User>>();
Stream<NetworkModel<DetailUserModel>> get userStream =>
_userController.stream;
UserBloc({this.email}) {
_userController = StreamController<NetworkModel<DetailUserModel>>();
_userRepository = GetIt.instance.get<BaseUserRepository>();
fetchUserDetail(email);
}
Future<void> fetchUserDetail(String email) async {
Future<void> fetchUserDetail() async {
userSink.add(NetworkModel.loading('Getting user'));
final response = await getUserDetail(email);
if (response is DetailUserModel) {
userSink.add(NetworkModel.completed(response));
} else {
userSink.add(NetworkModel.error('Gagal untuk mendapatkan profile'));
}
}
Future<dynamic> getUserDetail(String email) async {
try {
final userResponse = await _userRepository.fetchUserDetail(email);
userFromApi = List.from(userResponse.user);
userSink.add(NetworkModel.completed(userResponse));
return await _userRepository.fetchUserDetail(email);
} catch (e) {
if (!_userController.isClosed) {
userSink.add(NetworkModel.error(e.toString()));
}
return e;
}
}
Future<UserModel> fetchUser(String email) async {
Future<DetailUserModel> updateUser(UpdateUserModel updateUser) async {
try {
await fetchUserDetail(email);
return userFromApi.first;
} catch (e) {
return await _userRepository.updateUser(updateUser, email);
} catch (_) {
return null;
}
}
Future<bool> registerNewUser(RegisterUserModel registerUserModel) async {
try {
await _userRepository.registerUser(registerUserModel);
return true;
} catch (_) {
return false;
}
}
void dispose() {
_userController?.close();
}
......
......@@ -5,17 +5,24 @@ class BisaGoAppBar extends StatelessWidget implements PreferredSizeWidget {
final String title;
final List<Widget> actions;
final Widget leading;
final backgroundColor;
const BisaGoAppBar(
{this.title = 'bisaGo', this.actions = const [], this.leading, Key key})
{this.title = 'bisaGo',
this.actions = const [],
this.backgroundColor = greenPrimary,
this.leading,
Key key})
: super(key: key);
@override
final Size preferredSize = const Size.fromHeight(55);
@override
Widget build(BuildContext context) {
return AppBar(
elevation: 15,
centerTitle: true,
backgroundColor: greenPrimary,
backgroundColor: backgroundColor,
automaticallyImplyLeading: leading == null,
leading: leading,
title: Row(
......
import 'package:bisaGo/page/informasi/list_sekolah.dart';
import 'dart:convert';
import 'package:bisaGo/model/user.dart';
import 'package:bisaGo/page/informasi/list_kegiatan.dart';
import 'package:bisaGo/page/profile/profile.dart';
import 'package:bisaGo/page/tentang_disabilitas/tentang_disabilitas.dart';
import 'package:flutter/material.dart';
import 'package:bisaGo/config/styles.dart';
import 'package:bisaGo/page/login/login.dart';
import 'package:bisaGo/page/profile/profile_picture.dart';
import 'package:bisaGo/page/riwayat_pencarian/riwayat_pencarian.dart';
import 'package:bisaGo/page/aboutUs/about_us.dart';
import 'package:shared_preferences/shared_preferences.dart';
......@@ -12,10 +16,10 @@ class BisaGoDrawer extends StatelessWidget {
BisaGoDrawer({Key key}) : super(key: key);
final List<Map<String, dynamic>> drawerList = [
{'title': 'Profile', 'icon': Icons.person},
{'title': 'Beranda', 'icon': Icons.home},
{'title': 'Riwayat Pencarian', 'icon': Icons.history},
{'title': 'Layanan Disabilitas', 'icon': Icons.group},
// {'title': 'Layanan Disabilitas', 'icon': Icons.group},
{'title': 'Agenda Kegiatan', 'icon': Icons.calendar_today},
{'title': 'Tentang Disabilitas', 'icon': Icons.accessible},
{'title': 'Tentang Aplikasi', 'icon': Icons.info},
{'title': 'Login', 'icon': Icons.keyboard_backspace}
......@@ -43,8 +47,68 @@ class BisaGoDrawer extends StatelessWidget {
future: changeNameDrawer(),
builder: (BuildContext context,
AsyncSnapshot<String> snapshot) {
if (snapshot.hasData) {
return Padding(
if (snapshot.hasData &&
snapshot.data != 'Selamat datang ke BisaGo!') {
return Container(
child: InkWell(
onTap: () {
_navigateToProfilePage(context, jsonDecode(snapshot.data)['email']);
},
child: ListView(
children: <Widget>[
Expanded(
child: Row(
children: <Widget>[
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
border: Border.all(color: Colors.white)
),
child: ProfilePicture(DetailUserModel.fromJson(jsonDecode(snapshot.data)),
radius: 30, fontSize: 25, previewAble: true),
),
Expanded(
child: Container(
padding: const EdgeInsets.all(doubleSpace),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
jsonDecode(snapshot.data)['name'].split(' ')[0],
style: TextStyle(
fontSize: 30,
color: Colors.white,
fontFamily: 'Muli',
fontWeight: FontWeight.w900,
),
overflow: TextOverflow.ellipsis,
),
Text(
'Lihat Profil',
style: TextStyle(
fontSize: 13,
color: Colors.white,
fontFamily: 'Muli',
fontWeight: FontWeight.w500,
),
overflow: TextOverflow.ellipsis,
),
],
),
),
),
],
),
),
],
),
),
);
}
else if (snapshot.hasData &&
snapshot.data == 'Selamat datang ke BisaGo!') {
return Padding(
padding: EdgeInsets.all(smallSpace),
child: Text(
snapshot.data,
......@@ -58,14 +122,17 @@ class BisaGoDrawer extends StatelessWidget {
);
}
return Container();
}))),
}
)
)
),
],
),
),
),
Container(
decoration:
BoxDecoration(border: Border(top: BorderSide(color: Colors.white))),
BoxDecoration(border: Border(top: BorderSide(color: Colors.white))),
),
];
menus.forEach((element) => drawerItem.add(element));
......@@ -168,7 +235,9 @@ class BisaGoDrawer extends StatelessWidget {
if (sharedPreferences.getString('token') == null) {
return 'Selamat datang ke BisaGo!';
} else {
return sharedPreferences.getString('email');
//final userJson = jsonDecode(sharedPreferences.getString('user'));
final userJson = sharedPreferences.getString('user');
return userJson;
}
}
......@@ -195,8 +264,10 @@ class BisaGoDrawer extends StatelessWidget {
route = MaterialPageRoute(builder: (_) => const AboutUs());
} else if (page == 'Tentang Disabilitas') {
route = MaterialPageRoute(builder: (_) => TentangDisabilitas());
} else if (page == 'Layanan Disabilitas') {
route = MaterialPageRoute(builder: (_) => ListSekolah());
// } else if (page == 'Layanan Disabilitas') {
// route = MaterialPageRoute(builder: (_) => ListSekolah());
} else if (page == 'Agenda Kegiatan') {
route = MaterialPageRoute(builder: (_) => ListKegiatan());
}
Navigator.of(context).push(route);
}
......
import 'package:bisaGo/config/styles.dart';
import 'package:bisaGo/flavor/flavor.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:shimmer/shimmer.dart';
class ImageHolder extends StatelessWidget {
final String url;
......@@ -76,10 +76,12 @@ class ImageHolder extends StatelessWidget {
fit: BoxFit.cover,
)),
),
placeholder: (context, _) => const Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(greenPrimary),
)),
placeholder: (context, _) => Container(
child: Shimmer.fromColors(
baseColor: Colors.grey[300],
highlightColor: Colors.grey[100],
enabled: true,
child: Container())),
errorWidget: (context, url, error) =>
const Center(child: Icon(Icons.broken_image, color: Colors.black38)),
);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment