feat: Refactor code structure and optimize performance across multiple modules
This commit is contained in:
126
mobile-apps/client-app/lib/core/sevices/app_update_service.dart
Normal file
126
mobile-apps/client-app/lib/core/sevices/app_update_service.dart
Normal file
@@ -0,0 +1,126 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:firebase_remote_config/firebase_remote_config.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
@singleton
|
||||
class AppUpdateService {
|
||||
Future<void> checkForUpdate(BuildContext context) async {
|
||||
final remoteConfig = FirebaseRemoteConfig.instance;
|
||||
|
||||
await remoteConfig.setConfigSettings(RemoteConfigSettings(
|
||||
fetchTimeout: const Duration(seconds: 10),
|
||||
minimumFetchInterval: const Duration(seconds: 1),
|
||||
));
|
||||
|
||||
await remoteConfig.fetchAndActivate();
|
||||
|
||||
final minBuildNumber = remoteConfig.getInt(Platform.isIOS?'min_build_number_client_ios':'min_build_number_client_android');
|
||||
final canSkip = remoteConfig.getBool('can_skip_client');
|
||||
final canIgnore = remoteConfig.getBool('can_ignore_client');
|
||||
final message = remoteConfig.getString('message_client');
|
||||
|
||||
final packageInfo = await PackageInfo.fromPlatform();
|
||||
final currentBuildNumber = int.parse(packageInfo.buildNumber);
|
||||
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final skippedVersion = prefs.getInt('skipped_version') ?? 0;
|
||||
|
||||
if (minBuildNumber > currentBuildNumber &&
|
||||
minBuildNumber > skippedVersion) {
|
||||
await _showUpdateDialog(context, message, canSkip, canIgnore, minBuildNumber);
|
||||
}
|
||||
}
|
||||
|
||||
_showUpdateDialog(
|
||||
BuildContext context, String message, bool canSkip, bool canIgnore, int minBuildNumber) {
|
||||
return showDialog(
|
||||
context: context,
|
||||
barrierDismissible: canIgnore,
|
||||
builder: (BuildContext context) {
|
||||
if (Theme.of(context).platform == TargetPlatform.iOS) {
|
||||
return WillPopScope(
|
||||
onWillPop: () async => canIgnore,
|
||||
child: CupertinoAlertDialog(
|
||||
title: const Text('Update Available'),
|
||||
content: Text(
|
||||
message),
|
||||
actions: <Widget>[
|
||||
if (canSkip)
|
||||
CupertinoDialogAction(
|
||||
child: const Text('Skip this version'),
|
||||
onPressed: () async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setInt('skipped_version', minBuildNumber);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
CupertinoDialogAction(
|
||||
onPressed:
|
||||
canIgnore ? () => Navigator.of(context).pop() : null,
|
||||
child: const Text('Maybe later'),
|
||||
),
|
||||
CupertinoDialogAction(
|
||||
child: const Text('Update'),
|
||||
onPressed: () async {
|
||||
var url = dotenv.env['IOS_STORE_URL'] ??
|
||||
'';
|
||||
if (await canLaunchUrlString(url)) {
|
||||
await launchUrlString(url);
|
||||
} else {
|
||||
throw 'Could not launch $url';
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return WillPopScope(
|
||||
onWillPop: () async => canIgnore,
|
||||
child: AlertDialog(
|
||||
title: const Text('Update Available'),
|
||||
content: Text(
|
||||
message),
|
||||
actions: <Widget>[
|
||||
if (canSkip)
|
||||
TextButton(
|
||||
child: const Text('Skip this version'),
|
||||
onPressed: () async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setInt('skipped_version', minBuildNumber);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
onPressed:
|
||||
canIgnore ? () => Navigator.of(context).pop() : null,
|
||||
child: const Text('Maybe later'),
|
||||
),
|
||||
TextButton(
|
||||
child: const Text('Update'),
|
||||
onPressed: () async {
|
||||
var url = dotenv.env['ANDROID_STORE_URL'] ??
|
||||
'';
|
||||
if (await canLaunchUrlString(url)) {
|
||||
await launchUrlString(url);
|
||||
} else {
|
||||
throw 'Could not launch $url';
|
||||
}
|
||||
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:krow/core/application/clients/api/api_client.dart';
|
||||
import 'package:krow/core/application/di/injectable.dart';
|
||||
import 'package:krow/core/sevices/auth_state_service/auth_service_data_provider.dart';
|
||||
|
||||
enum AuthStatus {
|
||||
authenticated,
|
||||
adminValidation,
|
||||
prepareProfile,
|
||||
unauthenticated,
|
||||
error
|
||||
}
|
||||
|
||||
@injectable
|
||||
class AuthService {
|
||||
final AuthServiceDataProvider _dataProvider;
|
||||
|
||||
AuthService(this._dataProvider);
|
||||
|
||||
Future<AuthStatus> getAuthStatus() async {
|
||||
User? user;
|
||||
try {
|
||||
user = FirebaseAuth.instance.currentUser;
|
||||
} catch (e) {
|
||||
return AuthStatus.error;
|
||||
}
|
||||
|
||||
if (user == null) {
|
||||
return AuthStatus.unauthenticated;
|
||||
} else {
|
||||
return AuthStatus.authenticated;
|
||||
}
|
||||
|
||||
//todo get client? check if client is inactive - logout
|
||||
|
||||
// final staffStatus = await getCachedStaffStatus();
|
||||
//
|
||||
// if (staffStatus == StaffStatus.deactivated) {
|
||||
// return AuthStatus.unauthenticated;
|
||||
// } else if (staffStatus == StaffStatus.pending) {
|
||||
// return AuthStatus.adminValidation;
|
||||
// } else if (staffStatus == StaffStatus.registered ||
|
||||
// staffStatus == StaffStatus.declined) {
|
||||
// return AuthStatus.prepareProfile;
|
||||
// } else {
|
||||
return AuthStatus.authenticated;
|
||||
// }
|
||||
}
|
||||
|
||||
void logout() {
|
||||
FirebaseAuth.instance.signOut();
|
||||
getIt<ApiClient>().dropCache();
|
||||
}
|
||||
|
||||
Future<void> deleteAccount() async {
|
||||
await FirebaseAuth.instance.currentUser!.delete();
|
||||
getIt<ApiClient>().dropCache();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:krow/core/application/clients/api/api_client.dart';
|
||||
|
||||
@injectable
|
||||
class AuthServiceDataProvider {
|
||||
final ApiClient _client;
|
||||
|
||||
AuthServiceDataProvider({required ApiClient client}) : _client = client;
|
||||
|
||||
// Future<void> getStaffStatus() async {
|
||||
// final QueryResult result = await _client.query(schema: getStaffStatusQuery);
|
||||
//
|
||||
// if (result.hasException) {
|
||||
// throw Exception(result.exception.toString());
|
||||
// }
|
||||
//
|
||||
// final Map<String, dynamic> data = result.data!['me'];
|
||||
// return Staff.fromJson(data).status!;
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
const String getStaffStatusQuery = '''
|
||||
query GetMe {
|
||||
me {
|
||||
id
|
||||
status
|
||||
}
|
||||
}
|
||||
''';
|
||||
@@ -0,0 +1,120 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:krow/core/application/clients/api/api_exception.dart';
|
||||
import 'package:krow/core/application/di/injectable.dart';
|
||||
import 'package:krow/core/data/models/event/event_model.dart';
|
||||
import 'package:krow/core/entity/event_entity.dart';
|
||||
import 'package:krow/core/entity/position_entity.dart';
|
||||
import 'package:krow/core/entity/shift_entity.dart';
|
||||
import 'package:krow/core/sevices/create_event_service/create_event_service_repository.dart';
|
||||
import 'package:krow/core/sevices/create_event_service/data/models/create_event_input_model.dart';
|
||||
import 'package:krow/core/sevices/create_event_service/data/models/create_event_shift_input_model.dart';
|
||||
import 'package:krow/core/sevices/create_event_service/data/models/create_event_shift_position_input_model.dart';
|
||||
import 'package:krow/features/events/domain/events_repository.dart';
|
||||
|
||||
@lazySingleton
|
||||
class CreateEventService {
|
||||
EventsRepository? _eventsRepository;
|
||||
|
||||
set eventsRepository(EventsRepository eventsRepository) {
|
||||
_eventsRepository = eventsRepository;
|
||||
}
|
||||
|
||||
Future<String> createEventService(EventEntity entity) async {
|
||||
late CreateEventInputModel createEventInputModel;
|
||||
try {
|
||||
createEventInputModel = createInput(entity);
|
||||
} catch (e) {
|
||||
debugPrint('Create event model error: $e');
|
||||
throw DisplayableException('Field must be filled');
|
||||
}
|
||||
|
||||
var id = await getIt<CreateEventServiceRepository>()
|
||||
.createEvent(createEventInputModel);
|
||||
|
||||
_eventsRepository?.refreshEvents(EventStatus.draft);
|
||||
return id;
|
||||
}
|
||||
|
||||
Future<bool> validateEventInfo(EventEntity entity) async {
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<bool> validateShiftInfo(ShiftEntity entity) async {
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<bool> validateShiftPositionInfo(PositionEntity entity) async {
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> deleteDraft(EventEntity entity) async {
|
||||
if (entity.id.isEmpty) {
|
||||
return;
|
||||
}
|
||||
await getIt<CreateEventServiceRepository>().deleteDraft(entity.id);
|
||||
_eventsRepository?.refreshEvents(EventStatus.draft);
|
||||
}
|
||||
|
||||
Future<void> publishEvent(EventEntity entity) async {
|
||||
var id;
|
||||
if (entity.id.isEmpty) {
|
||||
id = await createEventService(entity);
|
||||
} else {
|
||||
await updateEvent(entity);
|
||||
id = entity.id;
|
||||
}
|
||||
|
||||
try {
|
||||
if (entity.status == EventStatus.draft || entity.status == null) {
|
||||
await getIt<CreateEventServiceRepository>().publishEvent(id);
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
|
||||
_eventsRepository?.refreshEvents(EventStatus.draft);
|
||||
_eventsRepository?.refreshEvents(EventStatus.pending);
|
||||
}
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateEvent(EventEntity entity) async {
|
||||
await getIt<CreateEventServiceRepository>()
|
||||
.updateEvent(createInput(entity));
|
||||
_eventsRepository?.refreshEvents(entity.status ?? EventStatus.draft);
|
||||
}
|
||||
|
||||
CreateEventInputModel createInput(EventEntity entity) {
|
||||
return CreateEventInputModel(
|
||||
id: entity.id.isEmpty ? null : entity.id,
|
||||
name: entity.name,
|
||||
date: DateFormat('yyyy-MM-dd').format(entity.startDate ?? DateTime.now()),
|
||||
hubId: entity.hub!.id,
|
||||
// contractId: entity.contractNumber,
|
||||
purchaseOrder: entity.poNumber,
|
||||
contractType: EventContractType.purchaseOrder,
|
||||
additionalInfo: entity.additionalInfo,
|
||||
tags: entity.tags?.map((e) => e.id).toList(),
|
||||
addons: entity.addons?.map((e) => e.id).toList(),
|
||||
shifts: entity.shifts?.indexed.map((e) {
|
||||
var (index, shiftState) = e;
|
||||
return CreateEventShiftInputModel(
|
||||
name: 'Shift #${index + 1}',
|
||||
address: shiftState.fullAddress!,
|
||||
contacts: shiftState.managers.map((e) => e.id).toList(),
|
||||
positions: shiftState.positions.map((roleEntity) {
|
||||
return CreateEventShiftPositionInputModel(
|
||||
businessSkillId: roleEntity.businessSkill!.id!,
|
||||
hubDepartmentId: roleEntity.department!.id,
|
||||
count: roleEntity.count!,
|
||||
startTime: DateFormat('HH:mm').format(roleEntity.startTime!),
|
||||
endTime: DateFormat('HH:mm').format(roleEntity.endTime!),
|
||||
rate: roleEntity.businessSkill!.skill!.price!,
|
||||
breakDuration: roleEntity.breakDuration!,
|
||||
);
|
||||
}).toList());
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import 'package:krow/core/entity/event_entity.dart';
|
||||
import 'package:krow/core/sevices/create_event_service/data/models/create_event_input_model.dart';
|
||||
|
||||
abstract class CreateEventServiceRepository {
|
||||
Future<String> createEvent(CreateEventInputModel input);
|
||||
|
||||
Future<void> deleteDraft(String id);
|
||||
Future<void> publishEvent(String id);
|
||||
|
||||
Future<void> updateEvent(CreateEventInputModel input);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:krow/core/application/clients/api/api_client.dart';
|
||||
import 'package:krow/core/application/clients/api/api_exception.dart';
|
||||
import 'package:krow/core/sevices/create_event_service/data/create_event_service_gql.dart';
|
||||
import 'package:krow/core/sevices/create_event_service/data/models/create_event_input_model.dart';
|
||||
|
||||
@Injectable()
|
||||
class CreateEventServiceApiProvider {
|
||||
final ApiClient _client;
|
||||
|
||||
CreateEventServiceApiProvider({required ApiClient client}) : _client = client;
|
||||
|
||||
Future<String> createEvent(CreateEventInputModel input) async {
|
||||
var result = await _client.mutate(
|
||||
schema: createEventMutation,
|
||||
body: {
|
||||
'input': input.toJson()..remove('id'),
|
||||
},
|
||||
);
|
||||
|
||||
if (result.hasException) {
|
||||
throw parseBackendError(result.exception);
|
||||
}
|
||||
|
||||
return result.data?['client_create_event']['id'] ?? '';
|
||||
}
|
||||
|
||||
Future<void> deleteDraft(String id) async {
|
||||
var result = await _client.mutate(
|
||||
schema: deleteDraftMutation,
|
||||
body: {
|
||||
'id': id,
|
||||
},
|
||||
);
|
||||
|
||||
if (result.hasException) {
|
||||
print(result.exception);
|
||||
throw parseBackendError(result.exception);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> publishEvent(String id) async {
|
||||
var result = await _client.mutate(
|
||||
schema: publishDraftMutation,
|
||||
body: {
|
||||
'id': id,
|
||||
},
|
||||
);
|
||||
|
||||
if (result.hasException) {
|
||||
throw parseBackendError(result.exception);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateEvent(CreateEventInputModel input) async {
|
||||
var result = await _client.mutate(
|
||||
schema: updateEventMutation,
|
||||
body: {
|
||||
'input': input.toJson(),
|
||||
},
|
||||
);
|
||||
|
||||
if (result.hasException) {
|
||||
throw parseBackendError(result.exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
const String createEventMutation = r'''
|
||||
mutation createEvent ($input: CreateEventInput!) {
|
||||
client_create_event(input: $input) {
|
||||
id
|
||||
}
|
||||
}
|
||||
''';
|
||||
|
||||
const String updateEventMutation = r'''
|
||||
mutation updateEvent ($input: UpdateEventInput!) {
|
||||
client_update_event(input: $input) {
|
||||
id
|
||||
}
|
||||
}
|
||||
''';
|
||||
|
||||
const String deleteDraftMutation = r'''
|
||||
mutation deleteDraft ($id: ID!) {
|
||||
delete_client_event(event_id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
''';
|
||||
|
||||
const String publishDraftMutation = r'''
|
||||
mutation publicsDraft ($id: ID!) {
|
||||
client_publish_event(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
''';
|
||||
@@ -0,0 +1,34 @@
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:krow/core/entity/event_entity.dart';
|
||||
import 'package:krow/core/sevices/create_event_service/create_event_service_repository.dart';
|
||||
import 'package:krow/core/sevices/create_event_service/data/create_event_service_api_provider.dart';
|
||||
import 'package:krow/core/sevices/create_event_service/data/models/create_event_input_model.dart';
|
||||
|
||||
@Singleton(as: CreateEventServiceRepository)
|
||||
class CreateEventServiceRepositoryImpl extends CreateEventServiceRepository {
|
||||
final CreateEventServiceApiProvider _apiProvider;
|
||||
|
||||
CreateEventServiceRepositoryImpl(
|
||||
{required CreateEventServiceApiProvider apiProvider})
|
||||
: _apiProvider = apiProvider;
|
||||
|
||||
@override
|
||||
Future<String> createEvent(CreateEventInputModel input) async {
|
||||
return _apiProvider.createEvent(input);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteDraft(String id) {
|
||||
return _apiProvider.deleteDraft(id);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> publishEvent(String id) {
|
||||
return _apiProvider.publishEvent(id);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateEvent(CreateEventInputModel input) {
|
||||
return _apiProvider.updateEvent(input);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:krow/core/data/models/event/event_model.dart';
|
||||
import 'package:krow/core/sevices/create_event_service/data/models/create_event_shift_input_model.dart';
|
||||
|
||||
part 'create_event_input_model.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class CreateEventInputModel {
|
||||
final String? id;
|
||||
final String name;
|
||||
final String date;
|
||||
final String hubId;
|
||||
|
||||
// final String? contractId;
|
||||
final String? purchaseOrder;
|
||||
final EventContractType contractType;
|
||||
final String? additionalInfo;
|
||||
final List<String>? tags;
|
||||
final List<String>? addons;
|
||||
final List<CreateEventShiftInputModel>? shifts;
|
||||
|
||||
CreateEventInputModel(
|
||||
{this.id,
|
||||
required this.name,
|
||||
required this.date,
|
||||
required this.hubId,
|
||||
// required this.contractId,
|
||||
required this.purchaseOrder,
|
||||
required this.contractType,
|
||||
required this.additionalInfo,
|
||||
required this.tags,
|
||||
required this.addons,
|
||||
required this.shifts});
|
||||
|
||||
factory CreateEventInputModel.fromJson(Map<String, dynamic> json) {
|
||||
return _$CreateEventInputModelFromJson(json);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => _$CreateEventInputModelToJson(this);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:krow/core/data/models/event/full_address_model.dart';
|
||||
import 'package:krow/core/sevices/create_event_service/data/models/create_event_shift_position_input_model.dart';
|
||||
|
||||
part 'create_event_shift_input_model.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class CreateEventShiftInputModel {
|
||||
final String name;
|
||||
final FullAddress address;
|
||||
final List<String>? contacts;
|
||||
final List<CreateEventShiftPositionInputModel> positions;
|
||||
|
||||
CreateEventShiftInputModel(
|
||||
{required this.name,
|
||||
required this.address,
|
||||
required this.contacts,
|
||||
required this.positions});
|
||||
|
||||
factory CreateEventShiftInputModel.fromJson(Map<String, dynamic> json) {
|
||||
return _$CreateEventShiftInputModelFromJson(json);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => _$CreateEventShiftInputModelToJson(this);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'create_event_shift_position_input_model.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class CreateEventShiftPositionInputModel {
|
||||
final String businessSkillId;
|
||||
final String hubDepartmentId;
|
||||
final int count;
|
||||
final String startTime;
|
||||
final String endTime;
|
||||
final double rate;
|
||||
@JsonKey(name: 'break')
|
||||
final int breakDuration;
|
||||
|
||||
CreateEventShiftPositionInputModel(
|
||||
{required this.businessSkillId,
|
||||
required this.hubDepartmentId,
|
||||
required this.count,
|
||||
required this.startTime,
|
||||
required this.endTime,
|
||||
required this.rate,
|
||||
required this.breakDuration});
|
||||
|
||||
factory CreateEventShiftPositionInputModel.fromJson(
|
||||
Map<String, dynamic> json) {
|
||||
return _$CreateEventShiftPositionInputModelFromJson(json);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() =>
|
||||
_$CreateEventShiftPositionInputModelToJson(this);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
|
||||
class GeofencingService {
|
||||
Future<GeolocationStatus> requestGeolocationPermission() async {
|
||||
LocationPermission permission;
|
||||
|
||||
permission = await Geolocator.checkPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
permission = await Geolocator.requestPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
return GeolocationStatus.denied;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (permission == LocationPermission.deniedForever){
|
||||
return GeolocationStatus.prohibited;
|
||||
}
|
||||
if (!(await Geolocator.isLocationServiceEnabled())) {
|
||||
// Location services are not enabled
|
||||
return GeolocationStatus.disabled;
|
||||
}
|
||||
return GeolocationStatus.enabled;
|
||||
}
|
||||
|
||||
bool _isInRange(
|
||||
Position currentPosition,
|
||||
double pointLat,
|
||||
double pointLon,
|
||||
int range,
|
||||
) {
|
||||
double distance = Geolocator.distanceBetween(
|
||||
currentPosition.latitude,
|
||||
currentPosition.longitude,
|
||||
pointLat,
|
||||
pointLon,
|
||||
);
|
||||
|
||||
return distance <= range;
|
||||
}
|
||||
|
||||
/// Checks if the user's current location is within [range] meters
|
||||
/// of the given [pointLatitude] and [pointLongitude].
|
||||
Future<bool> isInRangeCheck({
|
||||
required double pointLatitude,
|
||||
required double pointLongitude,
|
||||
int range = 500,
|
||||
}) async {
|
||||
try {
|
||||
Position position = await Geolocator.getCurrentPosition();
|
||||
|
||||
return _isInRange(position, pointLatitude, pointLongitude, range);
|
||||
} catch (except) {
|
||||
log('Error getting location: $except');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Constantly checks if the user's current location is within [range] meters
|
||||
/// of the given [pointLatitude] and [pointLongitude].
|
||||
Stream<bool> isInRangeStream({
|
||||
required double pointLatitude,
|
||||
required double pointLongitude,
|
||||
int range = 500,
|
||||
}) async* {
|
||||
await for (final position in Geolocator.getPositionStream()) {
|
||||
try {
|
||||
yield _isInRange(position, pointLatitude, pointLongitude, range);
|
||||
} catch (except) {
|
||||
log('Error getting location: $except');
|
||||
yield false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// [disabled] indicates that the user should enable geolocation on the device.
|
||||
/// [denied] indicates that permission should be requested or re-requested.
|
||||
/// [prohibited] indicates that permission is denied and can only be changed
|
||||
/// via the Settings.
|
||||
/// [enabled] - geolocation service is allowed and available for usage.
|
||||
enum GeolocationStatus {
|
||||
disabled,
|
||||
denied,
|
||||
prohibited,
|
||||
enabled,
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
class TimeSlotService {
|
||||
static DateTime calcTime({
|
||||
required DateTime? currentStartTime,
|
||||
required DateTime? currentEndTime,
|
||||
DateTime? startTime,
|
||||
DateTime? endTime,
|
||||
}) {
|
||||
|
||||
try {
|
||||
if (endTime != null) {
|
||||
return endTime.difference(currentStartTime!).inHours < 5 ||
|
||||
endTime.isBefore(currentStartTime!)
|
||||
? endTime.subtract(const Duration(hours: 5))
|
||||
: currentStartTime;
|
||||
} else if (startTime != null) {
|
||||
return startTime.difference(currentEndTime!).inHours < 5 ||
|
||||
startTime.isAfter(currentEndTime)
|
||||
? startTime.add(const Duration(hours: 5))
|
||||
: currentEndTime;
|
||||
}
|
||||
} catch (e) {
|
||||
return DateTime.now().subtract(Duration(minutes: DateTime.now().minute % 10));
|
||||
}
|
||||
return DateTime.now().subtract(Duration(minutes: DateTime.now().minute % 10));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user