Fakultas Ilmu Komputer UI

Commit 497b1135 authored by Jovi Handono Hutama's avatar Jovi Handono Hutama
Browse files

Merge branch 'dev-jovi' into 'PBI-9-info_kegiatan'

Pull from development

See merge request !33
parents 6e2f4d27 c6457f94
Pipeline #76880 failed with stages
in 1 minute and 52 seconds
......@@ -10,7 +10,7 @@ stages:
mv $FLUTTER_ENV .env
flutter pub get
image: poipole/bisago-be:latest
image: poipole/bisago-be:2.0.5-4.6.0.2311
before_script:
- *flutter_before_script
......
FROM cirrusci/flutter:latest
ENV SONARSCANNER="https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.2.0.1873-linux.zip" \
SONAR_SCANNER_CLI_VERSION=4.2.0.1873
FROM cirrusci/flutter:2.0.5
ENV SONAR_SCANNER_CLI_VERSION=4.6.0.2311
RUN wget -qO sonar-scanner-cli.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_CLI_VERSION}-linux.zip && \
unzip sonar-scanner-cli.zip -d /opt && \
rm sonar-scanner-cli.zip && \
chmod +x /opt/sonar-scanner-${SONAR_SCANNER_CLI_VERSION}-linux/bin/sonar-scanner && \
ln -s /opt/sonar-scanner-${SONAR_SCANNER_CLI_VERSION}-linux/bin/sonar-scanner /usr/local/bin/sonar-scanner
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
\ No newline at end of file
3.2.1:
- Fix cant update fasilitas
3.2.0:
- New feature share fasilitas as a link
3.1.2:
- Fix google oauth
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
......@@ -23,6 +23,15 @@ class KomentarBloc {
fetchKomentarList(placeId);
}
Future<KomentarModel> fetchDetailFasilitas(String placeId, int id) async {
try {
return await _komentarRepository.fetchDetailFasilitas(
placeId, id);
} catch (e) {
return KomentarModel();
}
}
Future<dynamic> addNewKomentar(
Map<String, dynamic> newKomentarData, String namaLokasi) async {
try {
......@@ -33,13 +42,13 @@ class KomentarBloc {
}
}
Future<Response> updateKomentar(Map<String, dynamic> newKomentarData,
String namaLokasi, int id, String token) async {
Future<dynamic> updateKomentar(Map<String, dynamic> newKomentarData,
String placeId, int id) async {
try {
return await _komentarRepository.updateKomentar(
newKomentarData, namaLokasi, id, token);
newKomentarData, placeId, id);
} catch (e) {
return Response('Failed to update komentar', 400);
return {'response': 'FAILED'};
}
}
......
......@@ -90,6 +90,12 @@ class BisaGoDrawer extends StatelessWidget {
if (snapshot.hasData &&
snapshot.data != 'Selamat datang ke BisaGo!') {
return Container(
decoration: BoxDecoration(
color: greenPrimary,
border: Border(
bottom: BorderSide(color: Colors.white),
),
),
child: ListTile(
key: Key(title),
leading: Icon(
......@@ -107,18 +113,12 @@ class BisaGoDrawer extends StatelessWidget {
),
onTap: () async {
if (title == 'Login') {
await checkLoginStatus(context);
checkLoginStatus(context);
} else if (title == 'Profile') {
_navigateToProfilePage(context, snapshot.data);
}
},
),
decoration: BoxDecoration(
color: greenPrimary,
border: Border(
bottom: BorderSide(color: Colors.white),
),
),
);
}
return Container();
......@@ -126,6 +126,12 @@ class BisaGoDrawer extends StatelessWidget {
);
}
return Container(
decoration: BoxDecoration(
color: greenPrimary,
border: Border(
bottom: BorderSide(color: Colors.white),
),
),
child: ListTile(
key: Key(title),
leading: Icon(
......@@ -145,12 +151,6 @@ class BisaGoDrawer extends StatelessWidget {
_navigateToPage(context, title);
},
),
decoration: BoxDecoration(
color: greenPrimary,
border: Border(
bottom: BorderSide(color: Colors.white),
),
),
);
}
......
......@@ -39,9 +39,8 @@ class _ImagePreviewHolderState extends State<ImagePreviewHolder> {
child: widget.image,
),
const SizedBox(height: regularSpace),
FlatButton(
TextButton(
key: const Key('Button Hapus Foto'),
padding: EdgeInsets.zero,
onPressed: widget.onPressed,
child: Container(
width: MediaQuery.of(context).size.width * 0.3,
......
import 'package:bisaGo/repository/kegiatan_repository.dart';
import 'package:bisaGo/repository/komentar_posting_kegiatan_repository.dart';
import 'package:bisaGo/repository/dynamic_links_service_repository.dart';
import 'package:bisaGo/repository/komentar_posting_repository.dart';
import 'package:bisaGo/repository/komentar_repository.dart';
import 'package:bisaGo/repository/komunitas_repository.dart';
......@@ -30,5 +31,7 @@ class AppGetIt {
() => LayananRepository());
_getIt.registerLazySingleton<BaseKegiatanRepository>(
() => KegiatanRepository());
_getIt.registerLazySingleton<BaseDynamicLinksServiceRepository>(
() => DynamicLinksServiceRepository());
}
}
......@@ -32,16 +32,25 @@ class NetworkInterface {
return responseJson;
}
Future<dynamic> put({String url}) async {
Future<dynamic> put({
String url,
Map<String, dynamic> bodyParams,
bool isLogin,
bool formData = true,
}) async {
var responseJson;
try {
if (isLogin) {
final sharedPreferences = await SharedPreferences.getInstance();
dio.options.headers['Authorization'] =
'Token ${sharedPreferences.getString('token')}';
}
dio.options.headers['content-type'] = 'application/json';
final response = await dio.put(
'${ApiFlavor.getBaseUrl()}$url',
data: formData ? FormData.fromMap(bodyParams) : json.encode(bodyParams),
);
responseJson = response.data;
responseJson = _response(response);
} on SocketException {
throw FetchDataException('No Internet Connection');
} on DioError catch (e) {
......@@ -77,6 +86,8 @@ class NetworkInterface {
return response.data;
case 201:
return response.data;
case 202:
return response.data;
case 400:
throw BadRequestException(response.data.toString());
break;
......
......@@ -56,15 +56,18 @@ class _AddLokasiState extends State<AddLokasi> {
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () => Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (BuildContext context) => const Pencarian())),
child: Scaffold(
resizeToAvoidBottomInset: true,
appBar: BisaGoAppBar(
title: 'Tambah Lokasi',
leading: InkWell(
child: const Icon(Icons.arrow_back_ios),
onTap: () => Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (BuildContext context) => const Pencarian()))),
builder: (BuildContext context) => const Pencarian())),
child: const Icon(Icons.arrow_back_ios),
),
),
body: Stack(
children: <Widget>[
......@@ -128,9 +131,7 @@ class _AddLokasiState extends State<AddLokasi> {
],
),
),
onWillPop: () => Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (BuildContext context) => const Pencarian())));
);
}
void onCreated(GoogleMapController controller) {
......@@ -283,8 +284,8 @@ class _AddLokasiState extends State<AddLokasi> {
),
)),
Expanded(
child: FlatButton(
color: greenPrimary,
child: TextButton(
style: TextButton.styleFrom(backgroundColor: greenPrimary),
onPressed: () {
addLocationToApi();
setState(() {
......
......@@ -206,9 +206,9 @@ class AddInformasiState extends State<AddInformasi> {
style: TextStyle(fontSize: 18),
textAlign: TextAlign.left,
),
FlatButton(
TextButton(
key: const Key('Button Input Foto Gallery'),
padding: EdgeInsets.zero,
style: TextButton.styleFrom(padding: EdgeInsets.zero),
onPressed: () async {
final imageSelected = await _getImage();
setState(() {
......@@ -240,9 +240,9 @@ class AddInformasiState extends State<AddInformasi> {
),
),
),
FlatButton(
TextButton(
key: const Key('Button Input Foto Camera'),
padding: EdgeInsets.zero,
style: TextButton.styleFrom(padding: EdgeInsets.zero),
onPressed: () async {
final imageSelected = await _getCameraImage();
setState(() {
......@@ -318,10 +318,14 @@ class AddInformasiState extends State<AddInformasi> {
decoration: BoxDecoration(boxShadow: regularShadow),
child: ButtonTheme(
height: 55.0,
child: FlatButton(
color: greenPrimary,
child: TextButton(
style: TextButton.styleFrom(
backgroundColor: greenPrimary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0)),
padding: const EdgeInsets.symmetric(vertical: 10.0)
),
onPressed: _validateInformationInput,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
......@@ -333,8 +337,6 @@ class AddInformasiState extends State<AddInformasi> {
color: Colors.white, fontSize: 20.0)),
],
),
padding: const EdgeInsets.symmetric(vertical: 10.0),
onPressed: _validateInformationInput,
),
)),
const SizedBox(
......@@ -419,7 +421,7 @@ class AddInformasiState extends State<AddInformasi> {
.buffer
.asUint8List();
newKomentarData['image'] =
await MultipartFile.fromBytes(bytes, filename: 'kursiroda.jpg');
MultipartFile.fromBytes(bytes, filename: 'kursiroda.jpg');
print('Fasilitas null dalem addinfo adalah: ' + _jenisFasilitas);
break;
case 'LF':
......@@ -427,103 +429,103 @@ class AddInformasiState extends State<AddInformasi> {
(await rootBundle.load('assets/images/liftdisabilitas.jpg'))
.buffer
.asUint8List();
newKomentarData['image'] = await MultipartFile.fromBytes(bytes,
filename: 'liftdisabilitas.jpg');
newKomentarData['image'] =
MultipartFile.fromBytes(bytes, filename: 'liftdisabilitas.jpg');
break;
case 'TD':
var bytes =
(await rootBundle.load('assets/images/toiletdisabilitas.jpg'))
.buffer
.asUint8List();
newKomentarData['image'] = await MultipartFile.fromBytes(bytes,
filename: 'toiletdisabilitas.jpg');
newKomentarData['image'] =
MultipartFile.fromBytes(bytes, filename: 'toiletdisabilitas.jpg');
break;
case 'MM':
var bytes = (await rootBundle.load('assets/images/masjidmushola.jpg'))
.buffer
.asUint8List();
newKomentarData['image'] = await MultipartFile.fromBytes(bytes,
filename: 'masjidmushola.jpg');
newKomentarData['image'] =
MultipartFile.fromBytes(bytes, filename: 'masjidmushola.jpg');
break;
case 'GB':
var bytes = (await rootBundle.load('assets/images/guidingblock.jpg'))
.buffer
.asUint8List();
newKomentarData['image'] = await MultipartFile.fromBytes(bytes,
filename: 'guidingblock.jpg');
newKomentarData['image'] =
MultipartFile.fromBytes(bytes, filename: 'guidingblock.jpg');
break;
case 'BM':
var bytes = (await rootBundle.load('assets/images/bidangmiring.jpg'))
.buffer
.asUint8List();
newKomentarData['image'] = await MultipartFile.fromBytes(bytes,
filename: 'bidangmiring.jpg');
newKomentarData['image'] =
MultipartFile.fromBytes(bytes, filename: 'bidangmiring.jpg');
break;
case 'CP':
var bytes =
(await rootBundle.load('assets/images/temandisabilitas.jpg'))
.buffer
.asUint8List();
newKomentarData['image'] = await MultipartFile.fromBytes(bytes,
filename: 'temandisabilitas.jpg');
newKomentarData['image'] =
MultipartFile.fromBytes(bytes, filename: 'temandisabilitas.jpg');
break;
case 'JI':
var bytes = (await rootBundle.load('assets/images/juruisyarat.jpg'))
.buffer
.asUint8List();
newKomentarData['image'] =
await MultipartFile.fromBytes(bytes, filename: 'juruisyarat.jpg');
MultipartFile.fromBytes(bytes, filename: 'juruisyarat.jpg');
break;
case 'TN':
var bytes = (await rootBundle.load('assets/images/tongkatnetra.jpg'))
.buffer
.asUint8List();
newKomentarData['image'] = await MultipartFile.fromBytes(bytes,
filename: 'tongkatnetra.jpg');
newKomentarData['image'] =
MultipartFile.fromBytes(bytes, filename: 'tongkatnetra.jpg');
break;
case 'KD':
var bytes =
(await rootBundle.load('assets/images/kursidisabilitas.jpg'))
.buffer
.asUint8List();
newKomentarData['image'] = await MultipartFile.fromBytes(bytes,
filename: 'kursidisabilitas.jpg');
newKomentarData['image'] =
MultipartFile.fromBytes(bytes, filename: 'kursidisabilitas.jpg');
break;
case 'PK':
var bytes =
(await rootBundle.load('assets/images/parkirdisabilitas.jpg'))
.buffer
.asUint8List();
newKomentarData['image'] = await MultipartFile.fromBytes(bytes,
filename: 'parkirdisabilitas.jpg');
newKomentarData['image'] =
MultipartFile.fromBytes(bytes, filename: 'parkirdisabilitas.jpg');
break;
case 'RT':
var bytes = (await rootBundle.load('assets/images/runningtext.jpg'))
.buffer
.asUint8List();
newKomentarData['image'] =
await MultipartFile.fromBytes(bytes, filename: 'runningtext.jpg');
MultipartFile.fromBytes(bytes, filename: 'runningtext.jpg');
break;
case 'TB':
var bytes = (await rootBundle.load('assets/images/parkirbiasa.jpg'))
.buffer
.asUint8List();
newKomentarData['image'] =
await MultipartFile.fromBytes(bytes, filename: 'parkirbiasa.jpg');
MultipartFile.fromBytes(bytes, filename: 'parkirbiasa.jpg');
break;
default:
var bytes =
(await rootBundle.load('assets/images/defaultDisable.png'))
.buffer
.asUint8List();
newKomentarData['image'] = await MultipartFile.fromBytes(bytes,
filename: 'defaultDisable.png');
newKomentarData['image'] =
MultipartFile.fromBytes(bytes, filename: 'defaultDisable.png');
print('Fasilitas null default addinfo adalah: ' + _jenisFasilitas);
break;
}
} else {
final fileName = _image.path.split('/').last;
newKomentarData['image'] = await MultipartFile.fromFile(
newKomentarData['image'] = MultipartFile.fromFile(
_image.path,
filename: fileName,
);
......
import 'package:bisaGo/bloc/lokasi_response_bloc.dart';
import 'package:bisaGo/model/komentar.dart';
import 'package:bisaGo/model/lokasi.dart';
import 'package:bisaGo/network/data/network_model.dart';
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_dynamic_links/firebase_dynamic_links.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:bisaGo/component/bisago_appbar.dart';
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';
class Dashboard extends StatefulWidget {
const Dashboard({Key key}) : super(key: key);
......@@ -31,6 +37,7 @@ class DashboardState extends State<Dashboard> {
super.initState();
_showTurnOnLocationDialog(context);
geolocator = Geolocator()..forceAndroidLocationManager;
initDynamicLinks();
setInitialLocation();
}
......@@ -187,8 +194,8 @@ class DashboardState extends State<Dashboard> {
// floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
appBar: const PreferredSize(
preferredSize: Size.fromHeight(55),
child: BisaGoAppBar(),
key: Key('Scaffold Text Field'),
child: BisaGoAppBar(),
),
);
}
......@@ -243,4 +250,75 @@ class DashboardState extends State<Dashboard> {
currentLocation = LatLng(position.latitude, position.longitude);
});
}
Future<String> getLokasiName(String placeId) async {
final _places = GoogleMapsPlaces(apiKey: DotEnv().env['API_KEY']);
var details = await _places.getDetailsByPlaceId(
placeId,
fields: [
'name',
],
);
return details.result.name;
}
void _navigateToDetailFasilitasPage(
BuildContext context, String placeId, int fasilitasId) async {
final fetches = await Future.wait([
KomentarRepository().fetchDetailFasilitas(placeId, fasilitasId),
getLokasiName(placeId),
]);
final KomentarModel fasilitas = fetches[0];
final String namaLokasi = fetches[1] ?? 'INVALID';
final lokasi = Lokasi()
..placeId = placeId
..name = namaLokasi;
final fasilitasRoute = MaterialPageRoute(
builder: (BuildContext context) => DetailPostPage(
lokasi: lokasi,
komentar: KomentarModel(
creator: fasilitas.creator,
dateTime: fasilitas.dateTime,
deskripsi: fasilitas.deskripsi,
id: fasilitas.id,
image: fasilitas.image,
isVerified: fasilitas.isVerified,
namaLokasi: lokasi.name,
tag: fasilitas.tag,
disabilitas: fasilitas.disabilitas,
jumlah: fasilitas.jumlah,
),
));
await Navigator.of(context).push(fasilitasRoute);
}
void initDynamicLinks() async {
final data = await FirebaseDynamicLinks.instance.getInitialLink();
_handleDeepLink(data);
FirebaseDynamicLinks.instance.onLink(
onSuccess: (PendingDynamicLinkData dynamicLink) async {
_handleDeepLink(dynamicLink);
}, onError: (OnLinkErrorException e) async {
print('Link Failed: ${e.message}');
});
}
void _handleDeepLink(PendingDynamicLinkData data) async {
final deepLink = data?.link;
if (deepLink != null) {
final params = deepLink.queryParameters;
final path = deepLink.pathSegments;
final placeId = params['place_id'];
final id = int.parse(params['id']);
final type = params['type'];
if (path[0] == 'link') {
if (type == 'fasilitas') {
_navigateToDetailFasilitasPage(context, placeId, id);
} else if (type == 'kegiatan') {
// TODO(:ariqbasyar) navigate kegiatan
}
}
}
}
}
......@@ -85,7 +85,7 @@ class _FasilitasState extends State<Fasilitas> {
SizedBox(
height: 150,
child: ImageHolder(
url: widget.lokasi.image,
url: widget.lokasi.image ?? '',
fasilitas: null,
),
),
......@@ -93,7 +93,7 @@ class _FasilitasState extends State<Fasilitas> {
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
widget.lokasi.name,
widget.lokasi.name ?? 'INVALID',
style: const TextStyle(
fontSize: 25,
fontWeight: FontWeight.w800,
......@@ -114,7 +114,7 @@ class _FasilitasState extends State<Fasilitas> {
Flexible(
key: const Key('Alamat'),
child: Text(
widget.lokasi.alamat,
widget.lokasi.alamat ?? 'LOCATION INVALID',
softWrap: true,
textAlign: TextAlign.left,
style: const TextStyle(
......@@ -137,7 +137,7 @@ class _FasilitasState extends State<Fasilitas> {
),
Flexible(
child: Text(
widget.lokasi.noTelp,
widget.lokasi.noTelp ?? '-',
softWrap: true,
textAlign: TextAlign.left,
style: const TextStyle(
......@@ -198,17 +198,31 @@ class _FasilitasState extends State<Fasilitas> {
decoration: BoxDecoration(boxShadow: regularShadow),
child: SizedBox(
width: double.infinity,
child: FlatButton(
child: TextButton(
key: const Key('Tambah Informasi'),
color: Colors.green[700],
textColor: Colors.white,
disabledColor: Colors.grey,
disabledTextColor: Colors.black,