Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions devtools_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
extensions:
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import 'package:dio/dio.dart';
import 'package:dipantau_desktop_client/config/flavor_config.dart';
import 'package:dipantau_desktop_client/feature/data/model/general/general_response.dart';

abstract class GeneralRemoteDataSource {
/// Panggil endpoint [host]/api/ping
///
/// Throws [DioException] untuk semua error kode
late String pathPing;

Future<GeneralResponse> ping(String baseUrl);
}

class GeneralRemoteDataSourceImpl implements GeneralRemoteDataSource {
final Dio dio;

GeneralRemoteDataSourceImpl({
required this.dio,
});

final baseUrl = FlavorConfig.instance.values.baseUrl;

@override
String pathPing = '';

@override
Future<GeneralResponse> ping(String baseUrl) async {
pathPing = '$baseUrl/api/ping';
final response = await dio.get(pathPing);
if (response.statusCode.toString().startsWith('2')) {
return GeneralResponse.fromJson(response.data);
} else {
throw DioException(requestOptions: RequestOptions(path: pathPing));
}
}
}
56 changes: 56 additions & 0 deletions lib/feature/data/repository/general/general_repository_impl.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import 'package:dio/dio.dart';
import 'package:dipantau_desktop_client/core/error/failure.dart';
import 'package:dipantau_desktop_client/core/network/network_info.dart';
import 'package:dipantau_desktop_client/feature/data/datasource/general/general_remote_data_source.dart';
import 'package:dipantau_desktop_client/feature/data/model/general/general_response.dart';
import 'package:dipantau_desktop_client/feature/domain/repository/general/general_repository.dart';

class GeneralRepositoryImpl implements GeneralRepository {
final GeneralRemoteDataSource remoteDataSource;
final NetworkInfo networkInfo;

GeneralRepositoryImpl({
required this.remoteDataSource,
required this.networkInfo,
});

String getErrorMessageFromEndpoint(dynamic dynamicErrorMessage, String httpErrorMessage, int? statusCode) {
if (dynamicErrorMessage is Map && dynamicErrorMessage.containsKey('message')) {
return '$statusCode ${dynamicErrorMessage['message']}';
} else if (dynamicErrorMessage is String) {
return httpErrorMessage;
} else {
return httpErrorMessage;
}
}

@override
Future<({Failure? failure, GeneralResponse? response})> ping(String baseUrl) async {
Failure? failure;
GeneralResponse? response;
final isConnected = await networkInfo.isConnected;
if (isConnected) {
try {
response = await remoteDataSource.ping(baseUrl);
} on DioException catch (error) {
final message = error.message ?? error.toString();
if (error.response == null) {
failure = ServerFailure(message);
} else {
final errorMessage = getErrorMessageFromEndpoint(
error.response?.data,
message,
error.response?.statusCode,
);
failure = ServerFailure(errorMessage);
}
} on TypeError catch (error) {
final errorMessage = error.toString();
failure = ParsingFailure(errorMessage);
}
} else {
failure = ConnectionFailure();
}
return (failure: failure, response: response);
}
}
6 changes: 6 additions & 0 deletions lib/feature/domain/repository/general/general_repository.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import 'package:dipantau_desktop_client/core/error/failure.dart';
import 'package:dipantau_desktop_client/feature/data/model/general/general_response.dart';

abstract class GeneralRepository {
Future<({Failure? failure, GeneralResponse? response})> ping(String baseUrl);
}
34 changes: 34 additions & 0 deletions lib/feature/domain/usecase/ping/ping.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import 'package:dipantau_desktop_client/core/error/failure.dart';
import 'package:dipantau_desktop_client/core/usecase/usecase.dart';
import 'package:dipantau_desktop_client/feature/data/model/general/general_response.dart';
import 'package:dipantau_desktop_client/feature/domain/repository/general/general_repository.dart';
import 'package:equatable/equatable.dart';

class Ping implements UseCaseRecords<GeneralResponse, ParamsPing> {
final GeneralRepository repository;

Ping({required this.repository});

@override
Future<({Failure? failure, GeneralResponse? response})> call(ParamsPing params) {
return repository.ping(params.baseUrl);
}
}

class ParamsPing extends Equatable {
final String baseUrl;

ParamsPing({
required this.baseUrl,
});

@override
List<Object?> get props => [
baseUrl,
];

@override
String toString() {
return 'ParamsPing{baseUrl: $baseUrl}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:dipantau_desktop_client/core/util/helper.dart';
import 'package:dipantau_desktop_client/feature/domain/usecase/ping/ping.dart';

part 'setup_credential_event.dart';

part 'setup_credential_state.dart';

class SetupCredentialBloc extends Bloc<SetupCredentialEvent, SetupCredentialState> {
final Helper helper;
final Ping ping;

SetupCredentialBloc({
required this.helper,
required this.ping,
}) : super(InitialSetupCredentialState()) {
on<PingSetupCredentialEvent>(_onPingSetupCredentialEvent);
}

FutureOr<void> _onPingSetupCredentialEvent(
PingSetupCredentialEvent event,
Emitter<SetupCredentialState> emit,
) async {
final baseUrl = event.baseUrl;
emit(LoadingSetupCredentialState());
final result = await ping(
ParamsPing(
baseUrl: baseUrl,
),
);
final response = result.response;
final failure = result.failure;
if (response != null) {
emit(SuccessPingSetupCredentialState(baseUrl: baseUrl));
return;
}

final errorMessage = helper.getErrorMessageFromFailure(failure);
emit(FailureSetupCredentialState(errorMessage: errorMessage));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
part of 'setup_credential_bloc.dart';

abstract class SetupCredentialEvent {}

class PingSetupCredentialEvent extends SetupCredentialEvent {
final String baseUrl;

PingSetupCredentialEvent({
required this.baseUrl,
});

@override
String toString() {
return 'PingSetupCredentialEvent{baseUrl: $baseUrl}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
part of 'setup_credential_bloc.dart';

abstract class SetupCredentialState {}

class InitialSetupCredentialState extends SetupCredentialState {}

class LoadingSetupCredentialState extends SetupCredentialState {}

class FailureSetupCredentialState extends SetupCredentialState {
final String errorMessage;

FailureSetupCredentialState({
required this.errorMessage,
});

@override
String toString() {
return 'FailureSetupCredentialState{errorMessage: $errorMessage}';
}
}

class SuccessPingSetupCredentialState extends SetupCredentialState {
final String baseUrl;

SuccessPingSetupCredentialState({
required this.baseUrl,
});

@override
String toString() {
return 'SuccessPingSetupCredentialState{baseUrl: $baseUrl}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ import 'package:dipantau_desktop_client/core/util/enum/global_variable.dart';
import 'package:dipantau_desktop_client/core/util/helper.dart';
import 'package:dipantau_desktop_client/core/util/shared_preferences_manager.dart';
import 'package:dipantau_desktop_client/core/util/widget_helper.dart';
import 'package:dipantau_desktop_client/feature/presentation/bloc/setup_credential/setup_credential_bloc.dart';
import 'package:dipantau_desktop_client/feature/presentation/widget/widget_loading_center_full_screen.dart';
import 'package:dipantau_desktop_client/feature/presentation/widget/widget_primary_button.dart';
import 'package:dipantau_desktop_client/injection_container.dart' as di;
import 'package:dipantau_desktop_client/injection_container.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';

class SetupCredentialPage extends StatefulWidget {
Expand All @@ -32,6 +36,7 @@ class _SetupCredentialPageState extends State<SetupCredentialPage> {
final controllerHostname = TextEditingController();
final widgetHelper = WidgetHelper();
final formState = GlobalKey<FormState>();
final setupCredentialBloc = sl<SetupCredentialBloc>();

var isLogin = false;
var defaultDomainApi = '';
Expand All @@ -52,37 +57,81 @@ class _SetupCredentialPageState extends State<SetupCredentialPage> {
appBar: AppBar(
automaticallyImplyLeading: widget.isFromSplashScreen ? false : true,
),
body: Padding(
padding: EdgeInsets.only(
left: helper.getDefaultPaddingLayout,
top: helper.getDefaultPaddingLayoutTop,
right: helper.getDefaultPaddingLayout,
bottom: helper.getDefaultPaddingLayout,
body: BlocProvider(
create: (context) => setupCredentialBloc,
child: BlocListener<SetupCredentialBloc, SetupCredentialState>(
listener: (context, state) {
if (state is FailureSetupCredentialState) {
final errorMessage = 'invalid_hostname'.tr();
widgetHelper.showDialogMessage(
context,
'info'.tr(),
errorMessage,
);
} else if (state is SuccessPingSetupCredentialState) {
final hostname = state.baseUrl;
sharedPreferencesManager.putString(SharedPreferencesManager.keyDomainApi, hostname).then((value) {
helper.setDomainApiToFlavor(hostname);
di.init();
if (mounted) {
context.go('/');
}
});
}
},
child: Stack(
children: [
buildWidgetBody(),
buildWidgetLoadingOverlay(),
],
),
),
child: SizedBox(
width: double.infinity,
child: Form(
key: formState,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
buildWidgetTitle(),
const SizedBox(height: 8),
Text(
'subtitle_set_hostname'.tr(),
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Colors.grey,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
buildWidgetTextFieldHostname(),
const SizedBox(height: 24),
buildWidgetButtonSave(),
],
),
),
);
}

Widget buildWidgetLoadingOverlay() {
return BlocBuilder<SetupCredentialBloc, SetupCredentialState>(
builder: (context, state) {
if (state is LoadingSetupCredentialState) {
return const WidgetLoadingCenterFullScreen();
}
return Container();
},
);
}

Widget buildWidgetBody() {
return Padding(
padding: EdgeInsets.only(
left: helper.getDefaultPaddingLayout,
top: helper.getDefaultPaddingLayoutTop,
right: helper.getDefaultPaddingLayout,
bottom: helper.getDefaultPaddingLayout,
),
child: SizedBox(
width: double.infinity,
child: Form(
key: formState,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
buildWidgetTitle(),
const SizedBox(height: 8),
Text(
'subtitle_set_hostname'.tr(),
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Colors.grey,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
buildWidgetTextFieldHostname(),
const SizedBox(height: 24),
buildWidgetButtonSave(),
],
),
),
),
Expand Down Expand Up @@ -149,12 +198,11 @@ class _SetupCredentialPageState extends State<SetupCredentialPage> {
}
if (isContinue != null && isContinue) {
final hostname = helper.removeTrailingSlash(controllerHostname.text.trim()).trim();
await sharedPreferencesManager.putString(SharedPreferencesManager.keyDomainApi, hostname);
helper.setDomainApiToFlavor(hostname);
di.init();
if (mounted) {
context.go('/');
}
setupCredentialBloc.add(
PingSetupCredentialEvent(
baseUrl: hostname,
),
);
}
}
}
Expand Down
Loading
Loading