diff --git a/lib/Bloc/config/graphql_config.dart b/lib/Bloc/config/graphql_config.dart index 12f5296..c9d6510 100644 --- a/lib/Bloc/config/graphql_config.dart +++ b/lib/Bloc/config/graphql_config.dart @@ -20,15 +20,17 @@ class GraphQLConfig { )); Future getToken() async { - final _token = userConfig!.currentUser!.authToken; - token = _token; + await localApi.init(); + final user = await localApi.fetchUser(); + if (user != null) { + token = user.authToken; + } return true; } GraphQLClient clientToQuery() { return GraphQLClient( - cache: GraphQLCache(), - // cache: GraphQLCache(partialDataPolicy: PartialDataCachePolicy.accept), + cache: GraphQLCache(partialDataPolicy: PartialDataCachePolicy.accept), link: httpLink, ); } @@ -45,7 +47,7 @@ class GraphQLConfig { GraphQLClient graphQlClient() { return GraphQLClient( - cache: GraphQLCache(), + cache: GraphQLCache(partialDataPolicy: PartialDataCachePolicy.accept), link: Link.split( (request) => request.isSubscription, websocketLink, diff --git a/lib/Bloc/config/user_config.dart b/lib/Bloc/config/user_config.dart new file mode 100644 index 0000000..c0e88e3 --- /dev/null +++ b/lib/Bloc/config/user_config.dart @@ -0,0 +1,12 @@ +import 'package:beacon/Bloc/data/models/user/user_model.dart'; +import 'package:beacon/locator.dart'; + +class UserModelConfig { + UserModel _userModel = UserModel(authToken: 'null'); + UserModel get userModel => _userModel; + + Future updateUser(UserModel updateUserDetails) async { + _userModel = updateUserDetails; + return localApi.saveUser(updateUserDetails); + } +} diff --git a/lib/Bloc/core/constants/location.dart b/lib/Bloc/core/constants/location.dart new file mode 100644 index 0000000..0e72c7c --- /dev/null +++ b/lib/Bloc/core/constants/location.dart @@ -0,0 +1,30 @@ +import 'package:geolocator/geolocator.dart'; + +class LocationService { + static Future getCurrentLocation() async { + bool serviceEnabled; + LocationPermission permission; + + serviceEnabled = await Geolocator.isLocationServiceEnabled(); + + if (!serviceEnabled) { + return Future.error('Location service is disabled.'); + } + + permission = await Geolocator.checkPermission(); + + if (permission == LocationPermission.denied) { + permission = await Geolocator.requestPermission(); + if (permission == LocationPermission.denied) { + return Future.error('Location permission is denied'); + } + } + + if (permission == LocationPermission.deniedForever) { + return Future.error('Location permission is permanently denied.'); + } + + return await Geolocator.getCurrentPosition( + desiredAccuracy: LocationAccuracy.high); + } +} diff --git a/lib/Bloc/core/queries/auth.dart b/lib/Bloc/core/queries/auth.dart index 37eca71..1c44b5c 100644 --- a/lib/Bloc/core/queries/auth.dart +++ b/lib/Bloc/core/queries/auth.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - class AuthQueries { String registerUser(String? name, String email, String? password) { return ''' @@ -43,7 +41,6 @@ class AuthQueries { } String fetchUserInfo() { - log('fetching user info'); return ''' query{ me{ @@ -52,38 +49,9 @@ class AuthQueries { name groups{ _id - title - shortcode - leader { - _id - name - } - members { - _id - name - } - beacons{ - _id - } } beacons{ _id - title - shortcode - leader { - _id - name - } - followers{ - _id - name - } - location { - lat - lon - } - startsAt - expiresAt } } } diff --git a/lib/Bloc/core/queries/group.dart b/lib/Bloc/core/queries/group.dart index f9b335b..bc76073 100644 --- a/lib/Bloc/core/queries/group.dart +++ b/lib/Bloc/core/queries/group.dart @@ -1,6 +1,29 @@ import 'package:graphql_flutter/graphql_flutter.dart'; class GroupQueries { + String fetchUserGroups(int page, int pageSize) { + return ''' + query { + groups(page: $page, pageSize: $pageSize) { + _id + title + shortcode + leader { + _id + name + } + members { + _id + name + } + beacons { + _id + } + } + } + '''; + } + String createGroup(String? title) { return ''' mutation{ @@ -129,6 +152,36 @@ class GroupQueries { '''; } + String fetchHikes(String groupID, int page, int pageSize) { + return ''' +query{ + beacons(groupId: "$groupID", page: $page, pageSize: $pageSize){ + _id + title + shortcode + leader { + _id + name + } + location{ + lat + lon + } + followers { + _id + name + } + group{ + _id + } + startsAt + expiresAt + + } +} +'''; + } + final groupJoinedSubGql = gql(r''' subscription StreamNewlyJoinedGroups($id: ID!){ groupJoined(id: $id){ diff --git a/lib/Bloc/core/resources/data_state.dart b/lib/Bloc/core/resources/data_state.dart index a330e54..a6bdca9 100644 --- a/lib/Bloc/core/resources/data_state.dart +++ b/lib/Bloc/core/resources/data_state.dart @@ -1,8 +1,6 @@ -import 'package:graphql/client.dart'; - abstract class DataState { final T? data; - final OperationException? error; + final String? error; const DataState({this.data, this.error}); } @@ -12,5 +10,5 @@ class DataSuccess extends DataState { } class DataFailed extends DataState { - const DataFailed(OperationException error) : super(error: error); + const DataFailed(String error) : super(error: error); } diff --git a/lib/Bloc/core/services/shared_prefrence_service.dart b/lib/Bloc/core/services/shared_prefrence_service.dart index 449a9e1..9e91b81 100644 --- a/lib/Bloc/core/services/shared_prefrence_service.dart +++ b/lib/Bloc/core/services/shared_prefrence_service.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; -class SharedPreferencesService { +class SharedPreferenceService { SharedPreferenceService() { init(); } diff --git a/lib/Bloc/core/utils/utils.dart b/lib/Bloc/core/utils/utils.dart index b941e64..aa80bd5 100644 --- a/lib/Bloc/core/utils/utils.dart +++ b/lib/Bloc/core/utils/utils.dart @@ -1,4 +1,5 @@ import 'dart:developer'; +import 'package:beacon/old/components/utilities/constants.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/material.dart'; import 'package:graphql/client.dart'; @@ -13,7 +14,7 @@ class Utils { message, style: TextStyle(color: Colors.black), ), - // backgroundColor: kLightBlue.withOpacity(0.8), + backgroundColor: kLightBlue.withOpacity(0.8), shape: RoundedRectangleBorder( borderRadius: BorderRadius.all( Radius.circular(10), @@ -40,7 +41,7 @@ class Utils { } Future checkInternetConnectivity() async { - final connectivityResult = await (Connectivity().checkConnectivity()); + final connectivityResult = await Connectivity().checkConnectivity(); if (connectivityResult == ConnectivityResult.mobile || connectivityResult == ConnectivityResult.wifi || diff --git a/lib/old/components/services/validators.dart b/lib/Bloc/core/utils/validators.dart similarity index 64% rename from lib/old/components/services/validators.dart rename to lib/Bloc/core/utils/validators.dart index 28a7a21..febbefc 100644 --- a/lib/old/components/services/validators.dart +++ b/lib/Bloc/core/utils/validators.dart @@ -1,28 +1,28 @@ class Validator { - static String? validateName(String name) { - if (name.isEmpty) { + static String? validateName(String? name) { + if (name != null && name.isEmpty) { return "Name must not be left blank"; } return null; } - static String? validateEmail(String email) { + static String? validateEmail(String? email) { // If email is empty return. - if (email.isEmpty) { + if (email != null && email.isEmpty) { return "Email must not be left blank"; } const String pattern = r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?)*$"; final RegExp regex = RegExp(pattern); - if (!regex.hasMatch(email)) { + if (email != null && !regex.hasMatch(email)) { return 'Please enter a valid Email Address'; } return null; } - static String? validatePassword(String password) { + static String? validatePassword(String? password) { // If password is empty return. - if (password.isEmpty) { + if (password != null && password.isEmpty) { return "Password must not be left blank"; } // const String pattern = r'^(?=.*?[0-9])(?=.*?[!@#\$&*%^~.]).{8,}$'; @@ -32,7 +32,7 @@ class Validator { const String noSpaces = r'^\S+$'; final RegExp noSpaceRegex = RegExp(noSpaces); - if (password.length < 8) { + if (password!.length < 8) { return "Must be of atleast 8 characters"; } // if (!regExp.hasMatch(password)) { @@ -44,33 +44,33 @@ class Validator { return null; } - static String? validateBeaconTitle(String title) { - if (title.isEmpty) { + static String? validateBeaconTitle(String? title) { + if (title != null && title.isEmpty) { return "Title must not be left blank"; } return null; } - static String? validatePasskey(String passkey) { - if (passkey.isEmpty) { + static String? validatePasskey(String? passkey) { + if (passkey != null && passkey.isEmpty) { return "Passkey must not be left blank"; } const String pattern = r'[A-Z]+'; final RegExp regExp = RegExp(pattern); - if (!regExp.hasMatch(passkey) || passkey.length != 6) { + if (!regExp.hasMatch(passkey!) || passkey.length != 6) { return "Invalid passkey"; } return null; } - static String? validateDuration(String duration) { - if (duration.startsWith("0:00:00.")) { + static String? validateDuration(String? duration) { + if (duration != null && duration.startsWith("0:00:00.")) { return "Duration cannot be $duration"; } return null; } - static String? validateStartingTime(String startTime) { + static String? validateStartingTime(String? startTime) { print(startTime); return null; } diff --git a/lib/Bloc/data/datasource/local/local_api.dart b/lib/Bloc/data/datasource/local/local_api.dart new file mode 100644 index 0000000..6b27fce --- /dev/null +++ b/lib/Bloc/data/datasource/local/local_api.dart @@ -0,0 +1,182 @@ +import 'dart:async'; +import 'dart:developer'; +import 'dart:io'; +import 'package:beacon/Bloc/data/models/beacon/beacon_model.dart'; +import 'package:beacon/Bloc/data/models/group/group_model.dart'; +import 'package:beacon/Bloc/data/models/landmark/landmark_model.dart'; +import 'package:beacon/Bloc/data/models/location/location_model.dart'; +import 'package:beacon/Bloc/data/models/user/user_model.dart'; +import 'package:hive/hive.dart'; +import 'package:path_provider/path_provider.dart' as path_provider; + +class LocalApi { + String userModelbox = 'userBox'; + String groupModelBox = 'groupBox'; + String beaconModelBox = 'beaconBox'; + String locationModelBox = 'locationBox'; + String landMarkModelBox = 'landMarkBox'; + + late Box userBox; + late Box groupBox; + late Box beaconBox; + late Box locationBox; + late Box landMarkbox; + + UserModel _userModel = UserModel(authToken: null); + UserModel get userModel => _userModel; + + init() async { + Directory directory = + await path_provider.getApplicationDocumentsDirectory(); + + Hive.init(directory.path); + + !Hive.isAdapterRegistered(10) + ? Hive.registerAdapter(UserModelAdapter()) + : null; + !Hive.isAdapterRegistered(20) + ? Hive.registerAdapter(BeaconModelAdapter()) + : null; + !Hive.isAdapterRegistered(30) + ? Hive.registerAdapter(GroupModelAdapter()) + : null; + + !Hive.isAdapterRegistered(40) + ? Hive.registerAdapter(LocationModelAdapter()) + : null; + !Hive.isAdapterRegistered(50) + ? Hive.registerAdapter(LandMarkModelAdapter()) + : null; + + try { + userBox = await Hive.openBox(userModelbox); + groupBox = await Hive.openBox(groupModelBox); + beaconBox = await Hive.openBox(beaconModelBox); + locationBox = await Hive.openBox(locationModelBox); + landMarkbox = await Hive.openBox(landMarkModelBox); + } catch (e) { + log('error: ${e.toString()}'); + } + } + + Future saveUser(UserModel user) async { + try { + _userModel = user; + await userBox.put('currentUser', user); + return true; + } catch (e) { + log(e.toString()); + return false; + } + } + + Future deleteUser() async { + // clearing the info + + try { + _userModel.copyWithModel( + authToken: null, + beacons: null, + email: null, + groups: null, + id: null, + isGuest: null, + location: null, + name: null); + await userBox.clear(); + await groupBox.clear(); + await beaconBox.clear(); + } catch (e) { + log(e.toString()); + } + } + + Future fetchUser() async { + try { + final user = await userBox.get('currentUser'); + return user; + } catch (e) { + log(e.toString()); + return null; + } + } + + Future userloggedIn() async { + try { + UserModel? user = await userBox.get('currentUser'); + + if (user == null) { + return false; + } + _userModel = user; + return true; + } catch (e) { + log(e.toString()); + return false; + } + } + + Future saveGroup(GroupModel group) async { + await deleteGroup(group.id); + try { + await groupBox.put(group.id, group); + return true; + } catch (e) { + log(e.toString()); + return false; + } + } + + Future deleteGroup(String? groupId) async { + try { + bool doesExist = await groupBox.containsKey(groupId); + doesExist ? await groupBox.delete(groupId) : null; + return true; + } catch (e) { + log(e.toString()); + return false; + } + } + + Future getGroup(String? groupId) async { + try { + final group = await groupBox.get(groupId); + return group; + } catch (e) { + log(e.toString()); + return null; + } + } + + Future saveBeacon(BeaconModel beacon) async { + try { + await deleteBeacon(beacon.id); + await beaconBox.put(beacon.id, beacon); + return true; + } catch (e) { + log(e.toString()); + return false; + } + } + + Future deleteBeacon(String? beaconId) async { + try { + bool doesExist = await beaconBox.containsKey(beaconId); + doesExist ? await beaconBox.delete(beaconId) : null; + return true; + } catch (e) { + log(e.toString()); + return false; + } + } + + Future getBeacon(String? beaconId) async { + try { + final beacon = await beaconBox.get(beaconId); + return beacon; + } catch (e) { + log(e.toString()); + return null; + } + } +} diff --git a/lib/Bloc/data/datasource/remote/remote_auth_api.dart b/lib/Bloc/data/datasource/remote/remote_auth_api.dart new file mode 100644 index 0000000..d40bcb3 --- /dev/null +++ b/lib/Bloc/data/datasource/remote/remote_auth_api.dart @@ -0,0 +1,137 @@ +import 'package:beacon/Bloc/core/queries/auth.dart'; +import 'package:beacon/Bloc/core/resources/data_state.dart'; +import 'package:beacon/Bloc/data/models/user/user_model.dart'; +import 'package:beacon/locator.dart'; +import 'package:flutter/material.dart'; +import 'package:graphql_flutter/graphql_flutter.dart'; + +class RemoteAuthApi { + final ValueNotifier clientNonAuth; + GraphQLClient clientAuth; + + RemoteAuthApi({ + required this.clientNonAuth, + required this.clientAuth, + }); + + AuthQueries _authQueries = AuthQueries(); + + Future> fetchUserInfo() async { + clientAuth = await graphqlConfig.authClient(); + + final isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) + return DataFailed('Beacon is trying to connect with internet...'); + + // api call + final result = await clientAuth + .mutate(MutationOptions(document: gql(_authQueries.fetchUserInfo()))); + + if (result.data != null && result.isConcrete) { + final json = result.data!['me']; + final user = UserModel.fromJson(json); + + final currentUser = await localApi.fetchUser(); + + // checking if user is login + if (currentUser == null) return DataFailed('Please login first'); + final newUser = user.copyWithModel( + authToken: currentUser.authToken, + isGuest: user.email == '' ? true : false); + + // saving user details locally + await localApi.saveUser(newUser); + + // returning + return DataSuccess(newUser); + } + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + + Future> register( + String name, String email, String password) async { + try { + final isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) + return DataFailed('Beacon is trying to connect with internet...'); + + final result = await clientNonAuth.value.mutate( + MutationOptions( + document: gql(_authQueries.registerUser(name, email, password)), + ), + ); + + if (result.data != null && result.isConcrete) { + // LOGIN API CALL + final dataState = await login(email, password); + return dataState; + } else if (result.hasException) { + final message = encounteredExceptionOrError(result.exception!); + return DataFailed(message); + } + + return DataFailed('An unexpected error occurred during registration.'); + } catch (e) { + return DataFailed(e.toString()); + } + } + + Future> login(String email, String password) async { + try { + final isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) { + return DataFailed('Beacon is trying to connect with internet...'); + } + + final QueryResult result = await clientNonAuth.value.mutate( + MutationOptions( + document: gql(_authQueries.loginUser(email, password)))); + + if (result.data != null && result.isConcrete) { + final token = "Bearer ${result.data!['login']}"; + + // storing auth token in hive + final user = + UserModel(authToken: token, isGuest: (email == '') ? true : false); + await localApi.saveUser(user); + + // fetching User Info + + final dataState = await fetchUserInfo(); + + if (dataState is DataSuccess) { + final updatedUser = dataState.data! + .copyWithModel(authToken: user.authToken, isGuest: user.isGuest); + + // if(locator.isRegistered( )) + + // saving locally + await localApi.saveUser(updatedUser); + + return dataState; + } + return dataState; + } else if (result.hasException) { + final message = encounteredExceptionOrError(result.exception!); + + return DataFailed(message); + } + + return DataFailed('An unexpected error occured.'); + } catch (e) { + return DataFailed(e.toString()); + } + } + + String encounteredExceptionOrError(OperationException exception) { + if (exception.linkException != null) { + debugPrint(exception.linkException.toString()); + return 'Server not running'; + } else { + return exception.graphqlErrors[0].message.toString(); + } + } +} diff --git a/lib/Bloc/data/datasource/remote/remote_group_api.dart b/lib/Bloc/data/datasource/remote/remote_group_api.dart new file mode 100644 index 0000000..7203aae --- /dev/null +++ b/lib/Bloc/data/datasource/remote/remote_group_api.dart @@ -0,0 +1,130 @@ +import 'dart:developer'; + +import 'package:beacon/Bloc/core/queries/beacon.dart'; +import 'package:beacon/Bloc/core/queries/group.dart'; +import 'package:beacon/Bloc/core/resources/data_state.dart'; +import 'package:beacon/Bloc/data/models/beacon/beacon_model.dart'; +import 'package:beacon/Bloc/data/models/group/group_model.dart'; +import 'package:beacon/locator.dart'; +import 'package:graphql_flutter/graphql_flutter.dart'; + +class RemoteGroupApi { + final GraphQLClient authClient; + + RemoteGroupApi({required this.authClient}); + + final _groupqueries = GroupQueries(); + + final _beaconQueries = BeaconQueries(); + + Future>> fetchHikes( + String groupId, int page, int pageSize) async { + bool isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) { + GroupModel? group = await localApi.getGroup(groupId); + + if (group != null && group.beacons != null) { + int condition = (page - 1) * pageSize + pageSize; + int beaconLen = group.beacons!.length; + + if (condition > beaconLen) { + condition = beaconLen; + } + + List beacons = []; + + for (int i = (page - 1) * pageSize; i < condition; i++) { + BeaconModel? beaconModel = + await localApi.getBeacon(group.beacons![i]!.id); + + beaconModel != null ? beacons.add(beaconModel) : null; + } + + return DataSuccess(beacons); + } + + return DataFailed('Please check your internet connection!'); + } + + final authClient = await graphqlConfig.authClient(); + final result = await authClient.query(QueryOptions( + document: gql(_groupqueries.fetchHikes(groupId, page, pageSize)))); + + if (result.data != null && result.isConcrete) { + List hikesJson = result.data!['beacons']; + + List hikes = []; + + for (var hikeJson in hikesJson) { + BeaconModel hike = BeaconModel.fromJson(hikeJson); + hikes.add(hike); + + // storing beacon + if (1 == 1) { + log('called'); + await localApi.saveBeacon(hike); + } + } + + return DataSuccess(hikes); + } + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + + Future> createHike(String title, int startsAt, + int expiresAt, String lat, String lon, String groupID) async { + final authClient = await graphqlConfig.authClient(); + + bool isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) { + return DataFailed('Please check your internet connection!'); + } + final result = await authClient.mutate(MutationOptions( + document: gql(_beaconQueries.createBeacon( + title, startsAt, expiresAt, lat, lon, groupID)))); + + if (result.data != null && result.isConcrete) { + final hikeJson = result.data!['createBeacon']; + + final beacon = BeaconModel.fromJson(hikeJson); + + // storing beacon + await localApi.saveBeacon(beacon); + return DataSuccess(beacon); + } + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + + Future> joinHike(String shortcode) async { + bool isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) { + return DataFailed('Please check your internet connection!'); + } + final authClient = await graphqlConfig.authClient(); + final result = await authClient.mutate( + MutationOptions(document: gql(_beaconQueries.joinBeacon(shortcode)))); + + if (result.data != null && result.isConcrete) { + final hikeJosn = result.data!['joinBeacon']; + + final beacon = BeaconModel.fromJson(hikeJosn); + + // storing beacon + await localApi.saveBeacon(beacon); + + return DataSuccess(beacon); + } + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + + String encounteredExceptionOrError(OperationException exception) { + if (exception.linkException != null) { + return 'Server not running'; + } else { + return exception.graphqlErrors[0].message.toString(); + } + } +} diff --git a/lib/Bloc/data/datasource/remote/remote_home_api.dart b/lib/Bloc/data/datasource/remote/remote_home_api.dart new file mode 100644 index 0000000..06fba35 --- /dev/null +++ b/lib/Bloc/data/datasource/remote/remote_home_api.dart @@ -0,0 +1,123 @@ +import 'package:beacon/Bloc/core/queries/group.dart'; +import 'package:beacon/Bloc/core/resources/data_state.dart'; +import 'package:beacon/Bloc/data/models/group/group_model.dart'; +import 'package:beacon/Bloc/data/models/user/user_model.dart'; +import 'package:beacon/locator.dart'; +import 'package:flutter/material.dart'; +import 'package:graphql_flutter/graphql_flutter.dart'; + +class RemoteHomeApi { + final GraphQLClient _clientAuth; + RemoteHomeApi(this._clientAuth); + + final _groupQueries = GroupQueries(); + + Future>> fetchUserGroups( + int page, int pageSize) async { + final isConnected = await utils.checkInternetConnectivity(); + + print(_clientAuth.toString()); + + if (!isConnected) { + // fetching the previous data stored + // here taking all the ids of group from the user model and then fetching the groups locally from the ids + // returning all groups in one go + UserModel? usermodel = await localApi.fetchUser(); + + if (usermodel != null && usermodel.groups != null) { + // taking user groups + + int condition = (page - 1) * pageSize + pageSize; + int groupLen = usermodel.groups!.length; + + if (condition > groupLen) { + condition = groupLen; + } + + List groups = []; + + for (int i = (page - 1) * pageSize; i < condition; i++) { + GroupModel? groupModel = + await localApi.getGroup(usermodel.groups![i]!.id); + groupModel != null ? groups.add(groupModel) : null; + } + + return DataSuccess(groups); + } + } + + final clientAuth = await graphqlConfig.authClient(); + + final result = await clientAuth.query(QueryOptions( + document: gql(_groupQueries.fetchUserGroups(page, pageSize)))); + + if (result.data != null && result.isConcrete) { + List groups = []; + List groupsData = result.data!['groups']; + for (var groupData in groupsData) { + final group = GroupModel.fromJson(groupData); + + // saving locally + await localApi.saveGroup(group); + + groups.add(group); + } + return DataSuccess(groups); + } + + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + + Future> createGroup(String title) async { + final isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) + return DataFailed('Beacon is trying to connect with internet...'); + final _clientAuth = await graphqlConfig.authClient(); + final result = await _clientAuth.mutate( + MutationOptions(document: gql(_groupQueries.createGroup(title)))); + + if (result.data != null && result.isConcrete) { + GroupModel group = GroupModel.fromJson( + result.data!['createGroup'] as Map); + + // storing group + await localApi.saveGroup(group); + + return DataSuccess(group); + } + + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + + Future> joinGroup(String shortCode) async { + final isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) + return DataFailed('Beacon is trying to connect with internet...'); + final _clientAuth = await graphqlConfig.authClient(); + final result = await _clientAuth.mutate( + MutationOptions(document: gql(_groupQueries.joinGroup(shortCode)))); + + if (result.data != null && result.isConcrete) { + GroupModel group = + GroupModel.fromJson(result.data as Map); + + // storing group + await localApi.saveGroup(group); + + return DataSuccess(group); + } + + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + + String encounteredExceptionOrError(OperationException exception) { + if (exception.linkException != null) { + debugPrint(exception.linkException.toString()); + return 'Server not running'; + } else { + return exception.graphqlErrors[0].message.toString(); + } + } +} diff --git a/lib/Bloc/data/models/beacon/beacon_model.dart b/lib/Bloc/data/models/beacon/beacon_model.dart new file mode 100644 index 0000000..24afdb3 --- /dev/null +++ b/lib/Bloc/data/models/beacon/beacon_model.dart @@ -0,0 +1,86 @@ +import 'package:beacon/Bloc/data/models/group/group_model.dart'; +import 'package:beacon/Bloc/data/models/landmark/landmark_model.dart'; +import 'package:beacon/Bloc/data/models/location/location_model.dart'; +import 'package:beacon/Bloc/data/models/user/user_model.dart'; +import 'package:beacon/Bloc/domain/entities/beacon/beacon_entity.dart'; +import 'package:hive/hive.dart'; +import 'package:json_annotation/json_annotation.dart'; +part 'beacon_model.g.dart'; + +@HiveType(typeId: 20) +@JsonSerializable() +class BeaconModel implements BeaconEntity { + @HiveField(0) + String? id; + @HiveField(1) + String? title; + @HiveField(2) + UserModel? leader; + @HiveField(3) + GroupModel? group; + @HiveField(4) + String? shortcode; + @HiveField(5) + List? followers; + @HiveField(6) + List? landmarks; + @HiveField(7) + LocationModel? location; + @HiveField(8) + List? route; + @HiveField(9) + int? startsAt; + @HiveField(10) + int? expiresAt; + + BeaconModel({ + this.id, + this.title, + this.leader, + this.group, + this.shortcode, + this.followers, + this.landmarks, + this.location, + this.route, + this.startsAt, + this.expiresAt, + }); + + @override + $BeaconEntityCopyWith get copyWith => + throw UnimplementedError(); + + factory BeaconModel.fromJson(Map json) => + _$BeaconModelFromJson(json); + + Map toJson() => _$BeaconModelToJson(this); + + BeaconModel copyWithModel({ + String? id, + String? title, + UserModel? leader, + GroupModel? group, + String? shortcode, + List? followers, + List? landmarks, + LocationModel? location, + List? route, + int? startsAt, + int? expiresAt, + }) { + return BeaconModel( + id: id ?? this.id, + title: title ?? this.title, + leader: leader ?? this.leader, + group: group ?? this.group, + shortcode: shortcode ?? this.shortcode, + followers: followers ?? this.followers, + landmarks: landmarks ?? this.landmarks, + location: location ?? this.location, + route: route ?? this.route, + startsAt: startsAt ?? this.startsAt, + expiresAt: expiresAt ?? this.expiresAt, + ); + } +} diff --git a/lib/Bloc/data/models/beacon/beacon_model.g.dart b/lib/Bloc/data/models/beacon/beacon_model.g.dart new file mode 100644 index 0000000..e94a13e --- /dev/null +++ b/lib/Bloc/data/models/beacon/beacon_model.g.dart @@ -0,0 +1,121 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'beacon_model.dart'; + +// ************************************************************************** +// TypeAdapterGenerator +// ************************************************************************** + +class BeaconModelAdapter extends TypeAdapter { + @override + final int typeId = 20; + + @override + BeaconModel read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return BeaconModel( + id: fields[0] as String?, + title: fields[1] as String?, + leader: fields[2] as UserModel?, + group: fields[3] as GroupModel?, + shortcode: fields[4] as String?, + followers: (fields[5] as List?)?.cast(), + landmarks: (fields[6] as List?)?.cast(), + location: fields[7] as LocationModel?, + route: (fields[8] as List?)?.cast(), + startsAt: fields[9] as int?, + expiresAt: fields[10] as int?, + ); + } + + @override + void write(BinaryWriter writer, BeaconModel obj) { + writer + ..writeByte(11) + ..writeByte(0) + ..write(obj.id) + ..writeByte(1) + ..write(obj.title) + ..writeByte(2) + ..write(obj.leader) + ..writeByte(3) + ..write(obj.group) + ..writeByte(4) + ..write(obj.shortcode) + ..writeByte(5) + ..write(obj.followers) + ..writeByte(6) + ..write(obj.landmarks) + ..writeByte(7) + ..write(obj.location) + ..writeByte(8) + ..write(obj.route) + ..writeByte(9) + ..write(obj.startsAt) + ..writeByte(10) + ..write(obj.expiresAt); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is BeaconModelAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +BeaconModel _$BeaconModelFromJson(Map json) => BeaconModel( + id: json['_id'] as String?, + title: json['title'] as String?, + leader: json['leader'] == null + ? null + : UserModel.fromJson(json['leader'] as Map), + group: json['group'] == null + ? null + : GroupModel.fromJson(json['group'] as Map), + shortcode: json['shortcode'] as String?, + followers: (json['followers'] as List?) + ?.map((e) => + e == null ? null : UserModel.fromJson(e as Map)) + .toList(), + landmarks: (json['landmarks'] as List?) + ?.map((e) => e == null + ? null + : LandMarkModel.fromJson(e as Map)) + .toList(), + location: json['location'] == null + ? null + : LocationModel.fromJson(json['location'] as Map), + route: (json['route'] as List?) + ?.map((e) => e == null + ? null + : LocationModel.fromJson(e as Map)) + .toList(), + startsAt: (json['startsAt'] as num?)?.toInt(), + expiresAt: (json['expiresAt'] as num?)?.toInt(), + ); + +Map _$BeaconModelToJson(BeaconModel instance) => + { + '_id': instance.id, + 'title': instance.title, + 'leader': instance.leader, + 'group': instance.group, + 'shortcode': instance.shortcode, + 'followers': instance.followers, + 'landmarks': instance.landmarks, + 'location': instance.location, + 'route': instance.route, + 'startsAt': instance.startsAt, + 'expiresAt': instance.expiresAt, + }; diff --git a/lib/Bloc/data/models/group/group_model.dart b/lib/Bloc/data/models/group/group_model.dart new file mode 100644 index 0000000..faa45dc --- /dev/null +++ b/lib/Bloc/data/models/group/group_model.dart @@ -0,0 +1,58 @@ +import 'package:beacon/Bloc/data/models/beacon/beacon_model.dart'; +import 'package:beacon/Bloc/data/models/user/user_model.dart'; +import 'package:beacon/Bloc/domain/entities/group/group_entity.dart'; +import 'package:hive/hive.dart'; +import 'package:json_annotation/json_annotation.dart'; +part 'group_model.g.dart'; + +@HiveType(typeId: 30) +@JsonSerializable() +class GroupModel implements GroupEntity { + @HiveField(0) + String? id; + @HiveField(1) + String? title; + @HiveField(2) + UserModel? leader; + @HiveField(3) + List? members; + @HiveField(4) + String? shortcode; + @HiveField(5) + List? beacons; + + GroupModel({ + this.id, + this.title, + this.leader, + this.members, + this.shortcode, + this.beacons, + }); + + @override + $GroupEntityCopyWith get copyWith => throw UnimplementedError(); + + factory GroupModel.fromJson(Map json) => + _$GroupModelFromJson(json); + + Map toJson() => _$GroupModelToJson(this); + + GroupModel copyWithModel({ + String? id, + String? title, + UserModel? leader, + List? members, + String? shortcode, + List? beacons, + }) { + return GroupModel( + id: id ?? this.id, + title: title ?? this.title, + leader: leader ?? this.leader, + members: members ?? this.members, + shortcode: shortcode ?? this.shortcode, + beacons: beacons ?? this.beacons, + ); + } +} diff --git a/lib/Bloc/data/models/group/group_model.g.dart b/lib/Bloc/data/models/group/group_model.g.dart new file mode 100644 index 0000000..64d110e --- /dev/null +++ b/lib/Bloc/data/models/group/group_model.g.dart @@ -0,0 +1,88 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'group_model.dart'; + +// ************************************************************************** +// TypeAdapterGenerator +// ************************************************************************** + +class GroupModelAdapter extends TypeAdapter { + @override + final int typeId = 30; + + @override + GroupModel read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return GroupModel( + id: fields[0] as String?, + title: fields[1] as String?, + leader: fields[2] as UserModel?, + members: (fields[3] as List?)?.cast(), + shortcode: fields[4] as String?, + beacons: (fields[5] as List?)?.cast(), + ); + } + + @override + void write(BinaryWriter writer, GroupModel obj) { + writer + ..writeByte(6) + ..writeByte(0) + ..write(obj.id) + ..writeByte(1) + ..write(obj.title) + ..writeByte(2) + ..write(obj.leader) + ..writeByte(3) + ..write(obj.members) + ..writeByte(4) + ..write(obj.shortcode) + ..writeByte(5) + ..write(obj.beacons); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is GroupModelAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +GroupModel _$GroupModelFromJson(Map json) => GroupModel( + id: json['_id'] as String?, + title: json['title'] as String?, + leader: json['leader'] == null + ? null + : UserModel.fromJson(json['leader'] as Map), + members: (json['members'] as List?) + ?.map((e) => + e == null ? null : UserModel.fromJson(e as Map)) + .toList(), + shortcode: json['shortcode'] as String?, + beacons: (json['beacons'] as List?) + ?.map((e) => e == null + ? null + : BeaconModel.fromJson(e as Map)) + .toList(), + ); + +Map _$GroupModelToJson(GroupModel instance) => + { + '_id': instance.id, + 'title': instance.title, + 'leader': instance.leader, + 'members': instance.members, + 'shortcode': instance.shortcode, + 'beacons': instance.beacons, + }; diff --git a/lib/Bloc/data/models/landmark/landmark_model.dart b/lib/Bloc/data/models/landmark/landmark_model.dart new file mode 100644 index 0000000..779c3c2 --- /dev/null +++ b/lib/Bloc/data/models/landmark/landmark_model.dart @@ -0,0 +1,36 @@ +import 'package:beacon/Bloc/data/models/location/location_model.dart'; +import 'package:beacon/Bloc/domain/entities/landmark/landmark_entity.dart'; +import 'package:hive/hive.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'landmark_model.g.dart'; + +@HiveType(typeId: 50) +@JsonSerializable() +class LandMarkModel implements LandMarkEntity { + @HiveField(0) + String? title; + @HiveField(1) + LocationModel? location; + + LandMarkModel({this.title, this.location}); + + @override + $LandMarkEntityCopyWith get copyWith => + throw UnimplementedError(); + + factory LandMarkModel.fromJson(Map json) => + _$LandMarkModelFromJson(json); + + Map toJson() => _$LandMarkModelToJson(this); + + LandMarkModel copyWithModel({ + String? title, + LocationModel? location, + }) { + return LandMarkModel( + title: title ?? this.title, + location: location ?? this.location, + ); + } +} diff --git a/lib/Bloc/data/models/landmark/landmark_model.g.dart b/lib/Bloc/data/models/landmark/landmark_model.g.dart new file mode 100644 index 0000000..272a500 --- /dev/null +++ b/lib/Bloc/data/models/landmark/landmark_model.g.dart @@ -0,0 +1,62 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'landmark_model.dart'; + +// ************************************************************************** +// TypeAdapterGenerator +// ************************************************************************** + +class LandMarkModelAdapter extends TypeAdapter { + @override + final int typeId = 50; + + @override + LandMarkModel read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return LandMarkModel( + title: fields[0] as String?, + location: fields[1] as LocationModel?, + ); + } + + @override + void write(BinaryWriter writer, LandMarkModel obj) { + writer + ..writeByte(2) + ..writeByte(0) + ..write(obj.title) + ..writeByte(1) + ..write(obj.location); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is LandMarkModelAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +LandMarkModel _$LandMarkModelFromJson(Map json) => + LandMarkModel( + title: json['title'] as String?, + location: json['location'] == null + ? null + : LocationModel.fromJson(json['location'] as Map), + ); + +Map _$LandMarkModelToJson(LandMarkModel instance) => + { + 'title': instance.title, + 'location': instance.location, + }; diff --git a/lib/Bloc/data/models/location/location_model.dart b/lib/Bloc/data/models/location/location_model.dart new file mode 100644 index 0000000..79701b6 --- /dev/null +++ b/lib/Bloc/data/models/location/location_model.dart @@ -0,0 +1,38 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:hive/hive.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:beacon/Bloc/domain/entities/location/location_entity.dart'; +part 'location_model.g.dart'; + +@HiveType(typeId: 40) +@JsonSerializable() +class LocationModel implements LocationEntity { + @HiveField(0) + final String? lat; + @HiveField(1) + final String? lon; + + LocationModel({ + this.lat, + this.lon, + }); + + factory LocationModel.fromJson(Map json) => + _$LocationModelFromJson(json); + + Map toJson() => _$LocationModelToJson(this); + + LocationModel copyWithModel({ + String? lat, + String? long, + }) { + return LocationModel( + lat: lat ?? this.lat, + lon: lon ?? this.lon, + ); + } + + @override + $LocationEntityCopyWith get copyWith => + throw UnimplementedError(); +} diff --git a/lib/Bloc/data/models/location/location_model.g.dart b/lib/Bloc/data/models/location/location_model.g.dart new file mode 100644 index 0000000..174fae3 --- /dev/null +++ b/lib/Bloc/data/models/location/location_model.g.dart @@ -0,0 +1,60 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'location_model.dart'; + +// ************************************************************************** +// TypeAdapterGenerator +// ************************************************************************** + +class LocationModelAdapter extends TypeAdapter { + @override + final int typeId = 40; + + @override + LocationModel read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return LocationModel( + lat: fields[0] as String?, + lon: fields[1] as String?, + ); + } + + @override + void write(BinaryWriter writer, LocationModel obj) { + writer + ..writeByte(2) + ..writeByte(0) + ..write(obj.lat) + ..writeByte(1) + ..write(obj.lon); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is LocationModelAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +LocationModel _$LocationModelFromJson(Map json) => + LocationModel( + lat: json['lat'] as String?, + lon: json['lon'] as String?, + ); + +Map _$LocationModelToJson(LocationModel instance) => + { + 'lat': instance.lat, + 'lon': instance.lon, + }; diff --git a/lib/Bloc/data/models/user/user_model.dart b/lib/Bloc/data/models/user/user_model.dart new file mode 100644 index 0000000..41bcd7b --- /dev/null +++ b/lib/Bloc/data/models/user/user_model.dart @@ -0,0 +1,76 @@ +import 'package:beacon/Bloc/data/models/beacon/beacon_model.dart'; +import 'package:beacon/Bloc/data/models/group/group_model.dart'; +import 'package:beacon/Bloc/data/models/location/location_model.dart'; +import 'package:beacon/Bloc/domain/entities/user/user_entity.dart'; +import 'package:hive/hive.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'user_model.g.dart'; + +@HiveType(typeId: 10) +@JsonSerializable() +class UserModel implements UserEntity { + @HiveField(0) + String? id; + + @HiveField(1) + String? name; + + @HiveField(2) + String? email; + + @HiveField(3) + String? authToken; + + @HiveField(4) + bool? isGuest; + + @HiveField(5) + List? groups; + + @HiveField(6) + List? beacons; + + @HiveField(7) + LocationModel? location; + + UserModel( + {this.authToken, + this.beacons, + this.email, + this.groups, + this.id, + this.isGuest, + this.location, + this.name}); + + @override + $UserEntityCopyWith get copyWith => throw UnimplementedError(); + + factory UserModel.fromJson(Map json) => + _$UserModelFromJson(json); + + Map toJson() => _$UserModelToJson(this); + + UserModel copyWithModel({ + String? id, + String? name, + String? authToken, + String? email, + bool? isGuest, + List? groups, + List? beacons, + LocationModel? location, + }) { + return UserModel( + id: id ?? this.id, + name: name ?? this.name, + authToken: authToken ?? this.authToken, + email: email ?? this.email, + isGuest: isGuest ?? this.isGuest, + groups: groups ?? this.groups, + beacons: beacons ?? this.beacons, + location: location ?? this.location, + ); + } +} diff --git a/lib/Bloc/data/models/user/user_model.g.dart b/lib/Bloc/data/models/user/user_model.g.dart new file mode 100644 index 0000000..e52a890 --- /dev/null +++ b/lib/Bloc/data/models/user/user_model.g.dart @@ -0,0 +1,97 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'user_model.dart'; + +// ************************************************************************** +// TypeAdapterGenerator +// ************************************************************************** + +class UserModelAdapter extends TypeAdapter { + @override + final int typeId = 10; + + @override + UserModel read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return UserModel( + authToken: fields[3] as String?, + beacons: (fields[6] as List?)?.cast(), + email: fields[2] as String?, + groups: (fields[5] as List?)?.cast(), + id: fields[0] as String?, + isGuest: fields[4] as bool?, + location: fields[7] as LocationModel?, + name: fields[1] as String?, + ); + } + + @override + void write(BinaryWriter writer, UserModel obj) { + writer + ..writeByte(8) + ..writeByte(0) + ..write(obj.id) + ..writeByte(1) + ..write(obj.name) + ..writeByte(2) + ..write(obj.email) + ..writeByte(3) + ..write(obj.authToken) + ..writeByte(4) + ..write(obj.isGuest) + ..writeByte(5) + ..write(obj.groups) + ..writeByte(6) + ..write(obj.beacons) + ..writeByte(7) + ..write(obj.location); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is UserModelAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +UserModel _$UserModelFromJson(Map json) => UserModel( + authToken: json['authToken'] as String?, + beacons: (json['beacons'] as List?) + ?.map((e) => e == null + ? null + : BeaconModel.fromJson(e as Map)) + .toList(), + email: json['email'] as String?, + groups: (json['groups'] as List?) + ?.map((e) => + e == null ? null : GroupModel.fromJson(e as Map)) + .toList(), + id: json['_id'] as String?, + isGuest: json['isGuest'] as bool?, + location: json['location'] == null + ? null + : LocationModel.fromJson(json['location'] as Map), + name: json['name'] as String?, + ); + +Map _$UserModelToJson(UserModel instance) => { + '_id': instance.id, + 'name': instance.name, + 'email': instance.email, + 'authToken': instance.authToken, + 'isGuest': instance.isGuest, + 'groups': instance.groups, + 'beacons': instance.beacons, + 'location': instance.location, + }; diff --git a/lib/Bloc/data/repositories/auth_repository_implementation.dart b/lib/Bloc/data/repositories/auth_repository_implementation.dart new file mode 100644 index 0000000..3ed41e6 --- /dev/null +++ b/lib/Bloc/data/repositories/auth_repository_implementation.dart @@ -0,0 +1,26 @@ +import 'package:beacon/Bloc/core/resources/data_state.dart'; +import 'package:beacon/Bloc/data/datasource/remote/remote_auth_api.dart'; +import 'package:beacon/Bloc/data/models/user/user_model.dart'; +import 'package:beacon/Bloc/domain/repositories/auth_repository.dart'; + +class AuthRepositoryImplementation extends AuthRepository { + final RemoteAuthApi remoteAuthApi; + + AuthRepositoryImplementation({required this.remoteAuthApi}); + + @override + Future> getUser() { + return remoteAuthApi.fetchUserInfo(); + } + + @override + Future> login(String email, String password) { + return remoteAuthApi.login(email, password); + } + + @override + Future> register( + String name, String email, String password) { + return remoteAuthApi.register(name, email, password); + } +} diff --git a/lib/Bloc/data/repositories/group_repository_implementation.dart b/lib/Bloc/data/repositories/group_repository_implementation.dart new file mode 100644 index 0000000..94ada78 --- /dev/null +++ b/lib/Bloc/data/repositories/group_repository_implementation.dart @@ -0,0 +1,26 @@ +import 'package:beacon/Bloc/core/resources/data_state.dart'; +import 'package:beacon/Bloc/data/datasource/remote/remote_group_api.dart'; +import 'package:beacon/Bloc/data/models/beacon/beacon_model.dart'; +import 'package:beacon/Bloc/domain/repositories/group_repository.dart'; + +class GroupRepostioryImplementation extends GroupRepository { + final RemoteGroupApi remoteGroupApi; + GroupRepostioryImplementation({required this.remoteGroupApi}); + @override + Future> createHike(String title, int startsAt, + int expiresAt, String lat, String lon, String groupID) async { + return remoteGroupApi.createHike( + title, startsAt, expiresAt, lat, lon, groupID); + } + + @override + Future>> fetchHikes( + String groupID, int page, int pageSize) { + return remoteGroupApi.fetchHikes(groupID, page, pageSize); + } + + @override + Future> joinHike(String shortcode) { + return remoteGroupApi.joinHike(shortcode); + } +} diff --git a/lib/Bloc/data/repositories/home_repository_implementation.dart b/lib/Bloc/data/repositories/home_repository_implementation.dart new file mode 100644 index 0000000..c63b036 --- /dev/null +++ b/lib/Bloc/data/repositories/home_repository_implementation.dart @@ -0,0 +1,25 @@ +import 'package:beacon/Bloc/core/resources/data_state.dart'; +import 'package:beacon/Bloc/data/datasource/remote/remote_home_api.dart'; +import 'package:beacon/Bloc/data/models/group/group_model.dart'; +import 'package:beacon/Bloc/domain/repositories/home_repository.dart'; + +class HomeRepostitoryImplementation extends HomeRepository { + final RemoteHomeApi remoteHomeApi; + + HomeRepostitoryImplementation({required this.remoteHomeApi}); + + @override + Future> createGroup(String title) { + return remoteHomeApi.createGroup(title); + } + + @override + Future>> fetchGroups(int page, int pageSize) { + return remoteHomeApi.fetchUserGroups(page, pageSize); + } + + @override + Future> joinGroup(String shortCode) { + return remoteHomeApi.joinGroup(shortCode); + } +} diff --git a/lib/Bloc/domain/entities/beacon/beacon_entity.dart b/lib/Bloc/domain/entities/beacon/beacon_entity.dart new file mode 100644 index 0000000..2f1ce48 --- /dev/null +++ b/lib/Bloc/domain/entities/beacon/beacon_entity.dart @@ -0,0 +1,23 @@ +import 'package:beacon/Bloc/domain/entities/group/group_entity.dart'; +import 'package:beacon/Bloc/domain/entities/landmark/landmark_entity.dart'; +import 'package:beacon/Bloc/domain/entities/location/location_entity.dart'; +import 'package:beacon/Bloc/domain/entities/user/user_entity.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'beacon_entity.freezed.dart'; + +@freezed +class BeaconEntity with _$BeaconEntity { + const factory BeaconEntity( + {String? id, + String? shortcode, + int? startsAt, + int? expiresAt, + String? title, + UserEntity? leader, + List? followers, + List? route, + List? landmarks, + LocationEntity? location, + GroupEntity? group}) = _BeaconEntity; +} diff --git a/lib/Bloc/domain/entities/beacon/beacon_entity.freezed.dart b/lib/Bloc/domain/entities/beacon/beacon_entity.freezed.dart new file mode 100644 index 0000000..a623a54 --- /dev/null +++ b/lib/Bloc/domain/entities/beacon/beacon_entity.freezed.dart @@ -0,0 +1,428 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'beacon_entity.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +/// @nodoc +mixin _$BeaconEntity { + String? get id => throw _privateConstructorUsedError; + String? get shortcode => throw _privateConstructorUsedError; + int? get startsAt => throw _privateConstructorUsedError; + int? get expiresAt => throw _privateConstructorUsedError; + String? get title => throw _privateConstructorUsedError; + UserEntity? get leader => throw _privateConstructorUsedError; + List? get followers => throw _privateConstructorUsedError; + List? get route => throw _privateConstructorUsedError; + List? get landmarks => throw _privateConstructorUsedError; + LocationEntity? get location => throw _privateConstructorUsedError; + GroupEntity? get group => throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $BeaconEntityCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $BeaconEntityCopyWith<$Res> { + factory $BeaconEntityCopyWith( + BeaconEntity value, $Res Function(BeaconEntity) then) = + _$BeaconEntityCopyWithImpl<$Res, BeaconEntity>; + @useResult + $Res call( + {String? id, + String? shortcode, + int? startsAt, + int? expiresAt, + String? title, + UserEntity? leader, + List? followers, + List? route, + List? landmarks, + LocationEntity? location, + GroupEntity? group}); + + $UserEntityCopyWith<$Res>? get leader; + $LocationEntityCopyWith<$Res>? get location; + $GroupEntityCopyWith<$Res>? get group; +} + +/// @nodoc +class _$BeaconEntityCopyWithImpl<$Res, $Val extends BeaconEntity> + implements $BeaconEntityCopyWith<$Res> { + _$BeaconEntityCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = freezed, + Object? shortcode = freezed, + Object? startsAt = freezed, + Object? expiresAt = freezed, + Object? title = freezed, + Object? leader = freezed, + Object? followers = freezed, + Object? route = freezed, + Object? landmarks = freezed, + Object? location = freezed, + Object? group = freezed, + }) { + return _then(_value.copyWith( + id: freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + shortcode: freezed == shortcode + ? _value.shortcode + : shortcode // ignore: cast_nullable_to_non_nullable + as String?, + startsAt: freezed == startsAt + ? _value.startsAt + : startsAt // ignore: cast_nullable_to_non_nullable + as int?, + expiresAt: freezed == expiresAt + ? _value.expiresAt + : expiresAt // ignore: cast_nullable_to_non_nullable + as int?, + title: freezed == title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String?, + leader: freezed == leader + ? _value.leader + : leader // ignore: cast_nullable_to_non_nullable + as UserEntity?, + followers: freezed == followers + ? _value.followers + : followers // ignore: cast_nullable_to_non_nullable + as List?, + route: freezed == route + ? _value.route + : route // ignore: cast_nullable_to_non_nullable + as List?, + landmarks: freezed == landmarks + ? _value.landmarks + : landmarks // ignore: cast_nullable_to_non_nullable + as List?, + location: freezed == location + ? _value.location + : location // ignore: cast_nullable_to_non_nullable + as LocationEntity?, + group: freezed == group + ? _value.group + : group // ignore: cast_nullable_to_non_nullable + as GroupEntity?, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $UserEntityCopyWith<$Res>? get leader { + if (_value.leader == null) { + return null; + } + + return $UserEntityCopyWith<$Res>(_value.leader!, (value) { + return _then(_value.copyWith(leader: value) as $Val); + }); + } + + @override + @pragma('vm:prefer-inline') + $LocationEntityCopyWith<$Res>? get location { + if (_value.location == null) { + return null; + } + + return $LocationEntityCopyWith<$Res>(_value.location!, (value) { + return _then(_value.copyWith(location: value) as $Val); + }); + } + + @override + @pragma('vm:prefer-inline') + $GroupEntityCopyWith<$Res>? get group { + if (_value.group == null) { + return null; + } + + return $GroupEntityCopyWith<$Res>(_value.group!, (value) { + return _then(_value.copyWith(group: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$BeaconEntityImplCopyWith<$Res> + implements $BeaconEntityCopyWith<$Res> { + factory _$$BeaconEntityImplCopyWith( + _$BeaconEntityImpl value, $Res Function(_$BeaconEntityImpl) then) = + __$$BeaconEntityImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String? id, + String? shortcode, + int? startsAt, + int? expiresAt, + String? title, + UserEntity? leader, + List? followers, + List? route, + List? landmarks, + LocationEntity? location, + GroupEntity? group}); + + @override + $UserEntityCopyWith<$Res>? get leader; + @override + $LocationEntityCopyWith<$Res>? get location; + @override + $GroupEntityCopyWith<$Res>? get group; +} + +/// @nodoc +class __$$BeaconEntityImplCopyWithImpl<$Res> + extends _$BeaconEntityCopyWithImpl<$Res, _$BeaconEntityImpl> + implements _$$BeaconEntityImplCopyWith<$Res> { + __$$BeaconEntityImplCopyWithImpl( + _$BeaconEntityImpl _value, $Res Function(_$BeaconEntityImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = freezed, + Object? shortcode = freezed, + Object? startsAt = freezed, + Object? expiresAt = freezed, + Object? title = freezed, + Object? leader = freezed, + Object? followers = freezed, + Object? route = freezed, + Object? landmarks = freezed, + Object? location = freezed, + Object? group = freezed, + }) { + return _then(_$BeaconEntityImpl( + id: freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + shortcode: freezed == shortcode + ? _value.shortcode + : shortcode // ignore: cast_nullable_to_non_nullable + as String?, + startsAt: freezed == startsAt + ? _value.startsAt + : startsAt // ignore: cast_nullable_to_non_nullable + as int?, + expiresAt: freezed == expiresAt + ? _value.expiresAt + : expiresAt // ignore: cast_nullable_to_non_nullable + as int?, + title: freezed == title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String?, + leader: freezed == leader + ? _value.leader + : leader // ignore: cast_nullable_to_non_nullable + as UserEntity?, + followers: freezed == followers + ? _value._followers + : followers // ignore: cast_nullable_to_non_nullable + as List?, + route: freezed == route + ? _value._route + : route // ignore: cast_nullable_to_non_nullable + as List?, + landmarks: freezed == landmarks + ? _value._landmarks + : landmarks // ignore: cast_nullable_to_non_nullable + as List?, + location: freezed == location + ? _value.location + : location // ignore: cast_nullable_to_non_nullable + as LocationEntity?, + group: freezed == group + ? _value.group + : group // ignore: cast_nullable_to_non_nullable + as GroupEntity?, + )); + } +} + +/// @nodoc + +class _$BeaconEntityImpl implements _BeaconEntity { + const _$BeaconEntityImpl( + {this.id, + this.shortcode, + this.startsAt, + this.expiresAt, + this.title, + this.leader, + final List? followers, + final List? route, + final List? landmarks, + this.location, + this.group}) + : _followers = followers, + _route = route, + _landmarks = landmarks; + + @override + final String? id; + @override + final String? shortcode; + @override + final int? startsAt; + @override + final int? expiresAt; + @override + final String? title; + @override + final UserEntity? leader; + final List? _followers; + @override + List? get followers { + final value = _followers; + if (value == null) return null; + if (_followers is EqualUnmodifiableListView) return _followers; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + final List? _route; + @override + List? get route { + final value = _route; + if (value == null) return null; + if (_route is EqualUnmodifiableListView) return _route; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + final List? _landmarks; + @override + List? get landmarks { + final value = _landmarks; + if (value == null) return null; + if (_landmarks is EqualUnmodifiableListView) return _landmarks; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + @override + final LocationEntity? location; + @override + final GroupEntity? group; + + @override + String toString() { + return 'BeaconEntity(id: $id, shortcode: $shortcode, startsAt: $startsAt, expiresAt: $expiresAt, title: $title, leader: $leader, followers: $followers, route: $route, landmarks: $landmarks, location: $location, group: $group)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$BeaconEntityImpl && + (identical(other.id, id) || other.id == id) && + (identical(other.shortcode, shortcode) || + other.shortcode == shortcode) && + (identical(other.startsAt, startsAt) || + other.startsAt == startsAt) && + (identical(other.expiresAt, expiresAt) || + other.expiresAt == expiresAt) && + (identical(other.title, title) || other.title == title) && + (identical(other.leader, leader) || other.leader == leader) && + const DeepCollectionEquality() + .equals(other._followers, _followers) && + const DeepCollectionEquality().equals(other._route, _route) && + const DeepCollectionEquality() + .equals(other._landmarks, _landmarks) && + (identical(other.location, location) || + other.location == location) && + (identical(other.group, group) || other.group == group)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + id, + shortcode, + startsAt, + expiresAt, + title, + leader, + const DeepCollectionEquality().hash(_followers), + const DeepCollectionEquality().hash(_route), + const DeepCollectionEquality().hash(_landmarks), + location, + group); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$BeaconEntityImplCopyWith<_$BeaconEntityImpl> get copyWith => + __$$BeaconEntityImplCopyWithImpl<_$BeaconEntityImpl>(this, _$identity); +} + +abstract class _BeaconEntity implements BeaconEntity { + const factory _BeaconEntity( + {final String? id, + final String? shortcode, + final int? startsAt, + final int? expiresAt, + final String? title, + final UserEntity? leader, + final List? followers, + final List? route, + final List? landmarks, + final LocationEntity? location, + final GroupEntity? group}) = _$BeaconEntityImpl; + + @override + String? get id; + @override + String? get shortcode; + @override + int? get startsAt; + @override + int? get expiresAt; + @override + String? get title; + @override + UserEntity? get leader; + @override + List? get followers; + @override + List? get route; + @override + List? get landmarks; + @override + LocationEntity? get location; + @override + GroupEntity? get group; + @override + @JsonKey(ignore: true) + _$$BeaconEntityImplCopyWith<_$BeaconEntityImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/Bloc/domain/entities/group/group_entity.dart b/lib/Bloc/domain/entities/group/group_entity.dart new file mode 100644 index 0000000..d036619 --- /dev/null +++ b/lib/Bloc/domain/entities/group/group_entity.dart @@ -0,0 +1,17 @@ +import 'package:beacon/Bloc/domain/entities/beacon/beacon_entity.dart'; +import 'package:beacon/Bloc/domain/entities/user/user_entity.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'group_entity.freezed.dart'; + +@freezed +class GroupEntity with _$GroupEntity { + const factory GroupEntity({ + String? id, + List? beacons, + List? members, + UserEntity? leader, + String? title, + String? shortcode, + }) = _GroupEntity; +} diff --git a/lib/Bloc/domain/entities/group/group_entity.freezed.dart b/lib/Bloc/domain/entities/group/group_entity.freezed.dart new file mode 100644 index 0000000..c601ea0 --- /dev/null +++ b/lib/Bloc/domain/entities/group/group_entity.freezed.dart @@ -0,0 +1,279 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'group_entity.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +/// @nodoc +mixin _$GroupEntity { + String? get id => throw _privateConstructorUsedError; + List? get beacons => throw _privateConstructorUsedError; + List? get members => throw _privateConstructorUsedError; + UserEntity? get leader => throw _privateConstructorUsedError; + String? get title => throw _privateConstructorUsedError; + String? get shortcode => throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $GroupEntityCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $GroupEntityCopyWith<$Res> { + factory $GroupEntityCopyWith( + GroupEntity value, $Res Function(GroupEntity) then) = + _$GroupEntityCopyWithImpl<$Res, GroupEntity>; + @useResult + $Res call( + {String? id, + List? beacons, + List? members, + UserEntity? leader, + String? title, + String? shortcode}); + + $UserEntityCopyWith<$Res>? get leader; +} + +/// @nodoc +class _$GroupEntityCopyWithImpl<$Res, $Val extends GroupEntity> + implements $GroupEntityCopyWith<$Res> { + _$GroupEntityCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = freezed, + Object? beacons = freezed, + Object? members = freezed, + Object? leader = freezed, + Object? title = freezed, + Object? shortcode = freezed, + }) { + return _then(_value.copyWith( + id: freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + beacons: freezed == beacons + ? _value.beacons + : beacons // ignore: cast_nullable_to_non_nullable + as List?, + members: freezed == members + ? _value.members + : members // ignore: cast_nullable_to_non_nullable + as List?, + leader: freezed == leader + ? _value.leader + : leader // ignore: cast_nullable_to_non_nullable + as UserEntity?, + title: freezed == title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String?, + shortcode: freezed == shortcode + ? _value.shortcode + : shortcode // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $UserEntityCopyWith<$Res>? get leader { + if (_value.leader == null) { + return null; + } + + return $UserEntityCopyWith<$Res>(_value.leader!, (value) { + return _then(_value.copyWith(leader: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$GroupEntityImplCopyWith<$Res> + implements $GroupEntityCopyWith<$Res> { + factory _$$GroupEntityImplCopyWith( + _$GroupEntityImpl value, $Res Function(_$GroupEntityImpl) then) = + __$$GroupEntityImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String? id, + List? beacons, + List? members, + UserEntity? leader, + String? title, + String? shortcode}); + + @override + $UserEntityCopyWith<$Res>? get leader; +} + +/// @nodoc +class __$$GroupEntityImplCopyWithImpl<$Res> + extends _$GroupEntityCopyWithImpl<$Res, _$GroupEntityImpl> + implements _$$GroupEntityImplCopyWith<$Res> { + __$$GroupEntityImplCopyWithImpl( + _$GroupEntityImpl _value, $Res Function(_$GroupEntityImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = freezed, + Object? beacons = freezed, + Object? members = freezed, + Object? leader = freezed, + Object? title = freezed, + Object? shortcode = freezed, + }) { + return _then(_$GroupEntityImpl( + id: freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + beacons: freezed == beacons + ? _value._beacons + : beacons // ignore: cast_nullable_to_non_nullable + as List?, + members: freezed == members + ? _value._members + : members // ignore: cast_nullable_to_non_nullable + as List?, + leader: freezed == leader + ? _value.leader + : leader // ignore: cast_nullable_to_non_nullable + as UserEntity?, + title: freezed == title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String?, + shortcode: freezed == shortcode + ? _value.shortcode + : shortcode // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc + +class _$GroupEntityImpl implements _GroupEntity { + const _$GroupEntityImpl( + {this.id, + final List? beacons, + final List? members, + this.leader, + this.title, + this.shortcode}) + : _beacons = beacons, + _members = members; + + @override + final String? id; + final List? _beacons; + @override + List? get beacons { + final value = _beacons; + if (value == null) return null; + if (_beacons is EqualUnmodifiableListView) return _beacons; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + final List? _members; + @override + List? get members { + final value = _members; + if (value == null) return null; + if (_members is EqualUnmodifiableListView) return _members; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + @override + final UserEntity? leader; + @override + final String? title; + @override + final String? shortcode; + + @override + String toString() { + return 'GroupEntity(id: $id, beacons: $beacons, members: $members, leader: $leader, title: $title, shortcode: $shortcode)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$GroupEntityImpl && + (identical(other.id, id) || other.id == id) && + const DeepCollectionEquality().equals(other._beacons, _beacons) && + const DeepCollectionEquality().equals(other._members, _members) && + (identical(other.leader, leader) || other.leader == leader) && + (identical(other.title, title) || other.title == title) && + (identical(other.shortcode, shortcode) || + other.shortcode == shortcode)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + id, + const DeepCollectionEquality().hash(_beacons), + const DeepCollectionEquality().hash(_members), + leader, + title, + shortcode); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$GroupEntityImplCopyWith<_$GroupEntityImpl> get copyWith => + __$$GroupEntityImplCopyWithImpl<_$GroupEntityImpl>(this, _$identity); +} + +abstract class _GroupEntity implements GroupEntity { + const factory _GroupEntity( + {final String? id, + final List? beacons, + final List? members, + final UserEntity? leader, + final String? title, + final String? shortcode}) = _$GroupEntityImpl; + + @override + String? get id; + @override + List? get beacons; + @override + List? get members; + @override + UserEntity? get leader; + @override + String? get title; + @override + String? get shortcode; + @override + @JsonKey(ignore: true) + _$$GroupEntityImplCopyWith<_$GroupEntityImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/Bloc/domain/entities/landmark/landmark_entity.dart b/lib/Bloc/domain/entities/landmark/landmark_entity.dart new file mode 100644 index 0000000..883699e --- /dev/null +++ b/lib/Bloc/domain/entities/landmark/landmark_entity.dart @@ -0,0 +1,9 @@ +import 'package:beacon/Bloc/domain/entities/location/location_entity.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +part 'landmark_entity.freezed.dart'; + +@freezed +class LandMarkEntity with _$LandMarkEntity { + const factory LandMarkEntity({String? title, LocationEntity? location}) = + _LandMarkEntity; +} diff --git a/lib/Bloc/domain/entities/landmark/landmark_entity.freezed.dart b/lib/Bloc/domain/entities/landmark/landmark_entity.freezed.dart new file mode 100644 index 0000000..3cbebc0 --- /dev/null +++ b/lib/Bloc/domain/entities/landmark/landmark_entity.freezed.dart @@ -0,0 +1,169 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'landmark_entity.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +/// @nodoc +mixin _$LandMarkEntity { + String? get title => throw _privateConstructorUsedError; + LocationEntity? get location => throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $LandMarkEntityCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $LandMarkEntityCopyWith<$Res> { + factory $LandMarkEntityCopyWith( + LandMarkEntity value, $Res Function(LandMarkEntity) then) = + _$LandMarkEntityCopyWithImpl<$Res, LandMarkEntity>; + @useResult + $Res call({String? title, LocationEntity? location}); + + $LocationEntityCopyWith<$Res>? get location; +} + +/// @nodoc +class _$LandMarkEntityCopyWithImpl<$Res, $Val extends LandMarkEntity> + implements $LandMarkEntityCopyWith<$Res> { + _$LandMarkEntityCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? title = freezed, + Object? location = freezed, + }) { + return _then(_value.copyWith( + title: freezed == title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String?, + location: freezed == location + ? _value.location + : location // ignore: cast_nullable_to_non_nullable + as LocationEntity?, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $LocationEntityCopyWith<$Res>? get location { + if (_value.location == null) { + return null; + } + + return $LocationEntityCopyWith<$Res>(_value.location!, (value) { + return _then(_value.copyWith(location: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$LandMarkEntityImplCopyWith<$Res> + implements $LandMarkEntityCopyWith<$Res> { + factory _$$LandMarkEntityImplCopyWith(_$LandMarkEntityImpl value, + $Res Function(_$LandMarkEntityImpl) then) = + __$$LandMarkEntityImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({String? title, LocationEntity? location}); + + @override + $LocationEntityCopyWith<$Res>? get location; +} + +/// @nodoc +class __$$LandMarkEntityImplCopyWithImpl<$Res> + extends _$LandMarkEntityCopyWithImpl<$Res, _$LandMarkEntityImpl> + implements _$$LandMarkEntityImplCopyWith<$Res> { + __$$LandMarkEntityImplCopyWithImpl( + _$LandMarkEntityImpl _value, $Res Function(_$LandMarkEntityImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? title = freezed, + Object? location = freezed, + }) { + return _then(_$LandMarkEntityImpl( + title: freezed == title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String?, + location: freezed == location + ? _value.location + : location // ignore: cast_nullable_to_non_nullable + as LocationEntity?, + )); + } +} + +/// @nodoc + +class _$LandMarkEntityImpl implements _LandMarkEntity { + const _$LandMarkEntityImpl({this.title, this.location}); + + @override + final String? title; + @override + final LocationEntity? location; + + @override + String toString() { + return 'LandMarkEntity(title: $title, location: $location)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$LandMarkEntityImpl && + (identical(other.title, title) || other.title == title) && + (identical(other.location, location) || + other.location == location)); + } + + @override + int get hashCode => Object.hash(runtimeType, title, location); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$LandMarkEntityImplCopyWith<_$LandMarkEntityImpl> get copyWith => + __$$LandMarkEntityImplCopyWithImpl<_$LandMarkEntityImpl>( + this, _$identity); +} + +abstract class _LandMarkEntity implements LandMarkEntity { + const factory _LandMarkEntity( + {final String? title, + final LocationEntity? location}) = _$LandMarkEntityImpl; + + @override + String? get title; + @override + LocationEntity? get location; + @override + @JsonKey(ignore: true) + _$$LandMarkEntityImplCopyWith<_$LandMarkEntityImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/Bloc/domain/entities/location/location_entity.dart b/lib/Bloc/domain/entities/location/location_entity.dart new file mode 100644 index 0000000..6f7448a --- /dev/null +++ b/lib/Bloc/domain/entities/location/location_entity.dart @@ -0,0 +1,7 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +part 'location_entity.freezed.dart'; + +@freezed +class LocationEntity with _$LocationEntity { + factory LocationEntity({String? lat, String? lon}) = _LocationEntity; +} diff --git a/lib/Bloc/domain/entities/location/location_entity.freezed.dart b/lib/Bloc/domain/entities/location/location_entity.freezed.dart new file mode 100644 index 0000000..01117b3 --- /dev/null +++ b/lib/Bloc/domain/entities/location/location_entity.freezed.dart @@ -0,0 +1,150 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'location_entity.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +/// @nodoc +mixin _$LocationEntity { + String? get lat => throw _privateConstructorUsedError; + String? get lon => throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $LocationEntityCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $LocationEntityCopyWith<$Res> { + factory $LocationEntityCopyWith( + LocationEntity value, $Res Function(LocationEntity) then) = + _$LocationEntityCopyWithImpl<$Res, LocationEntity>; + @useResult + $Res call({String? lat, String? lon}); +} + +/// @nodoc +class _$LocationEntityCopyWithImpl<$Res, $Val extends LocationEntity> + implements $LocationEntityCopyWith<$Res> { + _$LocationEntityCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? lat = freezed, + Object? lon = freezed, + }) { + return _then(_value.copyWith( + lat: freezed == lat + ? _value.lat + : lat // ignore: cast_nullable_to_non_nullable + as String?, + lon: freezed == lon + ? _value.lon + : lon // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$LocationEntityImplCopyWith<$Res> + implements $LocationEntityCopyWith<$Res> { + factory _$$LocationEntityImplCopyWith(_$LocationEntityImpl value, + $Res Function(_$LocationEntityImpl) then) = + __$$LocationEntityImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({String? lat, String? lon}); +} + +/// @nodoc +class __$$LocationEntityImplCopyWithImpl<$Res> + extends _$LocationEntityCopyWithImpl<$Res, _$LocationEntityImpl> + implements _$$LocationEntityImplCopyWith<$Res> { + __$$LocationEntityImplCopyWithImpl( + _$LocationEntityImpl _value, $Res Function(_$LocationEntityImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? lat = freezed, + Object? lon = freezed, + }) { + return _then(_$LocationEntityImpl( + lat: freezed == lat + ? _value.lat + : lat // ignore: cast_nullable_to_non_nullable + as String?, + lon: freezed == lon + ? _value.lon + : lon // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc + +class _$LocationEntityImpl implements _LocationEntity { + _$LocationEntityImpl({this.lat, this.lon}); + + @override + final String? lat; + @override + final String? lon; + + @override + String toString() { + return 'LocationEntity(lat: $lat, lon: $lon)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$LocationEntityImpl && + (identical(other.lat, lat) || other.lat == lat) && + (identical(other.lon, lon) || other.lon == lon)); + } + + @override + int get hashCode => Object.hash(runtimeType, lat, lon); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$LocationEntityImplCopyWith<_$LocationEntityImpl> get copyWith => + __$$LocationEntityImplCopyWithImpl<_$LocationEntityImpl>( + this, _$identity); +} + +abstract class _LocationEntity implements LocationEntity { + factory _LocationEntity({final String? lat, final String? lon}) = + _$LocationEntityImpl; + + @override + String? get lat; + @override + String? get lon; + @override + @JsonKey(ignore: true) + _$$LocationEntityImplCopyWith<_$LocationEntityImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/Bloc/domain/entities/user/user_entity.dart b/lib/Bloc/domain/entities/user/user_entity.dart new file mode 100644 index 0000000..d82447f --- /dev/null +++ b/lib/Bloc/domain/entities/user/user_entity.dart @@ -0,0 +1,19 @@ +import 'package:beacon/Bloc/domain/entities/beacon/beacon_entity.dart'; +import 'package:beacon/Bloc/domain/entities/group/group_entity.dart'; +import 'package:beacon/Bloc/domain/entities/location/location_entity.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'user_entity.freezed.dart'; + +@freezed +class UserEntity with _$UserEntity { + const factory UserEntity( + {String? id, + List? groups, + List? beacons, + String? authToken, + String? email, + bool? isGuest, + String? name, + LocationEntity? location}) = _UserEntity; +} diff --git a/lib/Bloc/domain/entities/user/user_entity.freezed.dart b/lib/Bloc/domain/entities/user/user_entity.freezed.dart new file mode 100644 index 0000000..6fbc309 --- /dev/null +++ b/lib/Bloc/domain/entities/user/user_entity.freezed.dart @@ -0,0 +1,322 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'user_entity.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +/// @nodoc +mixin _$UserEntity { + String? get id => throw _privateConstructorUsedError; + List? get groups => throw _privateConstructorUsedError; + List? get beacons => throw _privateConstructorUsedError; + String? get authToken => throw _privateConstructorUsedError; + String? get email => throw _privateConstructorUsedError; + bool? get isGuest => throw _privateConstructorUsedError; + String? get name => throw _privateConstructorUsedError; + LocationEntity? get location => throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $UserEntityCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $UserEntityCopyWith<$Res> { + factory $UserEntityCopyWith( + UserEntity value, $Res Function(UserEntity) then) = + _$UserEntityCopyWithImpl<$Res, UserEntity>; + @useResult + $Res call( + {String? id, + List? groups, + List? beacons, + String? authToken, + String? email, + bool? isGuest, + String? name, + LocationEntity? location}); + + $LocationEntityCopyWith<$Res>? get location; +} + +/// @nodoc +class _$UserEntityCopyWithImpl<$Res, $Val extends UserEntity> + implements $UserEntityCopyWith<$Res> { + _$UserEntityCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = freezed, + Object? groups = freezed, + Object? beacons = freezed, + Object? authToken = freezed, + Object? email = freezed, + Object? isGuest = freezed, + Object? name = freezed, + Object? location = freezed, + }) { + return _then(_value.copyWith( + id: freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + groups: freezed == groups + ? _value.groups + : groups // ignore: cast_nullable_to_non_nullable + as List?, + beacons: freezed == beacons + ? _value.beacons + : beacons // ignore: cast_nullable_to_non_nullable + as List?, + authToken: freezed == authToken + ? _value.authToken + : authToken // ignore: cast_nullable_to_non_nullable + as String?, + email: freezed == email + ? _value.email + : email // ignore: cast_nullable_to_non_nullable + as String?, + isGuest: freezed == isGuest + ? _value.isGuest + : isGuest // ignore: cast_nullable_to_non_nullable + as bool?, + name: freezed == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String?, + location: freezed == location + ? _value.location + : location // ignore: cast_nullable_to_non_nullable + as LocationEntity?, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $LocationEntityCopyWith<$Res>? get location { + if (_value.location == null) { + return null; + } + + return $LocationEntityCopyWith<$Res>(_value.location!, (value) { + return _then(_value.copyWith(location: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$UserEntityImplCopyWith<$Res> + implements $UserEntityCopyWith<$Res> { + factory _$$UserEntityImplCopyWith( + _$UserEntityImpl value, $Res Function(_$UserEntityImpl) then) = + __$$UserEntityImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String? id, + List? groups, + List? beacons, + String? authToken, + String? email, + bool? isGuest, + String? name, + LocationEntity? location}); + + @override + $LocationEntityCopyWith<$Res>? get location; +} + +/// @nodoc +class __$$UserEntityImplCopyWithImpl<$Res> + extends _$UserEntityCopyWithImpl<$Res, _$UserEntityImpl> + implements _$$UserEntityImplCopyWith<$Res> { + __$$UserEntityImplCopyWithImpl( + _$UserEntityImpl _value, $Res Function(_$UserEntityImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = freezed, + Object? groups = freezed, + Object? beacons = freezed, + Object? authToken = freezed, + Object? email = freezed, + Object? isGuest = freezed, + Object? name = freezed, + Object? location = freezed, + }) { + return _then(_$UserEntityImpl( + id: freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + groups: freezed == groups + ? _value._groups + : groups // ignore: cast_nullable_to_non_nullable + as List?, + beacons: freezed == beacons + ? _value._beacons + : beacons // ignore: cast_nullable_to_non_nullable + as List?, + authToken: freezed == authToken + ? _value.authToken + : authToken // ignore: cast_nullable_to_non_nullable + as String?, + email: freezed == email + ? _value.email + : email // ignore: cast_nullable_to_non_nullable + as String?, + isGuest: freezed == isGuest + ? _value.isGuest + : isGuest // ignore: cast_nullable_to_non_nullable + as bool?, + name: freezed == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String?, + location: freezed == location + ? _value.location + : location // ignore: cast_nullable_to_non_nullable + as LocationEntity?, + )); + } +} + +/// @nodoc + +class _$UserEntityImpl implements _UserEntity { + const _$UserEntityImpl( + {this.id, + final List? groups, + final List? beacons, + this.authToken, + this.email, + this.isGuest, + this.name, + this.location}) + : _groups = groups, + _beacons = beacons; + + @override + final String? id; + final List? _groups; + @override + List? get groups { + final value = _groups; + if (value == null) return null; + if (_groups is EqualUnmodifiableListView) return _groups; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + final List? _beacons; + @override + List? get beacons { + final value = _beacons; + if (value == null) return null; + if (_beacons is EqualUnmodifiableListView) return _beacons; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + @override + final String? authToken; + @override + final String? email; + @override + final bool? isGuest; + @override + final String? name; + @override + final LocationEntity? location; + + @override + String toString() { + return 'UserEntity(id: $id, groups: $groups, beacons: $beacons, authToken: $authToken, email: $email, isGuest: $isGuest, name: $name, location: $location)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$UserEntityImpl && + (identical(other.id, id) || other.id == id) && + const DeepCollectionEquality().equals(other._groups, _groups) && + const DeepCollectionEquality().equals(other._beacons, _beacons) && + (identical(other.authToken, authToken) || + other.authToken == authToken) && + (identical(other.email, email) || other.email == email) && + (identical(other.isGuest, isGuest) || other.isGuest == isGuest) && + (identical(other.name, name) || other.name == name) && + (identical(other.location, location) || + other.location == location)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + id, + const DeepCollectionEquality().hash(_groups), + const DeepCollectionEquality().hash(_beacons), + authToken, + email, + isGuest, + name, + location); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$UserEntityImplCopyWith<_$UserEntityImpl> get copyWith => + __$$UserEntityImplCopyWithImpl<_$UserEntityImpl>(this, _$identity); +} + +abstract class _UserEntity implements UserEntity { + const factory _UserEntity( + {final String? id, + final List? groups, + final List? beacons, + final String? authToken, + final String? email, + final bool? isGuest, + final String? name, + final LocationEntity? location}) = _$UserEntityImpl; + + @override + String? get id; + @override + List? get groups; + @override + List? get beacons; + @override + String? get authToken; + @override + String? get email; + @override + bool? get isGuest; + @override + String? get name; + @override + LocationEntity? get location; + @override + @JsonKey(ignore: true) + _$$UserEntityImplCopyWith<_$UserEntityImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/Bloc/domain/repositories/auth_repository.dart b/lib/Bloc/domain/repositories/auth_repository.dart new file mode 100644 index 0000000..87e687e --- /dev/null +++ b/lib/Bloc/domain/repositories/auth_repository.dart @@ -0,0 +1,14 @@ +import 'package:beacon/Bloc/core/resources/data_state.dart'; +import 'package:beacon/Bloc/domain/entities/user/user_entity.dart'; + +abstract class AuthRepository { + // userinfo function + Future> getUser(); + + // Signup function + Future> register( + String name, String email, String password); + + // Login function + Future> login(String email, String password); +} diff --git a/lib/Bloc/domain/repositories/group_repository.dart b/lib/Bloc/domain/repositories/group_repository.dart new file mode 100644 index 0000000..36bb561 --- /dev/null +++ b/lib/Bloc/domain/repositories/group_repository.dart @@ -0,0 +1,12 @@ +import 'package:beacon/Bloc/core/resources/data_state.dart'; +import 'package:beacon/Bloc/domain/entities/beacon/beacon_entity.dart'; + +abstract class GroupRepository { + Future> createHike(String title, int startsAt, + int expiresAt, String lat, String lon, String groupID); + + Future> joinHike(String hikeId); + + Future>> fetchHikes( + String groupID, int page, int pageSize); +} diff --git a/lib/Bloc/domain/repositories/home_repository.dart b/lib/Bloc/domain/repositories/home_repository.dart new file mode 100644 index 0000000..23a7dff --- /dev/null +++ b/lib/Bloc/domain/repositories/home_repository.dart @@ -0,0 +1,8 @@ +import 'package:beacon/Bloc/core/resources/data_state.dart'; +import 'package:beacon/Bloc/domain/entities/group/group_entity.dart'; + +abstract class HomeRepository { + Future>> fetchGroups(int page, int pageSize); + Future> createGroup(String title); + Future> joinGroup(String shortCode); +} diff --git a/lib/Bloc/domain/usecase/auth_usecase.dart b/lib/Bloc/domain/usecase/auth_usecase.dart new file mode 100644 index 0000000..3ce6307 --- /dev/null +++ b/lib/Bloc/domain/usecase/auth_usecase.dart @@ -0,0 +1,23 @@ +import 'package:beacon/Bloc/core/resources/data_state.dart'; +import 'package:beacon/Bloc/domain/entities/user/user_entity.dart'; +import 'package:beacon/Bloc/domain/repositories/auth_repository.dart'; + +class AuthUseCase { + final AuthRepository authRepository; + + AuthUseCase({required this.authRepository}); + + Future> registerUseCase( + String name, String email, String password) async { + return await authRepository.register(name, email, password); + } + + Future> loginUserCase( + String email, String password) async { + return await authRepository.login(email, password); + } + + Future> getUserInfoUseCase() async { + return await authRepository.getUser(); + } +} diff --git a/lib/Bloc/domain/usecase/group_usecase.dart b/lib/Bloc/domain/usecase/group_usecase.dart new file mode 100644 index 0000000..6122cae --- /dev/null +++ b/lib/Bloc/domain/usecase/group_usecase.dart @@ -0,0 +1,23 @@ +import 'package:beacon/Bloc/core/resources/data_state.dart'; +import 'package:beacon/Bloc/domain/entities/beacon/beacon_entity.dart'; +import 'package:beacon/Bloc/domain/repositories/group_repository.dart'; + +class GroupUseCase { + final GroupRepository _groupRepo; + + GroupUseCase(this._groupRepo); + + Future>> fetchHikes( + String groupID, int page, int pageSize) { + return _groupRepo.fetchHikes(groupID, page, pageSize); + } + + Future> joinHike(String shortcode) { + return _groupRepo.joinHike(shortcode); + } + + Future> createHike(String title, int startsAt, + int expiresAt, String lat, String lon, String groupID) { + return _groupRepo.createHike(title, startsAt, expiresAt, lat, lon, groupID); + } +} diff --git a/lib/Bloc/domain/usecase/home_usecase.dart b/lib/Bloc/domain/usecase/home_usecase.dart new file mode 100644 index 0000000..995f781 --- /dev/null +++ b/lib/Bloc/domain/usecase/home_usecase.dart @@ -0,0 +1,21 @@ +import 'package:beacon/Bloc/core/resources/data_state.dart'; +import 'package:beacon/Bloc/domain/entities/group/group_entity.dart'; +import 'package:beacon/Bloc/domain/repositories/home_repository.dart'; + +class HomeUseCase { + final HomeRepository homeRepository; + + HomeUseCase({required this.homeRepository}); + + Future>> groups(int page, int pageSize) { + return homeRepository.fetchGroups(page, pageSize); + } + + Future> createGroup(String title) { + return homeRepository.createGroup(title); + } + + Future> joinGroup(String shortCode) { + return homeRepository.joinGroup(shortCode); + } +} diff --git a/lib/Bloc/presentation/cubit/auth_cubit.dart b/lib/Bloc/presentation/cubit/auth_cubit.dart new file mode 100644 index 0000000..0daf56f --- /dev/null +++ b/lib/Bloc/presentation/cubit/auth_cubit.dart @@ -0,0 +1,84 @@ +import 'package:beacon/Bloc/core/resources/data_state.dart'; +import 'package:beacon/Bloc/domain/entities/user/user_entity.dart'; +import 'package:beacon/Bloc/domain/usecase/auth_usecase.dart'; +import 'package:beacon/locator.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +abstract class AuthState { + final UserEntity? user; + final String? message; + final String? error; + + AuthState({this.error, this.message, this.user}); +} + +class IconToggled extends AuthState { + final bool isIconChecked; + + IconToggled(this.isIconChecked); +} + +class InitialAuthState extends AuthState {} + +class AuthLoadingState extends AuthState {} + +class AuthErrorState extends AuthState { + final String? error; + AuthErrorState({required this.error}); +} + +class SuccessState extends AuthState { + final String? message; + final UserEntity? user; + SuccessState({this.message, this.user}); +} + +class AuthCubit extends Cubit { + final AuthUseCase authUseCase; + AuthCubit({required this.authUseCase}) : super(InitialAuthState()); + + Future register( + String name, + String email, + String password, + ) async { + emit(AuthLoadingState()); + final state = await authUseCase.registerUseCase(name, email, password); + if (state is DataFailed) { + emit(AuthErrorState(error: state.error)); + } else { + emit(SuccessState(user: state.data)); + } + } + + Future login(String email, String password) async { + emit(AuthLoadingState()); + final state = await authUseCase.loginUserCase(email, password); + if (state is DataFailed) { + emit(AuthErrorState(error: state.error)); + } else { + emit(SuccessState(user: state.data)); + } + } + + Future fetchUserInfo() async { + final userInfo = await authUseCase.getUserInfoUseCase(); + + if (userInfo is DataFailed) { + emit(AuthErrorState(error: userInfo.error!)); + } else { + emit(SuccessState(user: userInfo.data)); + } + } + + requestFocus(FocusNode focusNode, BuildContext context) { + FocusScope.of(context).requestFocus(focusNode); + } + + Future isGuest() async { + bool? isguest = await localApi.userModel.isGuest; + + return isguest!; + } +} diff --git a/lib/Bloc/presentation/cubit/group_cubit.dart b/lib/Bloc/presentation/cubit/group_cubit.dart new file mode 100644 index 0000000..34c8a94 --- /dev/null +++ b/lib/Bloc/presentation/cubit/group_cubit.dart @@ -0,0 +1,106 @@ +import 'package:beacon/Bloc/core/constants/location.dart'; +import 'package:beacon/Bloc/core/resources/data_state.dart'; +import 'package:beacon/Bloc/domain/entities/beacon/beacon_entity.dart'; +import 'package:beacon/Bloc/domain/usecase/group_usecase.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:geolocator/geolocator.dart'; + +abstract class GroupState {} + +class ShrimmerGroupState extends GroupState {} + +class GroupLoadingState extends GroupState {} + +class GroupErrorState extends GroupState { + final String error; + + GroupErrorState({required this.error}); +} + +class GroupReloadState extends GroupState { + List? beacons; + BeaconEntity? beacon; + + GroupReloadState({this.beacon, this.beacons}); +} + +class GroupCubit extends Cubit { + final GroupUseCase _groupUseCase; + GroupCubit(this._groupUseCase) : super(ShrimmerGroupState()); + + int page = 1; + int pageSize = 4; + bool isLoadingMore = false; + bool isCompletelyFetched = false; + List _beacons = []; + List get beacons => _beacons; + Position? position; + + Future createHike(String title, int startsAt, int expiresAt, String lat, + String lon, String groupID) async { + emit(GroupLoadingState()); + final state = await _groupUseCase.createHike( + title, startsAt, expiresAt, lat, lon, groupID); + + if (state is DataFailed) { + emit(GroupErrorState(error: state.error!)); + } else { + BeaconEntity? newHike = state.data; + newHike != null ? _beacons.insert(0, newHike) : null; + emit(GroupReloadState()); + } + } + + Future joinHike(String shortcode) async { + emit(GroupLoadingState()); + final state = await _groupUseCase.joinHike(shortcode); + if (state is DataFailed) { + emit(GroupErrorState(error: state.error!)); + } else { + BeaconEntity? newHike = state.data; + newHike != null ? _beacons.insert(0, newHike) : null; + emit(GroupReloadState()); + } + } + + Future fetchGroupHikes(String groupID) async { + if (isLoadingMore == true) return; + + if (page == 1) { + emit(ShrimmerGroupState()); + } + + isLoadingMore = true; + final state = await _groupUseCase.fetchHikes(groupID, page, pageSize); + + if (state is DataFailed) { + isLoadingMore = false; + emit(GroupErrorState(error: state.error!)); + } else { + final hikes = state.data ?? []; + isLoadingMore = false; + page++; + if (hikes.isEmpty) { + isCompletelyFetched = true; + emit(GroupReloadState()); + return; + } + for (var hike in hikes) { + _beacons.add(hike); + } + emit(GroupReloadState()); + } + } + + Future fetchPosition() async { + position = await LocationService.getCurrentLocation(); + } + + clear() { + page = 1; + pageSize = 4; + _beacons.clear(); + isCompletelyFetched = false; + emit(ShrimmerGroupState()); + } +} diff --git a/lib/Bloc/presentation/cubit/home_cubit.dart b/lib/Bloc/presentation/cubit/home_cubit.dart new file mode 100644 index 0000000..9202258 --- /dev/null +++ b/lib/Bloc/presentation/cubit/home_cubit.dart @@ -0,0 +1,102 @@ +import 'dart:developer'; +import 'package:beacon/Bloc/core/resources/data_state.dart'; +import 'package:beacon/Bloc/domain/entities/group/group_entity.dart'; +import 'package:beacon/Bloc/domain/usecase/home_usecase.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +abstract class HomeState {} + +class InitialHomeState extends HomeState {} + +class ReloadState extends HomeState {} + +class ShrimmerState extends HomeState {} + +class LoadingMoreGroups extends HomeState {} + +class NewGroupLoadingState extends HomeState {} + +class ErrorHomeState extends HomeState { + String error; + ErrorHomeState({required this.error}); +} + +class HomeCubit extends Cubit { + final HomeUseCase homeUseCase; + HomeCubit({required this.homeUseCase}) : super(InitialHomeState()); + + int page = 1; + int pageSize = 4; + bool isLoadingMore = false; + bool isCompletelyFetched = false; + List _totalGroups = []; + List get totalGroups => _totalGroups; + + Future createGroup(String title) async { + emit(NewGroupLoadingState()); + final dataState = await homeUseCase.createGroup(title); + if (dataState is DataFailed) { + emit(ErrorHomeState(error: dataState.error!)); + } else { + GroupEntity? group = dataState.data; + group != null ? _totalGroups.insert(0, group) : null; + emit(ReloadState()); + } + } + + Future joinGroup(String shortCode) async { + emit(NewGroupLoadingState()); + DataState state = await homeUseCase.joinGroup(shortCode); + + if (state is DataFailed) { + emit(ErrorHomeState(error: state.error!)); + } else { + GroupEntity? group = state.data; + group != null ? _totalGroups.insert(0, group) : null; + emit(ReloadState()); + } + } + + Future fetchUserGroups() async { + // if already loading then return + if (isCompletelyFetched == true || isLoadingMore == true) return; + + if (page != 1) log('fetching next set of groups'); + + if (page == 1) { + emit(ShrimmerState()); + } else { + isLoadingMore = true; + emit(LoadingMoreGroups()); + } + + DataState> state = + await homeUseCase.groups(page, pageSize); + + if (state is DataFailed) { + isLoadingMore = false; + emit(ErrorHomeState(error: state.error!)); + } else { + List newGroups = state.data ?? []; + if (newGroups.isEmpty) { + isCompletelyFetched = true; + emit(ReloadState()); + return; + } + for (var newGroup in newGroups) { + _totalGroups.add(newGroup); + } + page++; + isLoadingMore = false; + emit(ReloadState()); + } + } + + clear() { + page = 1; + isLoadingMore = false; + isCompletelyFetched = false; + _totalGroups.clear(); + emit(InitialHomeState()); + } +} diff --git a/lib/old/components/views/auth_screen.dart b/lib/Bloc/presentation/screens/auth_screen.dart similarity index 50% rename from lib/old/components/views/auth_screen.dart rename to lib/Bloc/presentation/screens/auth_screen.dart index b91fe75..7a16a16 100644 --- a/lib/old/components/views/auth_screen.dart +++ b/lib/Bloc/presentation/screens/auth_screen.dart @@ -1,17 +1,17 @@ import 'package:auto_route/auto_route.dart'; +import 'package:beacon/Bloc/presentation/cubit/auth_cubit.dart'; +import 'package:beacon/Bloc/presentation/widgets/text_field.dart'; +import 'package:beacon/locator.dart'; import 'package:beacon/old/components/hike_button.dart'; +import 'package:beacon/old/components/loading_screen.dart'; import 'package:beacon/old/components/shape_painter.dart'; -import 'package:beacon/old/components/services/validators.dart'; +import 'package:beacon/Bloc/core/utils/validators.dart'; import 'package:beacon/old/components/utilities/constants.dart'; import 'package:beacon/old/components/utilities/indication_painter.dart'; -import 'package:beacon/old/components/view_model/auth_screen_model.dart'; -import 'package:beacon/old/components/views/base_view.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:sizer/sizer.dart'; -import '../loading_screen.dart'; - @RoutePage() class AuthScreen extends StatefulWidget { const AuthScreen({super.key}); @@ -29,7 +29,6 @@ class _AuthScreenState extends State shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10.0), ), - // actionsAlignment: MainAxisAlignment.spaceEvenly, contentPadding: EdgeInsets.all(25.0), title: Text( 'Confirm Exit', @@ -43,14 +42,13 @@ class _AuthScreenState extends State HikeButton( buttonHeight: 2.5.h, buttonWidth: 8.w, - onTap: () => Navigator.of(context).pop(false), + onTap: () => AutoRouter.of(context).maybePop(false), text: 'No', ), HikeButton( buttonHeight: 2.5.h, buttonWidth: 8.w, - onTap: () => - SystemChannels.platform.invokeMethod('SystemNavigator.pop'), + onTap: () => AutoRouter.of(context).maybePop(true), text: 'Yes', ), ], @@ -58,19 +56,34 @@ class _AuthScreenState extends State ); } + PageController _pageController = PageController(); + + Color leftColor = Colors.white; + Color rightColor = Colors.black; + @override Widget build(BuildContext context) { Size screensize = MediaQuery.of(context).size; + final authCubit = BlocProvider.of(context); return // WillPopScope( // onWillPop: _onPopHome ?? , // child: - BaseView( - builder: (context, model, child) { - return (model.isBusy) + BlocConsumer( + listener: (context, state) { + if (state is SuccessState) { + AutoRouter.of(context).replaceNamed('/home'); + utils.showSnackBar('Login successful !', context, + duration: Duration(seconds: 2)); + } else if (state is AuthErrorState) { + utils.showSnackBar(state.error!, context, + duration: Duration(seconds: 2)); + } + }, + builder: (context, state) { + return state is AuthLoadingState ? LoadingScreen() - : new Scaffold( - key: model.scaffoldKey, + : Scaffold( resizeToAvoidBottomInset: true, body: Container( width: screensize.width, @@ -90,43 +103,45 @@ class _AuthScreenState extends State children: [ Padding( padding: EdgeInsets.only(top: 20.0), - child: _buildMenuBar(context, model), + child: _buildMenuBar(context, _pageController), ), Expanded( flex: 2, child: PageView( - controller: model.pageController, + controller: _pageController, onPageChanged: (i) { if (i == 0) { setState(() { - model.right = Colors.black; - model.left = Colors.white; + rightColor = Colors.black; + leftColor = Colors.white; }); Future.delayed(Duration(milliseconds: 500), () { - model.requestFocusForFocusNode( - model.emailLogin); + authCubit.requestFocus( + loginEmailFocus, context); }); } else if (i == 1) { setState(() { - model.right = Colors.white; - model.left = Colors.black; + rightColor = Colors.white; + leftColor = Colors.black; }); Future.delayed(Duration(milliseconds: 500), () { - model - .requestFocusForFocusNode(model.name); + authCubit.requestFocus( + signUpNameFocus, context); }); } }, children: [ new ConstrainedBox( constraints: const BoxConstraints.expand(), - child: _buildSignIn(context, model), + child: _buildSignIn(context), ), new ConstrainedBox( constraints: const BoxConstraints.expand(), - child: _buildSignUp(context, model), + child: _buildSignUp( + context, + ), ), ], ), @@ -139,11 +154,10 @@ class _AuthScreenState extends State ), ); }, - // ), ); } - Widget _buildMenuBar(BuildContext context, AuthViewModel model) { + Widget _buildMenuBar(BuildContext context, PageController pageController) { Size screensize = MediaQuery.of(context).size; return Container( padding: EdgeInsets.symmetric(horizontal: 13.5.w), @@ -153,7 +167,7 @@ class _AuthScreenState extends State borderRadius: BorderRadius.all(Radius.circular(25.0)), ), child: CustomPaint( - painter: TabIndicationPainter(pageController: model.pageController), + painter: TabIndicationPainter(pageController: pageController), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ @@ -163,11 +177,18 @@ class _AuthScreenState extends State overlayColor: MaterialStateProperty.all(Colors.transparent), ), //highlightColor: Colors.white, - onPressed: model.onSignInButtonPress, + onPressed: () { + pageController.animateToPage(0, + duration: Duration(milliseconds: 500), + curve: Curves.decelerate); + leftColor = Colors.white; + rightColor = Colors.black; + setState(() {}); + }, child: Text( "Existing", style: TextStyle( - color: model.left, + color: leftColor, fontSize: 18.0, ), ), @@ -178,11 +199,18 @@ class _AuthScreenState extends State style: ButtonStyle( overlayColor: MaterialStateProperty.all(Colors.transparent), ), - onPressed: model.onSignUpButtonPress, + onPressed: () { + pageController.animateToPage(1, + duration: Duration(milliseconds: 500), + curve: Curves.decelerate); + rightColor = Colors.white; + leftColor = Colors.black; + setState(() {}); + }, child: Text( "New", style: TextStyle( - color: model.right, + color: rightColor, fontSize: 18.0, ), ), @@ -194,8 +222,25 @@ class _AuthScreenState extends State ); } - Widget _buildSignIn(BuildContext context, AuthViewModel model) { + GlobalKey _signInFormKey = GlobalKey(); + GlobalKey _registerFormKey = GlobalKey(); + final loginEmailController = TextEditingController(); + final loginPasswordController = TextEditingController(); + + final signUpNameController = TextEditingController(); + final signUpEmailController = TextEditingController(); + final signUpPasswordController = TextEditingController(); + + final signUpNameFocus = FocusNode(); + final signUpEmailFocus = FocusNode(); + final signUpPasswordFocus = FocusNode(); + + final loginEmailFocus = FocusNode(); + final loginPasswordFocus = FocusNode(); + Widget _buildSignIn(BuildContext context) { Size screensize = MediaQuery.of(context).size; + + final authCubit = BlocProvider.of(context); return SingleChildScrollView( child: Container( padding: EdgeInsets.only(top: 3.h, left: 8.5.w, right: 8.5.w), @@ -210,8 +255,7 @@ class _AuthScreenState extends State borderRadius: BorderRadius.circular(8.0), ), child: Form( - key: model.formKeyLogin, - autovalidateMode: model.loginValidate, + key: _signInFormKey, child: Container( width: screensize.width - 70, child: Column( @@ -220,25 +264,13 @@ class _AuthScreenState extends State height: 13.h, padding: EdgeInsets.symmetric( horizontal: 10, vertical: 10.0), - child: TextFormField( - autovalidateMode: model.loginValidate, - focusNode: model.emailLogin, - controller: model.loginEmailController, - validator: (value) => - Validator.validateEmail(value!.trimRight()), - keyboardType: TextInputType.emailAddress, - style: TextStyle(fontSize: 16.0, color: Colors.black), - decoration: InputDecoration( - border: InputBorder.none, - icon: Icon( - Icons.mail_outline, - color: Colors.black, - size: 24.0, - ), - hintText: "Email Address", - hintStyle: TextStyle( - fontSize: hintsize - 2, color: hintColor), - ), + child: CustomTextField( + iconData: Icons.email, + hintText: 'Email Address', + controller: loginEmailController, + focusNode: loginEmailFocus, + nextFocusNode: loginPasswordFocus, + validator: Validator.validateEmail, ), ), separator(), @@ -246,36 +278,13 @@ class _AuthScreenState extends State height: 13.h, padding: EdgeInsets.symmetric( horizontal: 10, vertical: 10.0), - child: TextFormField( - autovalidateMode: model.loginValidate, - focusNode: model.passwordLogin, - controller: model.loginPasswordController, - obscureText: model.obscureTextLogin, - validator: (value) => - Validator.validatePassword(value!), - style: TextStyle(fontSize: 16.0, color: Colors.black), - decoration: InputDecoration( - border: InputBorder.none, - icon: Icon( - Icons.lock, - size: 24.0, - color: Colors.black, - ), - hintText: "Password", - hintStyle: TextStyle( - fontSize: hintsize - 2, color: hintColor), - suffixIcon: IconButton( - onPressed: () => model.displayPasswordLogin(), - icon: Icon( - model.obscureTextLogin - ? Icons.visibility - : Icons.visibility_off, - size: 20.0, - color: Colors.black, - ), - ), - ), - ), + child: CustomTextField( + iconData: Icons.lock, + hintText: 'Password', + controller: loginPasswordController, + focusNode: loginPasswordFocus, + showTrailing: true, + validator: Validator.validatePassword), ), ], ), @@ -285,9 +294,23 @@ class _AuthScreenState extends State SizedBox( height: 3.5.h, ), - HikeButton( - onTap: model.nextLogin, - text: 'LOGIN', + BlocBuilder( + builder: (context, state) { + return HikeButton( + onTap: () { + if (_signInFormKey.currentState!.validate()) { + authCubit.login( + loginEmailController.text.trim(), + loginPasswordController.text.trim(), + ); + } else { + utils.showSnackBar( + 'Please complete all the fields', context); + } + }, + text: 'LOGIN', + ); + }, ), Padding( padding: EdgeInsets.only( @@ -301,7 +324,12 @@ class _AuthScreenState extends State ), ), HikeButton( - onTap: () => model.loginAsGuest(), + onTap: () { + authCubit.login( + loginEmailController.text.trim(), + loginPasswordController.text.trim(), + ); + }, text: 'LOGIN AS GUEST', ), ], @@ -310,7 +338,8 @@ class _AuthScreenState extends State ); } - Widget _buildSignUp(BuildContext context, AuthViewModel model) { + Widget _buildSignUp(BuildContext context) { + final authCubit = BlocProvider.of(context); Size screensize = MediaQuery.of(context).size; return SingleChildScrollView( child: Container( @@ -324,8 +353,7 @@ class _AuthScreenState extends State borderRadius: BorderRadius.circular(8.0), ), child: Form( - key: model.formKeySignup, - autovalidateMode: model.signupValidate, + key: _registerFormKey, child: Container( width: screensize.width - 70, // height: 280.0, @@ -334,26 +362,13 @@ class _AuthScreenState extends State height: 13.h, padding: EdgeInsets.symmetric( horizontal: 10, vertical: 10.0), - child: TextFormField( - autovalidateMode: model.signupValidate, - validator: (value) => Validator.validateName(value!), - focusNode: model.name, - textInputAction: TextInputAction.next, - controller: model.signupNameController, - keyboardType: TextInputType.text, - textCapitalization: TextCapitalization.words, - style: TextStyle(fontSize: 18.0, color: Colors.black), - decoration: InputDecoration( - border: InputBorder.none, - icon: Icon( - Icons.account_box, - color: Colors.black, - size: 24, - ), - hintText: "Name", - hintStyle: TextStyle( - fontSize: hintsize - 2, color: hintColor), - ), + child: CustomTextField( + iconData: Icons.person_2_sharp, + hintText: 'Name', + controller: signUpNameController, + focusNode: signUpNameFocus, + nextFocusNode: signUpEmailFocus, + validator: Validator.validateName, ), ), separator(), @@ -361,25 +376,13 @@ class _AuthScreenState extends State height: 13.h, padding: EdgeInsets.symmetric( horizontal: 10, vertical: 10.0), - child: TextFormField( - autovalidateMode: model.signupValidate, - validator: (value) => Validator.validateEmail(value!), - focusNode: model.email, - textInputAction: TextInputAction.next, - controller: model.signupEmailController, - keyboardType: TextInputType.emailAddress, - style: TextStyle(fontSize: 16.0, color: Colors.black), - decoration: InputDecoration( - border: InputBorder.none, - icon: Icon( - Icons.mail, - color: Colors.black, - size: 24, - ), - hintText: "Email Address", - hintStyle: TextStyle( - fontSize: hintsize - 2, color: hintColor), - ), + child: CustomTextField( + iconData: Icons.mail, + hintText: 'Email Address', + controller: signUpEmailController, + focusNode: signUpEmailFocus, + nextFocusNode: signUpPasswordFocus, + validator: Validator.validateEmail, ), ), separator(), @@ -387,37 +390,13 @@ class _AuthScreenState extends State height: 13.h, padding: EdgeInsets.symmetric( horizontal: 10, vertical: 10.0), - child: TextFormField( - autovalidateMode: model.signupValidate, - focusNode: model.password, - textInputAction: TextInputAction.done, - validator: (value) => - Validator.validatePassword(value!), - controller: model.signupPasswordController, - obscureText: model.obscureTextSignup, - style: TextStyle(fontSize: 16.0, color: Colors.black), - decoration: InputDecoration( - border: InputBorder.none, - icon: Icon( - Icons.lock, - color: Colors.black, - size: 24, - ), - suffixIcon: IconButton( - onPressed: () => model.displayPasswordSignup(), - icon: Icon( - model.obscureTextSignup - ? Icons.visibility - : Icons.visibility_off, - size: 20.0, - color: Colors.black, - ), - ), - hintText: "Password", - hintStyle: TextStyle( - fontSize: hintsize - 2, color: hintColor), - ), - ), + child: CustomTextField( + iconData: Icons.lock, + hintText: 'Password', + controller: signUpPasswordController, + focusNode: signUpPasswordFocus, + showTrailing: true, + validator: Validator.validatePassword), ), ])), ), @@ -426,15 +405,32 @@ class _AuthScreenState extends State height: 3.5.h, ), Container( - // margin: EdgeInsets.only(top: 300.0), - decoration: new BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(5.0)), - ), - child: HikeButton( - onTap: () => model.nextSignup(), - text: 'SIGN UP', - ), - ), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(5.0)), + ), + child: BlocBuilder( + builder: (context, state) { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(5.0)), + ), + child: HikeButton( + onTap: () { + if (_registerFormKey.currentState!.validate()) { + authCubit.register( + signUpNameController.text.trim(), + signUpEmailController.text.trim(), + signUpPasswordController.text.trim()); + } else { + utils.showSnackBar( + 'Please complete all the fields', context); + } + }, + text: 'SIGN UP', + ), + ); + }, + )) ], ), ), diff --git a/lib/Bloc/presentation/screens/group_screen.dart b/lib/Bloc/presentation/screens/group_screen.dart new file mode 100644 index 0000000..d50053c --- /dev/null +++ b/lib/Bloc/presentation/screens/group_screen.dart @@ -0,0 +1,379 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:beacon/Bloc/domain/entities/group/group_entity.dart'; +import 'package:beacon/Bloc/presentation/cubit/group_cubit.dart'; +import 'package:beacon/Bloc/presentation/widgets/create_join_dialog.dart'; +import 'package:beacon/old/components/beacon_card.dart'; +import 'package:beacon/old/components/hike_button.dart'; +import 'package:beacon/old/components/loading_screen.dart'; +import 'package:beacon/old/components/shape_painter.dart'; +import 'package:beacon/locator.dart'; +import 'package:beacon/old/components/models/beacon/beacon.dart'; +import 'package:beacon/old/components/utilities/constants.dart'; +import 'package:beacon/router.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; +import 'package:sizer/sizer.dart'; + +@RoutePage() +class GroupScreen extends StatefulWidget { + final GroupEntity group; + GroupScreen(this.group) : super(); + + @override + _GroupScreenState createState() => _GroupScreenState(); +} + +class _GroupScreenState extends State + with TickerProviderStateMixin { + late List fetchingUserBeacons; + late List fetchingNearbyBeacons; + + late GroupCubit _groupCubit; + late ScrollController _scrollController; + + @override + void initState() { + _scrollController = ScrollController(); + _scrollController.addListener(_listener); + _groupCubit = context.read(); + _groupCubit.position == null ? _groupCubit.fetchPosition() : null; + _groupCubit.fetchGroupHikes(widget.group.id!); + super.initState(); + } + + _listener() { + if (_scrollController.position.pixels == + _scrollController.position.maxScrollExtent) { + if (_groupCubit.isCompletelyFetched == true) { + return; + } + _groupCubit.fetchGroupHikes(widget.group.id!); + } + } + + @override + void dispose() { + _groupCubit.clear(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + TabController tabController = new TabController(length: 2, vsync: this); + return Scaffold( + resizeToAvoidBottomInset: false, + body: SafeArea( + child: BlocConsumer( + listener: (context, state) { + if (state is GroupErrorState) { + utils.showSnackBar(state.error, context); + } + }, + builder: (context, state) { + return ModalProgressHUD( + progressIndicator: const LoadingScreen(), + inAsyncCall: (state is GroupLoadingState) ? true : false, + child: Stack( + children: [ + CustomPaint( + size: Size(MediaQuery.of(context).size.width, + MediaQuery.of(context).size.height - 200), + painter: ShapePainter(), + ), + Align( + alignment: Alignment(-0.7, -0.95), + child: Container( + width: MediaQuery.of(context).size.width * 0.6, + child: Text( + 'Welcome to Group ' + widget.group.title!, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 25, + color: Colors.white, + ), + ), + ), + ), + Align( + alignment: Alignment(0.9, -0.8), + child: FloatingActionButton( + onPressed: () => showDialog( + context: context, + builder: (context) => AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0), + ), + // actionsAlignment: + // MainAxisAlignment.spaceEvenly, + title: Text( + localApi.userModel.isGuest! + ? 'Create Account' + : 'Logout', + style: + TextStyle(fontSize: 25, color: kYellow), + ), + content: Text( + localApi.userModel.isGuest! + ? 'Would you like to create an account?' + : 'Are you sure you wanna logout?', + style: TextStyle(fontSize: 16, color: kBlack), + ), + actions: [ + HikeButton( + buttonHeight: 2.5.h, + buttonWidth: 8.w, + onTap: () => + Navigator.of(context).pop(false), + text: 'No', + textSize: 18.0, + ), + HikeButton( + buttonHeight: 2.5.h, + buttonWidth: 8.w, + onTap: () async { + AutoRouter.of(context).maybePop(); + AutoRouter.of(context).pushAndPopUntil( + AuthScreenRoute(), + predicate: (route) => true, + ); + await localApi.deleteUser(); + }, + text: 'Yes', + textSize: 18.0, + ), + ], + )), + backgroundColor: kYellow, + child: localApi.userModel.isGuest! + ? Icon(Icons.person) + : Icon(Icons.logout), + ), + ), + Padding( + padding: EdgeInsets.fromLTRB(4.w, 25.h, 4.w, 5), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + width: 45.w, + child: HikeButton( + buttonWidth: homebwidth, + buttonHeight: homebheight - 2, + text: 'Create Hike', + textColor: Colors.white, + borderColor: Colors.white, + buttonColor: kYellow, + onTap: () { + CreateJoinBeaconDialog.createHikeDialog( + context, widget.group.id, _groupCubit); + }, + ), + ), + SizedBox( + width: 1.w, + ), + Container( + width: 45.w, + child: HikeButton( + buttonWidth: homebwidth, + buttonHeight: homebheight - 2, + text: 'Join a Hike', + textColor: kYellow, + borderColor: kYellow, + buttonColor: Colors.white, + onTap: () async { + CreateJoinBeaconDialog.joinBeaconDialog( + context, _groupCubit); + }, + ), + ), + ], + ), + ), + Column( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + height: MediaQuery.of(context).size.height * 0.565, + margin: EdgeInsets.only(top: 20), + decoration: BoxDecoration( + color: kLightBlue, + borderRadius: BorderRadius.only( + topLeft: const Radius.circular(50.0), + topRight: const Radius.circular(50.0), + ), + ), + child: Column( + children: [ + TabBar( + indicatorSize: TabBarIndicatorSize.tab, + indicatorColor: kBlue, + labelColor: kBlack, + tabs: [ + Tab(text: 'Your Beacons'), + Tab(text: 'Nearby Beacons'), + ], + controller: tabController, + ), + Expanded( + child: TabBarView( + controller: tabController, + children: [ + _groupBeacons(), + _nearByBeacons() + ], + ), + ) + ], + )) + ], + ), + ], + ), + ); + }, + ), + ), + ); + } + + Widget _groupBeacons() { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 2.0), + child: BlocBuilder( + builder: (context, state) { + if (state is ShrimmerGroupState) { + return Center( + child: BeaconCustomWidgets.getPlaceholder(), + ); + } + final beacons = _groupCubit.beacons; + + return Container( + alignment: Alignment.center, + child: beacons.length == 0 + ? SingleChildScrollView( + physics: AlwaysScrollableScrollPhysics(), + child: Column( + children: [ + Text( + 'You haven\'t joined or created any beacon yet', + textAlign: TextAlign.center, + style: TextStyle(color: kBlack, fontSize: 20), + ), + SizedBox( + height: 2.h, + ), + RichText( + text: TextSpan( + style: TextStyle(color: kBlack, fontSize: 20), + children: [ + TextSpan( + text: 'Join', + style: + TextStyle(fontWeight: FontWeight.bold)), + TextSpan(text: ' a Hike or '), + TextSpan( + text: 'Create', + style: + TextStyle(fontWeight: FontWeight.bold)), + TextSpan(text: ' a new one! '), + ], + ), + ), + ], + ), + ) + : ListView.builder( + controller: _scrollController, + physics: AlwaysScrollableScrollPhysics(), + scrollDirection: Axis.vertical, + itemCount: beacons.length, + padding: EdgeInsets.all(8), + itemBuilder: (context, index) { + if (index == beacons.length) { + return _groupCubit.isLoadingMore + ? Center( + child: LinearProgressIndicator( + color: kBlue, + )) + : SizedBox.shrink(); + } + return BeaconCustomWidgets.getBeaconCard( + context, beacons[index]); + }, + )); + }, + ), + ); + } + + Widget _nearByBeacons() { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 2), + child: BlocBuilder( + builder: (context, state) { + if (state is ShrimmerGroupState) { + return Center( + child: BeaconCustomWidgets.getPlaceholder(), + ); + } + final beacons = _groupCubit.beacons; + + return Container( + alignment: Alignment.center, + child: beacons.length == 0 + ? SingleChildScrollView( + physics: AlwaysScrollableScrollPhysics(), + child: Column( + children: [ + Text( + 'You haven\'t joined or created any beacon yet', + textAlign: TextAlign.center, + style: TextStyle(color: kBlack, fontSize: 20), + ), + SizedBox( + height: 2.h, + ), + RichText( + text: TextSpan( + // textAlign: + // TextAlign + // .center, + style: TextStyle(color: kBlack, fontSize: 20), + children: [ + TextSpan( + text: 'Join', + style: + TextStyle(fontWeight: FontWeight.bold)), + TextSpan(text: ' a Hike or '), + TextSpan( + text: 'Create', + style: + TextStyle(fontWeight: FontWeight.bold)), + TextSpan(text: ' a new one! '), + ], + ), + ), + ], + ), + ) + : ListView.builder( + physics: AlwaysScrollableScrollPhysics(), + scrollDirection: Axis.vertical, + itemCount: beacons.length, + padding: EdgeInsets.all(8), + itemBuilder: (context, index) { + return BeaconCustomWidgets.getBeaconCard( + context, beacons[index]); + }, + )); + }, + ), + ); + } +} diff --git a/lib/old/components/views/hike_screen.dart b/lib/Bloc/presentation/screens/hike_screen.dart similarity index 93% rename from lib/old/components/views/hike_screen.dart rename to lib/Bloc/presentation/screens/hike_screen.dart index ae3d56b..e622c81 100644 --- a/lib/old/components/views/hike_screen.dart +++ b/lib/Bloc/presentation/screens/hike_screen.dart @@ -162,19 +162,20 @@ class _HikeScreenState extends State { // setPolyline(); }, onTap: (loc) async { - if (model.panelController.isPanelOpen) - model.panelController.close(); - else { - String? title; - HikeScreenWidget - .showCreateLandMarkDialogueDialog( - context, - model.landmarkFormKey, - title, - loc, - model.createLandmark, - ); - } + // if (model.panelController.isPanelOpen) + // model.panelController.close(); + // else { + // String? title; + // HikeScreenWidget + // .showCreateLandMarkDialogueDialog( + // context, + // model.landmarkFormKey, + // title, + // loc, + // model.createLandmark, + + // ); + // } }), ), Align( diff --git a/lib/Bloc/presentation/screens/home_screen.dart b/lib/Bloc/presentation/screens/home_screen.dart new file mode 100644 index 0000000..9e49418 --- /dev/null +++ b/lib/Bloc/presentation/screens/home_screen.dart @@ -0,0 +1,361 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:beacon/Bloc/domain/entities/group/group_entity.dart'; +import 'package:beacon/Bloc/presentation/cubit/home_cubit.dart'; +import 'package:beacon/Bloc/presentation/widgets/create_join_dialog.dart'; +import 'package:beacon/old/components/beacon_card.dart'; +import 'package:beacon/old/components/group_card.dart'; +import 'package:beacon/old/components/hike_button.dart'; +import 'package:beacon/old/components/loading_screen.dart'; +import 'package:beacon/old/components/shape_painter.dart'; +import 'package:beacon/locator.dart'; +import 'package:beacon/old/components/utilities/constants.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; +import 'package:sizer/sizer.dart'; + +@RoutePage() +class HomeScreen extends StatefulWidget { + const HomeScreen({super.key}); + + @override + State createState() => _HomeScreenState(); +} + +class _HomeScreenState extends State { + Future _onPopHome(BuildContext context) async { + return showDialog( + context: context, + builder: (context) => AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0), + ), + // actionsAlignment: MainAxisAlignment.spaceEvenly, + contentPadding: EdgeInsets.all(25.0), + title: Text( + 'Confirm Exit', + style: TextStyle(fontSize: 25, color: kYellow), + ), + content: Text( + 'Do you really want to exit?', + style: TextStyle(fontSize: 18, color: kBlack), + ), + actions: [ + HikeButton( + buttonHeight: 2.5.h, + buttonWidth: 8.w, + onTap: () => AutoRouter.of(context).maybePop(false), + text: 'No', + ), + HikeButton( + buttonHeight: 2.5.h, + buttonWidth: 8.w, + onTap: () => AutoRouter.of(context).maybePop(true), + text: 'Yes', + ), + ], + ), + ); + } + + late ScrollController _scrollController; + late HomeCubit _homeCubit; + + @override + void initState() { + _scrollController = ScrollController(); + if (localApi.userModel.isGuest == false) { + _homeCubit = BlocProvider.of(context); + _homeCubit.fetchUserGroups(); + _scrollController.addListener(_onScroll); + } + super.initState(); + } + + void _onScroll() { + if (_scrollController.position.pixels == + _scrollController.position.maxScrollExtent) { + if (_homeCubit.isCompletelyFetched) return; + _homeCubit.fetchUserGroups(); + } + } + + @override + void dispose() { + _scrollController.dispose(); + if (localApi.userModel.isGuest == false) { + _homeCubit.clear(); + } + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return PopScope( + onPopInvoked: (didPop) { + _onPopHome(context); + }, + child: BlocConsumer( + listener: (context, state) { + if (state is ErrorHomeState) { + utils.showSnackBar(state.error, context); + } + }, + builder: (context, state) { + return Scaffold( + resizeToAvoidBottomInset: false, + body: SafeArea( + child: ModalProgressHUD( + inAsyncCall: state is NewGroupLoadingState ? true : false, + progressIndicator: LoadingScreen(), + child: Stack( + children: [ + CustomPaint( + size: Size(MediaQuery.of(context).size.width, + MediaQuery.of(context).size.height - 200), + painter: ShapePainter(), + ), + Align( + alignment: Alignment(0.9, -0.8), + child: FloatingActionButton( + onPressed: () => showDialog( + context: context, + builder: (context) => AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0), + ), + title: Text( + localApi.userModel.isGuest == true + ? 'Create Account' + : 'Logout', + style: TextStyle( + fontSize: 25, color: kYellow), + ), + content: Text( + localApi.userModel.isGuest == true + ? 'Would you like to create an account?' + : 'Are you sure you wanna logout?', + style: TextStyle( + fontSize: 16, color: kBlack), + ), + actions: [ + HikeButton( + buttonHeight: 2.5.h, + buttonWidth: 8.w, + onTap: () => AutoRouter.of(context) + .maybePop(false), + text: 'No', + textSize: 18.0, + ), + HikeButton( + buttonHeight: 2.5.h, + buttonWidth: 8.w, + onTap: () { + AutoRouter.of(context) + .replaceNamed('/auth'); + localApi.deleteUser(); + }, + text: 'Yes', + textSize: 18.0, + ), + ], + )), + backgroundColor: kYellow, + child: localApi.userModel.isGuest == true + ? Icon(Icons.person) + : Icon(Icons.logout)), + ), + Padding( + padding: EdgeInsets.fromLTRB(4.w, 25.h, 4.w, 5), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + StatefulBuilder( + builder: (context, setState) { + return Container( + width: 45.w, + child: HikeButton( + buttonWidth: homebwidth - 10, + buttonHeight: homebheight - 2, + text: 'Create Group', + textColor: Colors.white, + borderColor: Colors.white, + buttonColor: kYellow, + onTap: () async { + CreateJoinGroupDialog.createGroupDialog( + context); + }, + ), + ); + }, + ), + SizedBox( + width: 1.w, + ), + Container( + width: 45.w, + child: HikeButton( + buttonWidth: homebwidth, + buttonHeight: homebheight - 2, + text: 'Join a Group', + textColor: kYellow, + borderColor: kYellow, + buttonColor: Colors.white, + onTap: () async { + CreateJoinGroupDialog.joinGroupDialog(context); + }, + ), + ), + ], + ), + ), + Positioned( + bottom: 0, + child: Container( + decoration: BoxDecoration( + color: kLightBlue, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30), + topRight: Radius.circular(30))), + height: 56.h, + width: 100.w, + child: Column( + children: [ + Tab(text: 'Your Groups'), + Container( + height: 0.2.h, + color: kBlack, + ), + localApi.userModel.isGuest == true + ? Expanded( + child: Center( + child: SingleChildScrollView( + physics: + AlwaysScrollableScrollPhysics(), + child: Column( + children: [ + Text( + 'You haven\'t joined or created any group yet', + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.black, + fontSize: 20), + ), + SizedBox( + height: 20, + ), + RichText( + text: TextSpan( + style: TextStyle( + color: Colors.black, + fontSize: 20), + children: [ + TextSpan( + text: 'Join', + style: TextStyle( + fontWeight: + FontWeight + .bold)), + TextSpan( + text: ' a Group or '), + TextSpan( + text: 'Create', + style: TextStyle( + fontWeight: + FontWeight + .bold)), + TextSpan( + text: ' a new one!'), + ], + ), + ), + ], + ), + )), + ) + : _buildList() + ], + ))) + ], + ), + )), + ); + }, + )); + } + + Widget _buildList() { + return Expanded( + child: BlocBuilder( + builder: (context, state) { + if (state is ShrimmerState) { + return Center( + child: BeaconCustomWidgets.getPlaceholder(), + ); + } + + List groups = _homeCubit.totalGroups; + + if (groups.isEmpty) { + return Center( + child: SingleChildScrollView( + physics: AlwaysScrollableScrollPhysics(), + child: Column( + children: [ + Text( + 'You haven\'t joined or created any group yet', + textAlign: TextAlign.center, + style: TextStyle(color: Colors.black, fontSize: 20), + ), + SizedBox( + height: 20, + ), + RichText( + text: TextSpan( + style: TextStyle(color: Colors.black, fontSize: 20), + children: [ + TextSpan( + text: 'Join', + style: TextStyle(fontWeight: FontWeight.bold)), + TextSpan(text: ' a Group or '), + TextSpan( + text: 'Create', + style: TextStyle(fontWeight: FontWeight.bold)), + TextSpan(text: ' a new one!'), + ], + ), + ), + ], + ), + )); + } else { + return ListView.builder( + controller: _scrollController, + physics: AlwaysScrollableScrollPhysics(), + scrollDirection: Axis.vertical, + itemCount: groups.length + 1, + padding: EdgeInsets.all(8), + itemBuilder: (context, index) { + if (index == groups.length) { + if (_homeCubit.isLoadingMore && + !_homeCubit.isCompletelyFetched) { + return Center( + child: LinearProgressIndicator( + color: kBlue, + ), + ); + } else { + return SizedBox.shrink(); + } + } + return GroupCustomWidgets.getGroupCard(context, groups[index]); + }, + ); + } + }, + ), + ); + } +} diff --git a/lib/Bloc/presentation/screens/splash_screen.dart b/lib/Bloc/presentation/screens/splash_screen.dart new file mode 100644 index 0000000..0d7764a --- /dev/null +++ b/lib/Bloc/presentation/screens/splash_screen.dart @@ -0,0 +1,58 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:beacon/Bloc/domain/usecase/auth_usecase.dart'; +import 'package:flutter/material.dart'; +import 'package:beacon/locator.dart'; +import '../../../old/components/loading_screen.dart'; + +@RoutePage() +class SplashScreen extends StatefulWidget { + const SplashScreen({super.key}); + + @override + _SplashScreenState createState() => _SplashScreenState(); +} + +class _SplashScreenState extends State { + bool isCheckingUrl = false; + + @override + void initState() { + _handleNavigation(); + super.initState(); + } + + _handleNavigation() async { + await localApi.init(); + bool? isLoggedIn = await localApi.userloggedIn(); + final authUseCase = locator(); + + if (isLoggedIn == true) { + bool isConnected = await utils.checkInternetConnectivity(); + if (isConnected) { + final userInfo = await authUseCase.getUserInfoUseCase(); + if (userInfo.data != null) { + AutoRouter.of(context).replaceNamed('/home'); + } else { + AutoRouter.of(context).replaceNamed('/auth'); + } + } else { + AutoRouter.of(context).replaceNamed('/home'); + } + } else { + AutoRouter.of(context).replaceNamed('/auth'); + } + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + key: const Key('SplashScreenScaffold'), + body: LoadingScreen(), + ); + } +} diff --git a/lib/old/components/create_join_dialog.dart b/lib/Bloc/presentation/widgets/create_join_dialog.dart similarity index 55% rename from lib/old/components/create_join_dialog.dart rename to lib/Bloc/presentation/widgets/create_join_dialog.dart index 25e7bc4..6e31782 100644 --- a/lib/old/components/create_join_dialog.dart +++ b/lib/Bloc/presentation/widgets/create_join_dialog.dart @@ -1,80 +1,88 @@ -import 'package:beacon/old/components/services/validators.dart'; +import 'package:auto_route/auto_route.dart'; +import 'package:beacon/Bloc/core/utils/validators.dart'; +import 'package:beacon/Bloc/presentation/cubit/group_cubit.dart'; +import 'package:beacon/Bloc/presentation/cubit/home_cubit.dart'; +import 'package:beacon/locator.dart'; import 'package:beacon/old/components/hike_button.dart'; import 'package:beacon/old/components/utilities/constants.dart'; -import 'package:beacon/old/components/view_model/group_screen_view_model.dart'; import 'package:duration_picker/duration_picker.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:intl/intl.dart'; import 'package:sizer/sizer.dart'; -import 'view_model/home_screen_view_model.dart'; - class CreateJoinGroupDialog { - static Future createGroupDialog(BuildContext context, HomeViewModel model) { + static GlobalKey _groupKey = GlobalKey(); + + static final TextEditingController _groupNameController = + TextEditingController(); + + static Future createGroupDialog( + BuildContext context, + ) { bool isSmallSized = MediaQuery.of(context).size.height < 800; return showDialog( context: context, - builder: (context) => GestureDetector( - onTap: () => FocusManager.instance.primaryFocus?.unfocus(), - child: Dialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10.0), - ), - child: SingleChildScrollView( - child: Form( - key: model.formKeyCreate, - child: Container( - height: isSmallSized ? 35.h : 25.h, - child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: 32, vertical: 16), - child: Column( - children: [ - Container( - height: isSmallSized ? 12.h : 10.h, - child: Padding( - padding: const EdgeInsets.all(4.0), - child: TextFormField( - style: TextStyle(fontSize: 22.0), - validator: (value) => - Validator.validateBeaconTitle(value!), - onChanged: (name) { - model.title = name; - }, - decoration: InputDecoration( - border: InputBorder.none, - hintText: 'Enter Title Here', - labelStyle: TextStyle( - fontSize: labelsize, color: kYellow), - hintStyle: TextStyle( - fontSize: hintsize, color: hintColor), - labelText: 'Title', - alignLabelWithHint: true, - floatingLabelBehavior: - FloatingLabelBehavior.always, - focusedBorder: InputBorder.none, - enabledBorder: InputBorder.none), - ), + builder: (context) => Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0), + ), + child: SingleChildScrollView( + child: Form( + key: _groupKey, + child: Container( + height: isSmallSized ? 35.h : 25.h, + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 32, vertical: 16), + child: Column( + children: [ + Container( + height: isSmallSized ? 12.h : 10.h, + child: Padding( + padding: const EdgeInsets.all(4.0), + child: TextFormField( + controller: _groupNameController, + style: TextStyle(fontSize: 22.0), + validator: (value) => + Validator.validateBeaconTitle(value!), + onChanged: (name) {}, + decoration: InputDecoration( + border: InputBorder.none, + hintText: 'Enter Title Here', + labelStyle: TextStyle( + fontSize: labelsize, color: kYellow), + hintStyle: TextStyle( + fontSize: hintsize, color: hintColor), + labelText: 'Title', + alignLabelWithHint: true, + floatingLabelBehavior: + FloatingLabelBehavior.always, + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none), ), - color: kLightBlue, - ), - SizedBox( - height: 2.h, ), - Flexible( - flex: 2, - child: HikeButton( - text: 'Create Group', - textSize: 18.0, - textColor: Colors.white, - buttonColor: kYellow, - onTap: () { - // FocusManager.instance.primaryFocus?.unfocus(); - // navigationService.pop(); - model.createGroupRoom(); - }), - ), - ], - ), + color: kLightBlue, + ), + SizedBox( + height: 2.h, + ), + Flexible( + flex: 2, + child: HikeButton( + text: 'Create Group', + textSize: 18.0, + textColor: Colors.white, + buttonColor: kYellow, + onTap: () { + if (!_groupKey.currentState!.validate()) return; + AutoRouter.of(context).maybePop(); + BlocProvider.of(context) + .createGroup(_groupNameController.text.trim()); + _groupNameController.clear(); + }), + ), + ], ), ), ), @@ -84,7 +92,12 @@ class CreateJoinGroupDialog { ); } - static Future joinGroupDialog(BuildContext context, HomeViewModel model) { + static GlobalKey _joinGroupKey = GlobalKey(); + + static final TextEditingController _joinGroupController = + TextEditingController(); + + static Future joinGroupDialog(BuildContext context) { bool isSmallSized = MediaQuery.of(context).size.height < 800; return showDialog( context: context, @@ -93,24 +106,25 @@ class CreateJoinGroupDialog { borderRadius: BorderRadius.circular(10.0), ), child: Form( - key: model.formKeyJoin, + key: _joinGroupKey, child: Container( height: isSmallSized ? 35.h : 25.h, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), child: Column( - children: [ + children: [ Container( height: isSmallSized ? 12.h : 10.h, child: Padding( padding: const EdgeInsets.all(4.0), child: TextFormField( + controller: _joinGroupController, keyboardType: TextInputType.text, textCapitalization: TextCapitalization.characters, style: TextStyle(fontSize: 22.0), validator: (value) => Validator.validatePasskey(value!), - onChanged: (key) { - model.enteredGroupCode = key.toUpperCase(); + onChanged: (value) { + _joinGroupController.text = value.toUpperCase(); }, decoration: InputDecoration( alignLabelWithHint: true, @@ -137,8 +151,11 @@ class CreateJoinGroupDialog { textColor: Colors.white, buttonColor: kYellow, onTap: () { - // navigationService.pop(); - model.joinGroupRoom(); + if (!_joinGroupKey.currentState!.validate()) return; + AutoRouter.of(context).maybePop(); + BlocProvider.of(context) + .joinGroup(_joinGroupController.text.trim()); + _joinGroupController.clear(); }, ), ), @@ -153,13 +170,26 @@ class CreateJoinGroupDialog { } class CreateJoinBeaconDialog { - static Future createHikeDialog(BuildContext context, GroupViewModel model, - Function reloadList, String? groupID) { + static late String title; + static DateTime? startDate = DateTime.now(); + static TimeOfDay? startTime = + TimeOfDay(hour: TimeOfDay.now().hour, minute: TimeOfDay.now().minute + 1); + static Duration? duration = Duration(minutes: 5); + + static GlobalKey _createFormKey = GlobalKey(); + + static FocusNode _titleNode = FocusNode(); + static FocusNode _startDateNode = FocusNode(); + static FocusNode _startTimeNode = FocusNode(); + static FocusNode _durationNode = FocusNode(); + + static TextEditingController _dateController = TextEditingController(); + static TextEditingController _startTimeController = TextEditingController(); + static TextEditingController _durationController = TextEditingController(); + + static Future createHikeDialog( + BuildContext context, String? groupID, GroupCubit groupCubit) { bool isSmallSized = MediaQuery.of(context).size.height < 800; - model.resultingDuration = Duration(minutes: 30); - model.durationController = new TextEditingController(); - model.startsAtDate = new TextEditingController(); - model.startsAtTime = new TextEditingController(); return showDialog( context: context, builder: (context) => GestureDetector( @@ -170,14 +200,14 @@ class CreateJoinBeaconDialog { ), child: SingleChildScrollView( child: Form( - key: model.formKeyCreate, + key: _createFormKey, child: Container( height: isSmallSized ? 75.h : 65.h, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), child: Column( - children: [ + children: [ Container( height: isSmallSized ? 14.h : 12.h, child: Padding( @@ -187,7 +217,11 @@ class CreateJoinBeaconDialog { validator: (value) => Validator.validateBeaconTitle(value!), onChanged: (name) { - model.title = name; + title = name; + }, + focusNode: _titleNode, + onEditingComplete: () { + _titleNode.unfocus(); }, decoration: InputDecoration( border: InputBorder.none, @@ -206,44 +240,42 @@ class CreateJoinBeaconDialog { ), color: kLightBlue, ), - SizedBox( - height: 2.h, - ), + SizedBox(height: 2.h), + // Start Date Field Container( height: isSmallSized ? 12.h : 10.h, child: Padding( padding: const EdgeInsets.all(4.0), child: InkWell( onTap: () async { - FocusManager.instance.primaryFocus?.unfocus(); - model.startingdate = await showDatePicker( + startDate = await showDatePicker( context: context, - initialDate: DateTime.now(), + initialDate: startDate!, firstDate: DateTime.now(), lastDate: DateTime(2100), builder: (context, child) => Theme( data: ThemeData().copyWith( textTheme: Theme.of(context).textTheme, colorScheme: ColorScheme.light( - primary: kBlue, - onPrimary: Colors.white, + primary: kLightBlue, + onPrimary: Colors.grey, surface: kBlue, ), ), child: child!), ); - model.startsAtDate.text = model.startingdate - .toString() - .substring(0, 10); + _dateController.text = + DateFormat('yyyy-MM-dd').format(startDate!); + + _startDateNode.unfocus(); + FocusScope.of(context) + .requestFocus(_startTimeNode); }, child: TextFormField( + controller: _dateController, enabled: false, - controller: model.startsAtDate, - onChanged: (value) { - model.startsAtDate.text = model.startingdate - .toString() - .substring(0, 10); - }, + focusNode: _startDateNode, + onEditingComplete: () {}, decoration: InputDecoration( border: InputBorder.none, hintText: 'Choose Start Date', @@ -262,61 +294,27 @@ class CreateJoinBeaconDialog { ), color: kLightBlue, ), - SizedBox( - height: 2.h, - ), + SizedBox(height: 2.h), + // Start Time Field Container( height: isSmallSized ? 12.h : 10.h, child: Padding( padding: const EdgeInsets.all(4.0), child: InkWell( onTap: () async { - FocusManager.instance.primaryFocus?.unfocus(); - model.startingTime = await showTimePicker( - context: context, - initialTime: TimeOfDay.now(), - builder: (context, child) { - return Theme( - data: ThemeData( - textTheme: Theme.of(context).textTheme, - timePickerTheme: TimePickerThemeData( - dialHandColor: kBlue, - dayPeriodTextColor: kBlue, - hourMinuteTextColor: kBlue, - helpTextStyle: TextStyle( - fontFamily: 'FuturaBold', - fontSize: 15.0, - fontWeight: FontWeight.w600, - ), - hourMinuteTextStyle: TextStyle( - fontFamily: 'FuturaBold', - fontSize: 40.0, - fontWeight: FontWeight.w600, - ), - dayPeriodTextStyle: TextStyle( - fontFamily: 'FuturaBold', - fontSize: 18.0, - fontWeight: FontWeight.w600, - ), - ), - ), - // This will change to light theme. - child: child!, - ); - }, - ); - model.startsAtTime.text = model.startingTime - .toString() - .substring(10, 15); + startTime = await showTimePicker( + context: context, initialTime: startTime!); + + if (startTime != null) { + _startTimeController.text = + '${startTime!.hour}:${startTime!.minute}'; + } }, child: TextFormField( + controller: _startTimeController, enabled: false, - controller: model.startsAtTime, - onChanged: (value) { - model.startsAtTime.text = model.startingTime - .toString() - .substring(10, 15); - }, + onEditingComplete: () {}, + focusNode: _startTimeNode, decoration: InputDecoration( border: InputBorder.none, alignLabelWithHint: true, @@ -337,40 +335,36 @@ class CreateJoinBeaconDialog { ), color: kLightBlue, ), - SizedBox( - height: 2.h, - ), + SizedBox(height: 2.h), + // Duration Field Container( height: isSmallSized ? 14.h : 12.h, child: Padding( padding: const EdgeInsets.all(4.0), child: InkWell( onTap: () async { - FocusManager.instance.primaryFocus?.unfocus(); - model.resultingDuration = - await showDurationPicker( + duration = await showDurationPicker( context: context, - initialTime: model.resultingDuration != null - ? model.resultingDuration! - : Duration(minutes: 30), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(5.0), - ), + initialTime: duration!, ); - model.durationController.text = model - .resultingDuration - .toString() - .substring(0, 8); + if (duration!.inHours != 0 && + duration!.inMinutes != 0) { + _durationController.text = + '${duration!.inHours.toString()} hour ${(duration!.inMinutes % 60)} minutes'; + } else if (duration!.inMinutes != 0) { + _durationController.text = + '${duration!.inMinutes.toString()} minutes'; + } + if (_durationController.text.isEmpty) { + _durationNode.unfocus(); + } }, child: TextFormField( enabled: false, - controller: model.durationController, - onChanged: (value) { - model.durationController.text = model - .resultingDuration - .toString() - .substring(0, 8); + focusNode: _durationNode, + controller: _durationController, + onEditingComplete: () { + _durationNode.unfocus(); }, validator: (value) => Validator.validateDuration(value.toString()), @@ -393,9 +387,7 @@ class CreateJoinBeaconDialog { ), color: kLightBlue, ), - SizedBox( - height: 2.h, - ), + SizedBox(height: 2.h), Flexible( flex: 2, child: HikeButton( @@ -403,29 +395,50 @@ class CreateJoinBeaconDialog { textSize: 18.0, textColor: Colors.white, buttonColor: kYellow, - onTap: () { - FocusManager.instance.primaryFocus?.unfocus(); - // navigationService.pop(); - if (model.startingdate == null || - model.startingTime == null) { - // navigationService! - // .showSnackBar("Enter date and time"); - return; - } - model.startsAt = DateTime( - model.startingdate!.year, - model.startingdate!.month, - model.startingdate!.day, - model.startingTime!.hour, - model.startingTime!.minute, - ); - // localNotif.scheduleNotification(); - if (model.startsAt.isBefore(DateTime.now())) { - // navigationService!.showSnackBar( - // "Enter a valid date and time!!"); - return; + onTap: () async { + if (_createFormKey.currentState!.validate()) { + DateTime startsAt = DateTime( + startDate!.year, + startDate!.month, + startDate!.day, + startTime!.hour, + startTime!.minute); + + final startingTime = + startsAt.millisecondsSinceEpoch; + + int currenTime = + DateTime.now().millisecondsSinceEpoch; + + if (startingTime < currenTime) { + utils.showSnackBar( + 'Please chose a correct time!', context); + return; + } + + final endTime = startsAt + .copyWith( + hour: startsAt.hour + duration!.inHours, + minute: startsAt.minute + + duration!.inMinutes) + .millisecondsSinceEpoch; + + if (groupCubit.position == null) { + utils.showSnackBar( + 'Please give access to location!', + context); + groupCubit.fetchPosition(); + return; + } + AutoRouter.of(context).maybePop(); + groupCubit.createHike( + title, + startingTime, + endTime, + groupCubit.position!.latitude.toString(), + groupCubit.position!.longitude.toString(), + groupID!); } - model.createHikeRoom(groupID, reloadList); }), ), ], @@ -439,8 +452,9 @@ class CreateJoinBeaconDialog { ); } - static Future joinBeaconDialog( - BuildContext context, GroupViewModel model, Function reloadList) { + static GlobalKey _joinBeaconKey = GlobalKey(); + static TextEditingController _joinBeaconController = TextEditingController(); + static Future joinBeaconDialog(BuildContext context, GroupCubit groupCubit) { bool isSmallSized = MediaQuery.of(context).size.height < 800; return showDialog( context: context, @@ -449,7 +463,7 @@ class CreateJoinBeaconDialog { borderRadius: BorderRadius.circular(10.0), ), child: Form( - key: model.formKeyJoin, + key: _joinBeaconKey, child: Container( height: isSmallSized ? 30.h : 25.h, child: Padding( @@ -461,12 +475,13 @@ class CreateJoinBeaconDialog { child: Padding( padding: const EdgeInsets.all(4.0), child: TextFormField( + controller: _joinBeaconController, keyboardType: TextInputType.text, textCapitalization: TextCapitalization.characters, style: TextStyle(fontSize: 22.0), validator: (value) => Validator.validatePasskey(value!), onChanged: (key) { - model.enteredPasskey = key.toUpperCase(); + _joinBeaconController.text = key.toUpperCase(); }, decoration: InputDecoration( alignLabelWithHint: true, @@ -493,8 +508,10 @@ class CreateJoinBeaconDialog { textColor: Colors.white, buttonColor: kYellow, onTap: () { - // navigationService.pop(); - model.joinHikeRoom(reloadList); + if (!_joinBeaconKey.currentState!.validate()) return; + AutoRouter.of(context).maybePop(); + groupCubit.joinHike(_joinBeaconController.text.trim()); + _joinBeaconController.clear(); }, ), ), diff --git a/lib/Bloc/presentation/widgets/text_field.dart b/lib/Bloc/presentation/widgets/text_field.dart new file mode 100644 index 0000000..db87ca9 --- /dev/null +++ b/lib/Bloc/presentation/widgets/text_field.dart @@ -0,0 +1,68 @@ +import 'package:beacon/old/components/utilities/constants.dart'; +import 'package:flutter/material.dart'; + +class CustomTextField extends StatefulWidget { + final IconData iconData; + final String hintText; + final bool showTrailing; + final TextEditingController controller; + final String? Function(String?)? validator; + final FocusNode? focusNode; + final FocusNode? nextFocusNode; + + CustomTextField( + {super.key, + required this.iconData, + required this.hintText, + required this.controller, + this.showTrailing = false, + this.validator, + this.focusNode, + this.nextFocusNode}); + + @override + State createState() => _CustomTextFieldState(); +} + +class _CustomTextFieldState extends State { + bool obscureText = true; + + @override + Widget build(BuildContext context) { + return TextFormField( + validator: widget.validator, + controller: widget.controller, + focusNode: widget.focusNode, + decoration: InputDecoration( + border: InputBorder.none, + icon: Icon( + widget.iconData, + color: Colors.black, + size: 24.0, + ), + hintText: widget.hintText, + hintStyle: TextStyle(fontSize: hintsize - 2, color: hintColor), + suffixIcon: widget.showTrailing == true + ? IconButton( + onPressed: () { + setState(() { + obscureText = !obscureText; + }); + }, + icon: Icon( + obscureText ? Icons.visibility_off : Icons.visibility, + color: Colors.black, + )) + : null), + style: TextStyle(color: Colors.black), + obscureText: widget.showTrailing == false ? false : obscureText, + onEditingComplete: () { + if (widget.nextFocusNode != null) { + FocusScope.of(context).requestFocus(widget.nextFocusNode); + } else { + FocusScope.of(context).unfocus(); + } + }, + ); + } +} diff --git a/lib/locator.dart b/lib/locator.dart index 2073c85..127a8df 100644 --- a/lib/locator.dart +++ b/lib/locator.dart @@ -1,3 +1,18 @@ +import 'package:beacon/Bloc/core/services/shared_prefrence_service.dart'; +import 'package:beacon/Bloc/core/utils/utils.dart'; +import 'package:beacon/Bloc/data/datasource/local/local_api.dart'; +import 'package:beacon/Bloc/data/datasource/remote/remote_auth_api.dart'; +import 'package:beacon/Bloc/data/datasource/remote/remote_group_api.dart'; +import 'package:beacon/Bloc/data/datasource/remote/remote_home_api.dart'; +import 'package:beacon/Bloc/data/repositories/auth_repository_implementation.dart'; +import 'package:beacon/Bloc/data/repositories/group_repository_implementation.dart'; +import 'package:beacon/Bloc/data/repositories/home_repository_implementation.dart'; +import 'package:beacon/Bloc/domain/repositories/auth_repository.dart'; +import 'package:beacon/Bloc/domain/repositories/group_repository.dart'; +import 'package:beacon/Bloc/domain/repositories/home_repository.dart'; +import 'package:beacon/Bloc/domain/usecase/auth_usecase.dart'; +import 'package:beacon/Bloc/domain/usecase/group_usecase.dart'; +import 'package:beacon/Bloc/domain/usecase/home_usecase.dart'; import 'package:beacon/main.dart'; import 'package:beacon/old/components/services/connection_checker.dart'; import 'package:beacon/old/components/services/database_mutation_functions.dart'; @@ -5,12 +20,12 @@ import 'package:beacon/Bloc/config/graphql_config.dart'; import 'package:beacon/old/components/services/hive_localdb.dart'; import 'package:beacon/old/components/services/local_notification.dart'; import 'package:beacon/old/components/services/navigation_service.dart'; -import 'package:beacon/old/components/services/shared_preference_service.dart'; import 'package:beacon/old/components/services/user_config.dart'; import 'package:beacon/old/components/view_model/auth_screen_model.dart'; import 'package:beacon/old/components/view_model/home_screen_view_model.dart'; import 'package:beacon/old/components/view_model/hike_screen_model.dart'; import 'package:beacon/old/components/view_model/group_screen_view_model.dart'; +import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; GetIt locator = GetIt.instance; @@ -18,13 +33,17 @@ final UserConfig? userConfig = locator(); final NavigationService? navigationService = locator(); final DataBaseMutationFunctions? databaseFunctions = locator(); -final GraphQLConfig? graphqlConfig = locator(); +final GraphQLConfig graphqlConfig = locator(); final LocalNotification? localNotif = locator(); final HiveLocalDb? hiveDb = locator(); final ConnectionChecker? connectionChecker = locator(); final sharedPrefrenceService = locator(); +final localApi = locator(); +final remoteAuthApi = locator(); +final remoteHomeApi = locator(); +final utils = locator(); -void setupLocator() { +void setupLocator() async { // shared prefrence services locator.registerSingleton(SharedPreferenceService()); @@ -33,7 +52,8 @@ void setupLocator() { //userConfig locator.registerSingleton(UserConfig()); - locator.registerSingleton(GraphQLConfig()); + + locator.registerSingleton(GraphQLConfig()); //databaseMutationFunction locator.registerSingleton(DataBaseMutationFunctions()); @@ -52,4 +72,46 @@ void setupLocator() { //local Notification locator.registerSingleton(LocalNotification()); + + // hive localDb + locator.registerSingleton(LocalApi()); + + final authClient = await graphqlConfig.authClient(); + + // Remote Api + locator.registerSingleton( + RemoteAuthApi( + clientAuth: authClient, + clientNonAuth: ValueNotifier(graphqlConfig.clientToQuery()), + ), + ); + + locator.registerSingleton( + RemoteHomeApi(authClient), + ); + + locator.registerSingleton( + RemoteGroupApi(authClient: authClient)); + + // registering auth reporitory of domain + locator.registerSingleton( + AuthRepositoryImplementation(remoteAuthApi: locator())); + locator.registerSingleton( + HomeRepostitoryImplementation(remoteHomeApi: locator())); + locator.registerSingleton( + GroupRepostioryImplementation(remoteGroupApi: locator())); + + // use case + locator.registerSingleton( + AuthUseCase(authRepository: locator())); + locator.registerSingleton( + HomeUseCase(homeRepository: locator())); + locator.registerSingleton( + GroupUseCase(locator())); + + // // cubit + // locator.registerFactory(() => HomeCubit(homeUseCase: locator())); + + // registering utils + locator.registerSingleton(Utils()); } diff --git a/lib/main.dart b/lib/main.dart index 1be083d..00c807e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,38 +1,85 @@ +// main.dart import 'package:beacon/Bloc/config/enviornment_config.dart'; +import 'package:beacon/Bloc/presentation/cubit/auth_cubit.dart'; +import 'package:beacon/Bloc/presentation/cubit/group_cubit.dart'; +import 'package:beacon/Bloc/presentation/cubit/home_cubit.dart'; import 'package:beacon/locator.dart'; -import 'package:beacon/router.dart' as router; import 'package:beacon/old/components/view_model/base_view_model.dart'; import 'package:beacon/old/components/views/base_view.dart'; +import 'package:beacon/router.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:sizer/sizer.dart'; import 'package:overlay_support/overlay_support.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); - EnvironmentConfig.loadEnvVariables(); + await EnvironmentConfig.loadEnvVariables(); SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, DeviceOrientation.portraitDown, ]); setupLocator(); + // starting local api for storing data + await localApi.init(); await localNotif!.initialize(); await hiveDb!.init(); - runApp( - OverlaySupport( + + AppRouter _appRouter = AppRouter(); + + runApp(MyApp(router: _appRouter)); +} + +class MyApp extends StatefulWidget { + final AppRouter router; + + const MyApp({Key? key, required this.router}) : super(key: key); + + @override + _MyAppState createState() => _MyAppState(); +} + +class _MyAppState extends State { + late AppRouter _appRouter; + + @override + void initState() { + super.initState(); + _appRouter = widget.router; + } + + void restartApp() { + setState(() {}); + } + + @override + Widget build(BuildContext context) { + return OverlaySupport( child: Sizer( - builder: (context, orientation, deviceType) => MaterialApp( - debugShowCheckedModeBanner: false, - title: 'Beacon', - navigatorKey: navigationService!.navigatorKey, - theme: ThemeData(fontFamily: 'FuturaBold'), - initialRoute: '/', - onGenerateRoute: router.generateRoute, + builder: (context, orientation, deviceType) => MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => AuthCubit(authUseCase: locator()), + ), + BlocProvider( + create: (context) => HomeCubit(homeUseCase: locator()), + ), + BlocProvider( + create: (context) => GroupCubit(locator()), + ), + ], + child: MaterialApp.router( + debugShowCheckedModeBanner: false, + title: 'Beacon', + theme: ThemeData(fontFamily: 'FuturaBold'), + routerConfig: _appRouter.config(), + ), ), ), - ), - ); + ); + } } class DemoPageView extends StatelessWidget { diff --git a/lib/old/components/beacon_card.dart b/lib/old/components/beacon_card.dart index 6bed349..cede5b5 100644 --- a/lib/old/components/beacon_card.dart +++ b/lib/old/components/beacon_card.dart @@ -1,10 +1,15 @@ +import 'dart:developer'; +import 'package:auto_route/auto_route.dart'; +import 'package:beacon/Bloc/domain/entities/beacon/beacon_entity.dart'; import 'package:beacon/old/components/active_beacon.dart'; -import 'package:beacon/old/components/timer.dart'; +import 'package:beacon/old/components/models/location/location.dart'; +import 'package:beacon/old/components/models/user/user_info.dart'; import 'package:beacon/locator.dart'; import 'package:beacon/old/components/models/beacon/beacon.dart'; import 'package:beacon/old/components/utilities/constants.dart'; -import 'package:beacon/old/components/views/hike_screen.dart'; +import 'package:beacon/router.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:sizer/sizer.dart'; import 'package:skeleton_text/skeleton_text.dart'; import 'package:intl/intl.dart'; @@ -12,7 +17,7 @@ import 'package:intl/intl.dart'; class BeaconCustomWidgets { static final Color textColor = Color(0xFFAFAFAF); - static Widget getBeaconCard(BuildContext context, Beacon beacon) { + static Widget getBeaconCard(BuildContext context, BeaconEntity beacon) { print(beacon.leader!.name); bool hasStarted; bool hasEnded; @@ -25,41 +30,73 @@ class BeaconCustomWidgets { .isBefore(DateTime.fromMillisecondsSinceEpoch(beacon.startsAt!)); return GestureDetector( onTap: () async { + if (hasEnded) + utils.showSnackBar('Beacon is not active anymore!', context); bool isJoinee = false; for (var i in beacon.followers!) { - if (i.id == userConfig!.currentUser!.id) { + if (i!.id == localApi.userModel.id) { isJoinee = true; } } if (!hasStarted) { - // navigationService!.showSnackBar( - // 'Beacon has not yet started! \nPlease come back at ${DateFormat("hh:mm a, d/M/y").format(DateTime.fromMillisecondsSinceEpoch(beacon.startsAt!)).toString()}', - // ); + utils.showSnackBar( + 'Beacon has not yet started! \nPlease come back at ${DateFormat("hh:mm a, d/M/y").format(DateTime.fromMillisecondsSinceEpoch(beacon.startsAt!)).toString()}', + context); return; } if (hasStarted && - (beacon.leader!.id == userConfig!.currentUser!.id || isJoinee)) { - navigationService!.pushScreen('/hikeScreen', - arguments: HikeScreen( - beacon, - isLeader: (beacon.leader!.id == userConfig!.currentUser!.id), - )); - } else { - await databaseFunctions!.init(); - final Beacon? _beacon = - await databaseFunctions!.joinBeacon(beacon.shortcode); - if (!hasStarted) { - // navigationService!.showSnackBar( - // 'Beacon has not yet started! \nPlease come back at ${DateFormat("hh:mm a, d/M/y").format(DateTime.fromMillisecondsSinceEpoch(beacon.startsAt!)).toString()}', - // ); - return; - } - if (hasStarted && _beacon != null) { - navigationService!.pushScreen('/hikeScreen', - arguments: HikeScreen(beacon, isLeader: false)); - } - //Snackbar is displayed by joinBeacon itself on any error or trying to join expired beacon. + (beacon.leader!.id == localApi.userModel.id || isJoinee)) { + log('here'); + // navigationService!.pushScreen('/hikeScreen', + // arguments: HikeScreen( + // beacon, + // isLeader: (beacon.leader!.id == userConfig!.currentUser!.id), + // )); + + // for(int i=0; i fetchBeaconInfo(String? id) async { final QueryResult result = await clientAuth .query(QueryOptions(document: gql(_beaconQuery.fetchBeaconDetail(id)))); + + log('fetching beacon info: $result'); if (result.hasException) { final bool exception = encounteredExceptionOrError(result.exception!, showSnackBar: false); @@ -461,7 +463,8 @@ class DataBaseMutationFunctions { // navigationService!.showSnackBar( // "Something went wrong: ${result.exception!.graphqlErrors.first.message}"); print("Something went wrong: ${result.exception}"); - navigationService!.removeAllAndPush('/main', '/'); + // navigationService!.removeAllAndPush('/main', '/'); + // AutoRouter.of(context).pushNamed('/home'); } else if (result.data != null && result.isConcrete) { final Group group = Group.fromJson( result.data!['joinBeacon'] as Map, diff --git a/lib/old/components/services/local_notification.dart b/lib/old/components/services/local_notification.dart index 835f88e..d6a3090 100644 --- a/lib/old/components/services/local_notification.dart +++ b/lib/old/components/services/local_notification.dart @@ -1,6 +1,6 @@ import 'package:beacon/locator.dart'; import 'package:beacon/old/components/models/beacon/beacon.dart'; -import 'package:beacon/old/components/views/hike_screen.dart'; +import 'package:beacon/Bloc/presentation/screens/hike_screen.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:timezone/timezone.dart' as tz; import 'package:timezone/data/latest.dart' as tz; diff --git a/lib/old/components/services/shared_preference_service.dart b/lib/old/components/services/shared_preference_service.dart deleted file mode 100644 index b6b1444..0000000 --- a/lib/old/components/services/shared_preference_service.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:shared_preferences/shared_preferences.dart'; - -class SharedPreferenceService { - late SharedPreferences _prefs; - - Future getSharedPreferencesInstance() async { - try { - _prefs = await SharedPreferences.getInstance(); - return true; - } catch (e) { - print("SharedPreferences error: $e"); - return false; - } - } - - Future setToken(String token) async { - await _prefs.setString('token', token); - } - - Future clearToken() async { - await _prefs.clear(); - } - - Future get token async => _prefs.getString('token'); -} - -SharedPreferenceService sharedPreferenceService = SharedPreferenceService(); diff --git a/lib/old/components/services/user_config.dart b/lib/old/components/services/user_config.dart index 8706d37..01c8773 100644 --- a/lib/old/components/services/user_config.dart +++ b/lib/old/components/services/user_config.dart @@ -15,7 +15,7 @@ class UserConfig { return false; } bool userUpdated = true; - await graphqlConfig!.getToken().then((value) async { + await graphqlConfig.getToken().then((value) async { print('${userConfig!._currentUser!.authToken}'); await databaseFunctions!.init(); await databaseFunctions!.fetchCurrentUserInfo().then((value) { diff --git a/lib/old/components/timer.dart b/lib/old/components/timer.dart index a4ea073..45cc84c 100644 --- a/lib/old/components/timer.dart +++ b/lib/old/components/timer.dart @@ -1,7 +1,7 @@ import 'package:beacon/locator.dart'; import 'package:beacon/old/components/models/beacon/beacon.dart'; import 'package:beacon/old/components/utilities/constants.dart'; -import 'package:beacon/old/components/views/hike_screen.dart'; +import 'package:beacon/Bloc/presentation/screens/hike_screen.dart'; import 'package:flutter/material.dart'; import 'package:flutter_countdown_timer/index.dart'; diff --git a/lib/old/components/view_model/group_screen_view_model.dart b/lib/old/components/view_model/group_screen_view_model.dart index 9951dc1..bdba389 100644 --- a/lib/old/components/view_model/group_screen_view_model.dart +++ b/lib/old/components/view_model/group_screen_view_model.dart @@ -1,8 +1,9 @@ +import 'package:auto_route/auto_route.dart'; import 'package:beacon/old/components/enums/view_state.dart'; import 'package:beacon/locator.dart'; import 'package:beacon/old/components/models/beacon/beacon.dart'; import 'package:beacon/old/components/view_model/base_view_model.dart'; -import 'package:beacon/old/components/views/hike_screen.dart'; +import 'package:beacon/Bloc/presentation/screens/hike_screen.dart'; import 'package:flutter/material.dart'; class GroupViewModel extends BaseModel { @@ -24,11 +25,13 @@ class GroupViewModel extends BaseModel { TextEditingController startsAtTime = new TextEditingController(); String? enteredPasskey; - createHikeRoom(String? groupID, Function reloadList) async { + createHikeRoom( + String? groupID, Function reloadList, BuildContext context) async { FocusScope.of(navigationService!.navigatorKey.currentContext!).unfocus(); validate = AutovalidateMode.always; if (formKeyCreate.currentState!.validate()) { - navigationService!.pop(); + // navigationService!.pop(); + AutoRouter.of(context).maybePop(); setState(ViewState.busy); validate = AutovalidateMode.disabled; databaseFunctions!.init(); @@ -64,7 +67,7 @@ class GroupViewModel extends BaseModel { } joinHikeRoom(Function reloadList) async { - FocusScope.of(navigationService!.navigatorKey.currentContext!).unfocus(); + // FocusScope.of(navigationService!.navigatorKey.currentContext!).unfocus(); validate = AutovalidateMode.always; if (formKeyJoin.currentState!.validate()) { setState(ViewState.busy); @@ -78,8 +81,8 @@ class GroupViewModel extends BaseModel { .isAfter(DateTime.fromMillisecondsSinceEpoch(beacon.startsAt!)); if (hasStarted) { - navigationService!.pushScreen('/hikeScreen', - arguments: HikeScreen(beacon, isLeader: false)); + // navigationService!.pushScreen('/hikeScreen', + // arguments: HikeScreen(beacon, isLeader: false)); } else { localNotif!.scheduleNotification(beacon); setState(ViewState.idle); @@ -105,6 +108,6 @@ class GroupViewModel extends BaseModel { await hiveDb!.beaconsBox.clear(); // setState(ViewState.idle); await localNotif!.deleteNotification(); - navigationService!.removeAllAndPush('/auth', '/'); + // navigationService!.removeAllAndPush('/auth', '/'); } } diff --git a/lib/old/components/view_model/hike_screen_model.dart b/lib/old/components/view_model/hike_screen_model.dart index 81f6364..0dcd830 100644 --- a/lib/old/components/view_model/hike_screen_model.dart +++ b/lib/old/components/view_model/hike_screen_model.dart @@ -1,4 +1,6 @@ import 'dart:async'; +import 'dart:developer'; +import 'package:auto_route/auto_route.dart'; import 'package:beacon/Bloc/core/queries/beacon.dart'; import 'package:beacon/old/components/dialog_boxes.dart'; import 'package:beacon/Bloc/config/enviornment_config.dart'; @@ -227,6 +229,7 @@ class HikeScreenViewModel extends BaseModel { value = hiveDb!.beaconsBox.get(beacon!.id); beacon = value; } + log('value: ${value}'); await updateModel(value!); }); } @@ -375,6 +378,8 @@ class HikeScreenViewModel extends BaseModel { Future initialise(Beacon beaconParsed, bool? widgetIsLeader) async { beacon = hiveDb!.beaconsBox.get(beaconParsed.id); isLeader = widgetIsLeader; + beacon = beaconParsed; + await databaseFunctions!.init(); if (await connectionChecker!.checkForInternetConnection()) { await fetchData(); @@ -425,12 +430,10 @@ class HikeScreenViewModel extends BaseModel { setState(ViewState.idle); } - Future createLandmark( - var title, - var loc, - ) async { + Future createLandmark(var title, var loc, BuildContext context) async { if (landmarkFormKey.currentState!.validate()) { - navigationService!.pop(); + // navigationService!.pop(); + AutoRouter.of(context).maybePop(); await databaseFunctions!.init(); await databaseFunctions! .createLandmark(title, loc, beacon!.id) diff --git a/lib/old/components/view_model/home_screen_view_model.dart b/lib/old/components/view_model/home_screen_view_model.dart index 7b769f4..9aeff65 100644 --- a/lib/old/components/view_model/home_screen_view_model.dart +++ b/lib/old/components/view_model/home_screen_view_model.dart @@ -1,10 +1,11 @@ +import 'package:auto_route/auto_route.dart'; import 'package:beacon/old/components/enums/view_state.dart'; import 'package:beacon/locator.dart'; import 'package:beacon/old/components/view_model/base_view_model.dart'; +import 'package:beacon/router.dart'; import 'package:flutter/material.dart'; import '../models/group/group.dart'; -import '../views/group_screen.dart'; class HomeViewModel extends BaseModel { final formKeyCreate = GlobalKey(); @@ -14,11 +15,12 @@ class HomeViewModel extends BaseModel { bool isCreatingGroup = false; String? enteredGroupCode; - createGroupRoom() async { + createGroupRoom(BuildContext context) async { FocusScope.of(navigationService!.navigatorKey.currentContext!).unfocus(); validate = AutovalidateMode.always; if (formKeyCreate.currentState!.validate()) { - navigationService!.pop(); + // navigationService!.pop(); + AutoRouter.of(context).maybePop(); setState(ViewState.busy); validate = AutovalidateMode.disabled; databaseFunctions!.init(); @@ -26,10 +28,12 @@ class HomeViewModel extends BaseModel { title, ); if (group != null) { - navigationService!.pushScreen('/groupScreen', - arguments: GroupScreen( - group, - )); + // navigationService!.pushScreen('/groupScreen', + // arguments: GroupScreen( + // group, + // )); + + AutoRouter.of(context).pushNamed('/group'); } } else { // navigationService!.showSnackBar('Something went wrong'); @@ -37,8 +41,8 @@ class HomeViewModel extends BaseModel { } } - joinGroupRoom() async { - FocusScope.of(navigationService!.navigatorKey.currentContext!).unfocus(); + joinGroupRoom(BuildContext context) async { + // FocusScope.of(navigationService!.navigatorKey.currentContext!).unfocus(); validate = AutovalidateMode.always; if (formKeyJoin.currentState!.validate()) { setState(ViewState.busy); @@ -47,10 +51,12 @@ class HomeViewModel extends BaseModel { final Group? group = await databaseFunctions!.joinGroup(enteredGroupCode); // setState(ViewState.idle); if (group != null) { - navigationService!.pushScreen('/groupScreen', - arguments: GroupScreen( - group, - )); + // navigationService!.pushScreen('/groupScreen', + // arguments: GroupScreen( + // group, + // )); + + AutoRouter.of(context).pushNamed('/group'); } else { //there was some error, go back to homescreen. setState(ViewState.idle); @@ -61,12 +67,18 @@ class HomeViewModel extends BaseModel { } } - logout() async { + logout(BuildContext context) async { setState(ViewState.busy); await userConfig!.currentUser!.delete(); await hiveDb!.beaconsBox.clear(); // setState(ViewState.idle); await localNotif!.deleteNotification(); - navigationService!.removeAllAndPush('/auth', '/'); + // navigationService!.removeAllAndPush('/auth', '/'); + AutoRouter.of(context).pushAndPopUntil( + AuthScreenRoute(), + predicate: (route) { + return true; + }, + ); } } diff --git a/lib/old/components/views/group_screen.dart b/lib/old/components/views/group_screen.dart deleted file mode 100644 index fecb5b6..0000000 --- a/lib/old/components/views/group_screen.dart +++ /dev/null @@ -1,419 +0,0 @@ -import 'package:auto_route/auto_route.dart'; -import 'package:beacon/old/components/beacon_card.dart'; -import 'package:beacon/old/components/create_join_dialog.dart'; -import 'package:beacon/old/components/hike_button.dart'; -import 'package:beacon/old/components/loading_screen.dart'; -import 'package:beacon/old/components/shape_painter.dart'; -import 'package:beacon/locator.dart'; -import 'package:beacon/old/components/models/beacon/beacon.dart'; -import 'package:beacon/old/components/utilities/constants.dart'; -import 'package:beacon/old/components/view_model/group_screen_view_model.dart'; -import 'package:beacon/old/components/views/base_view.dart'; -import 'package:flutter/material.dart'; -import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; -import 'package:sizer/sizer.dart'; -import '../models/group/group.dart'; - -@RoutePage() -class GroupScreen extends StatefulWidget { - final Group group; - GroupScreen(this.group); - - @override - _GroupScreenState createState() => _GroupScreenState(); -} - -class _GroupScreenState extends State - with TickerProviderStateMixin { - late List fetchingUserBeacons; - late List fetchingNearbyBeacons; - - @override - void initState() { - super.initState(); - } - - fetchUserBeacons() async { - return await databaseFunctions!.fetchUserBeacons(widget.group.id); - } - - fetchNearByBeacons() async { - return await databaseFunctions!.fetchNearbyBeacon(widget.group.id); - } - - reloadList() async { - fetchingUserBeacons = - await databaseFunctions!.fetchUserBeacons(widget.group.id); - fetchingNearbyBeacons = await databaseFunctions! - .fetchNearbyBeacon(widget.group.id) as List; - setState(() {}); - } - - @override - Widget build(BuildContext context) { - return BaseView(builder: (context, model, child) { - TabController tabController = new TabController(length: 2, vsync: this); - return model.isBusy - ? LoadingScreen() - : Scaffold( - resizeToAvoidBottomInset: false, - body: SafeArea( - child: ModalProgressHUD( - inAsyncCall: model.isCreatingHike, - child: Stack( - children: [ - CustomPaint( - size: Size(MediaQuery.of(context).size.width, - MediaQuery.of(context).size.height - 200), - painter: ShapePainter(), - ), - // Creating a back button - // Align( - // alignment: Alignment(-0.9, -0.8), - // child: FloatingActionButton( - // onPressed: () => navigationService.pop(), - // backgroundColor: kYellow, - // child: Icon(Icons.arrow_back_rounded), - // ), - // ), - Align( - alignment: Alignment(-0.7, -0.95), - child: Container( - width: MediaQuery.of(context).size.width * 0.6, - child: Text( - 'Welcome to Group ' + widget.group.title!, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 25, - color: Colors.white, - ), - ), - ), - ), - Align( - alignment: Alignment(0.9, -0.8), - child: FloatingActionButton( - onPressed: () => showDialog( - context: context, - builder: (context) => AlertDialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10.0), - ), - // actionsAlignment: - // MainAxisAlignment.spaceEvenly, - title: Text( - userConfig!.currentUser!.isGuest! - ? 'Create Account' - : 'Logout', - style: TextStyle( - fontSize: 25, color: kYellow), - ), - content: Text( - userConfig!.currentUser!.isGuest! - ? 'Would you like to create an account?' - : 'Are you sure you wanna logout?', - style: TextStyle( - fontSize: 16, color: kBlack), - ), - actions: [ - HikeButton( - buttonHeight: 2.5.h, - buttonWidth: 8.w, - onTap: () => - Navigator.of(context).pop(false), - text: 'No', - textSize: 18.0, - ), - HikeButton( - buttonHeight: 2.5.h, - buttonWidth: 8.w, - onTap: () { - navigationService!.pop(); - model.logout(); - }, - text: 'Yes', - textSize: 18.0, - ), - ], - )), - backgroundColor: kYellow, - child: userConfig!.currentUser!.isGuest! - ? Icon(Icons.person) - : Icon(Icons.logout), - ), - ), - Padding( - padding: EdgeInsets.fromLTRB(4.w, 25.h, 4.w, 5), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - width: 45.w, - child: HikeButton( - buttonWidth: homebwidth, - buttonHeight: homebheight - 2, - text: 'Create Hike', - textColor: Colors.white, - borderColor: Colors.white, - buttonColor: kYellow, - onTap: () { - if (userConfig!.currentUser!.isGuest!) { - // navigationService!.showSnackBar( - // 'You need to login with credentials to start a hike'); - } else { - CreateJoinBeaconDialog.createHikeDialog( - context, - model, - reloadList, - widget.group.id); - } - }, - ), - ), - SizedBox( - width: 1.w, - ), - Container( - width: 45.w, - child: HikeButton( - buttonWidth: homebwidth, - buttonHeight: homebheight - 2, - text: 'Join a Hike', - textColor: kYellow, - borderColor: kYellow, - buttonColor: Colors.white, - onTap: () async { - CreateJoinBeaconDialog.joinBeaconDialog( - context, model, reloadList); - }, - ), - ), - ], - ), - ), - Column( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - height: MediaQuery.of(context).size.height * 0.565, - margin: EdgeInsets.only(top: 20), - decoration: BoxDecoration( - color: kLightBlue, - borderRadius: BorderRadius.only( - topLeft: const Radius.circular(50.0), - topRight: const Radius.circular(50.0), - ), - ), - child: Column( - children: [ - TabBar( - indicatorSize: TabBarIndicatorSize.tab, - indicatorColor: kBlue, - labelColor: kBlack, - tabs: [ - Tab(text: 'Your Beacons'), - Tab(text: 'Nearby Beacons'), - ], - controller: tabController, - ), - Expanded( - child: TabBarView( - controller: tabController, - children: [ - Padding( - padding: const EdgeInsets.all(12.0), - child: FutureBuilder( - future: fetchUserBeacons(), - builder: (context, snapshot) { - if (snapshot.connectionState == - ConnectionState.done) { - if (snapshot.hasError) { - return Center( - child: Text( - snapshot.error.toString(), - textAlign: TextAlign.center, - textScaler: - TextScaler.linear(1.3), - ), - ); - } - final List posts = - snapshot.data - as List; - return Container( - alignment: Alignment.center, - child: posts.length == 0 - ? SingleChildScrollView( - physics: - AlwaysScrollableScrollPhysics(), - child: Column( - children: [ - Text( - 'You haven\'t joined or created any beacon yet', - textAlign: - TextAlign - .center, - style: TextStyle( - color: - kBlack, - fontSize: - 20), - ), - SizedBox( - height: 2.h, - ), - RichText( - text: TextSpan( - // textAlign: - // TextAlign - // .center, - style: TextStyle( - color: - kBlack, - fontSize: - 20), - children: [ - TextSpan( - text: - 'Join', - style: TextStyle( - fontWeight: - FontWeight.bold)), - TextSpan( - text: - ' a Hike or '), - TextSpan( - text: - 'Create', - style: TextStyle( - fontWeight: - FontWeight.bold)), - TextSpan( - text: - ' a new one! '), - ], - ), - ), - ], - ), - ) - : ListView.builder( - physics: - AlwaysScrollableScrollPhysics(), - scrollDirection: - Axis.vertical, - itemCount: - posts.length, - padding: - EdgeInsets.all(8), - itemBuilder: - (context, index) { - return BeaconCustomWidgets - .getBeaconCard( - context, - posts[ - index]!); - }, - )); - } else { - return Center( - child: BeaconCustomWidgets - .getPlaceholder(), - ); - } - }, - ), - ), - Padding( - padding: const EdgeInsets.all(12.0), - child: Container( - alignment: Alignment.center, - child: FutureBuilder( - future: fetchNearByBeacons(), - builder: (context, snapshot) { - if (snapshot.connectionState == - ConnectionState.waiting) - return Center( - child: BeaconCustomWidgets - .getPlaceholder(), - ); - if (snapshot.connectionState == - ConnectionState.done) { - if (snapshot.hasError) { - return Center( - child: Text( - snapshot.error.toString(), - textAlign: - TextAlign.center, - textScaler: - TextScaler.linear( - 1.3), - ), - ); - } - - final posts = snapshot.data - as List; - if (posts.length == 0) { - return SingleChildScrollView( - physics: - AlwaysScrollableScrollPhysics(), - child: Center( - child: Text( - 'No nearby beacons found :(', - style: TextStyle( - color: kBlack, - fontSize: 20), - ), - ), - ); - } - return ListView.builder( - physics: - AlwaysScrollableScrollPhysics(), - scrollDirection: - Axis.vertical, - itemCount: posts.length, - padding: EdgeInsets.all(8), - itemBuilder: - (context, index) { - return BeaconCustomWidgets - .getBeaconCard(context, - posts[index]); - }, - ); - } else { - return SingleChildScrollView( - physics: - AlwaysScrollableScrollPhysics(), - child: Center( - child: Text( - 'No nearby beacons found :(', - style: TextStyle( - color: kBlack, - fontSize: 18))), - ); - } - }, - ), - ), - ), - ], - ), - ), - ], - ), - ), - ], - ), - ], - ), - ), - ), - ); - }); - } -} diff --git a/lib/old/components/views/home_screen.dart b/lib/old/components/views/home_screen.dart deleted file mode 100644 index 499cd63..0000000 --- a/lib/old/components/views/home_screen.dart +++ /dev/null @@ -1,342 +0,0 @@ -import 'package:auto_route/auto_route.dart'; -import 'package:beacon/old/components/beacon_card.dart'; -import 'package:beacon/old/components/create_join_dialog.dart'; -import 'package:beacon/old/components/hike_button.dart'; -import 'package:beacon/old/components/loading_screen.dart'; -import 'package:beacon/old/components/shape_painter.dart'; -import 'package:beacon/locator.dart'; -import 'package:beacon/old/components/models/group/group.dart'; -import 'package:beacon/old/components/utilities/constants.dart'; -import 'package:beacon/old/components/views/base_view.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; -// import 'package:modal_progress_hud/modal_progress_hud.dart'; -import 'package:sizer/sizer.dart'; - -import '../group_card.dart'; -import '../view_model/home_screen_view_model.dart'; - -@RoutePage() -class HomeScreen extends StatefulWidget { - const HomeScreen({super.key}); - @override - HomeScreenState createState() => HomeScreenState(); -} - -class HomeScreenState extends State with TickerProviderStateMixin { - var fetchingUserGroups; - Future _onPopHome() async { - return showDialog( - context: context, - builder: (context) => AlertDialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10.0), - ), - // actionsAlignment: MainAxisAlignment.spaceEvenly, - contentPadding: EdgeInsets.all(25.0), - title: Text( - 'Confirm Exit', - style: TextStyle(fontSize: 25, color: kYellow), - ), - content: Text( - 'Do you really want to exit?', - style: TextStyle(fontSize: 18, color: kBlack), - ), - actions: [ - HikeButton( - buttonHeight: 2.5.h, - buttonWidth: 8.w, - onTap: () => Navigator.of(context).pop(false), - text: 'No', - ), - HikeButton( - buttonHeight: 2.5.h, - buttonWidth: 8.w, - onTap: () => - SystemChannels.platform.invokeMethod('SystemNavigator.pop'), - text: 'Yes', - ), - ], - ), - ); - } - - @override - void initState() { - fetchingUserGroups = databaseFunctions!.fetchUserGroups(); - super.initState(); - } - - void reloadList() { - setState(() { - fetchingUserGroups = databaseFunctions!.fetchUserGroups(); - }); - } - - @override - Widget build(BuildContext context) { - return PopScope(onPopInvoked: (didPop) { - _onPopHome(); - }, child: BaseView(builder: (context, model, child) { - TabController tabController = new TabController(length: 1, vsync: this); - return model.isBusy - ? LoadingScreen() - : Scaffold( - resizeToAvoidBottomInset: false, - body: SafeArea( - child: ModalProgressHUD( - inAsyncCall: model.isCreatingGroup, - child: Stack( - children: [ - CustomPaint( - size: Size(MediaQuery.of(context).size.width, - MediaQuery.of(context).size.height - 200), - painter: ShapePainter(), - ), - Align( - alignment: Alignment(0.9, -0.8), - child: FloatingActionButton( - onPressed: () => showDialog( - context: context, - builder: (context) => AlertDialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10.0), - ), - // actionsAlignment: - // MainAxisAlignment.spaceEvenly, - title: Text( - userConfig!.currentUser!.isGuest! - ? 'Create Account' - : 'Logout', - style: TextStyle( - fontSize: 25, color: kYellow), - ), - content: Text( - userConfig!.currentUser!.isGuest! - ? 'Would you like to create an account?' - : 'Are you sure you wanna logout?', - style: TextStyle( - fontSize: 16, color: kBlack), - ), - actions: [ - HikeButton( - buttonHeight: 2.5.h, - buttonWidth: 8.w, - onTap: () => - Navigator.of(context).pop(false), - text: 'No', - textSize: 18.0, - ), - HikeButton( - buttonHeight: 2.5.h, - buttonWidth: 8.w, - onTap: () { - navigationService!.pop(); - model.logout(); - }, - text: 'Yes', - textSize: 18.0, - ), - ], - )), - backgroundColor: kYellow, - child: userConfig!.currentUser!.isGuest! - ? Icon(Icons.person) - : Icon(Icons.logout), - ), - ), - Padding( - padding: EdgeInsets.fromLTRB(4.w, 25.h, 4.w, 5), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - width: 45.w, - child: HikeButton( - buttonWidth: homebwidth - 10, - buttonHeight: homebheight - 2, - text: 'Create Group', - textColor: Colors.white, - borderColor: Colors.white, - buttonColor: kYellow, - onTap: () { - if (userConfig!.currentUser!.isGuest!) { - // navigationService!.showSnackBar( - // 'You need to login with credentials to be able to create a group'); - } else { - CreateJoinGroupDialog.createGroupDialog( - context, model); - } - }, - ), - ), - SizedBox( - width: 1.w, - ), - Container( - width: 45.w, - child: HikeButton( - buttonWidth: homebwidth, - buttonHeight: homebheight - 2, - text: 'Join a Group', - textColor: kYellow, - borderColor: kYellow, - buttonColor: Colors.white, - onTap: () async { - CreateJoinGroupDialog.joinGroupDialog( - context, model); - }, - ), - ), - ], - ), - ), - Column( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - height: MediaQuery.of(context).size.height * 0.565, - margin: EdgeInsets.only(top: 20), - decoration: BoxDecoration( - color: kLightBlue, - borderRadius: BorderRadius.only( - topLeft: const Radius.circular(50.0), - topRight: const Radius.circular(50.0))), - child: Column( - children: [ - TabBar( - indicatorSize: TabBarIndicatorSize.tab, - indicatorColor: kBlue, - labelColor: kBlack, - tabs: [ - Tab(text: 'Your Groups'), - ], - controller: tabController, - ), - Expanded( - child: TabBarView( - controller: tabController, - children: [ - Padding( - padding: const EdgeInsets.all(12.0), - child: FutureBuilder( - future: fetchingUserGroups, - builder: (context, snapshot) { - if (snapshot.connectionState == - ConnectionState.done) { - if (snapshot.hasError) { - return Center( - child: Text( - snapshot.error.toString(), - textAlign: TextAlign.center, - textScaler: - TextScaler.linear(1.3), - ), - ); - } - final List posts = - snapshot.data as List; - return Container( - alignment: Alignment.center, - child: posts.length == 0 - ? SingleChildScrollView( - physics: - AlwaysScrollableScrollPhysics(), - child: Column( - children: [ - Text( - 'You haven\'t joined or created any group yet', - textAlign: - TextAlign - .center, - style: TextStyle( - color: - kBlack, - fontSize: - 20), - ), - SizedBox( - height: 2.h, - ), - RichText( - text: TextSpan( - // textAlign: - // TextAlign - // .center, - style: TextStyle( - color: - kBlack, - fontSize: - 20), - children: [ - TextSpan( - text: - 'Join', - style: TextStyle( - fontWeight: - FontWeight.bold)), - TextSpan( - text: - ' a Group or '), - TextSpan( - text: - 'Create', - style: TextStyle( - fontWeight: - FontWeight.bold)), - TextSpan( - text: - ' a new one! '), - ], - ), - ), - ], - ), - ) - : ListView.builder( - physics: - AlwaysScrollableScrollPhysics(), - scrollDirection: - Axis.vertical, - itemCount: - posts.length, - padding: - EdgeInsets.all(8), - itemBuilder: - (context, index) { - return GroupCustomWidgets - .getGroupCard( - context, - posts[ - index]); - }, - )); - } else { - return Center( - child: BeaconCustomWidgets - .getPlaceholder(), - ); - } - }, - ), - ), - ], - ), - ), - ], - ), - ), - ], - ), - ], - ), - ), - ), - ); - })); - } -} diff --git a/lib/router.dart b/lib/router.dart index 5db844c..601b617 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -1,13 +1,13 @@ import 'package:auto_route/auto_route.dart'; +import 'package:beacon/Bloc/domain/entities/group/group_entity.dart'; +import 'package:beacon/Bloc/presentation/screens/splash_screen.dart'; import 'package:beacon/old/components/models/beacon/beacon.dart'; -import 'package:beacon/old/components/models/group/group.dart'; -import 'package:beacon/splash_screen.dart'; -import 'package:beacon/old/components/views/home_screen.dart'; +import 'package:beacon/Bloc/presentation/screens/home_screen.dart'; import 'package:flutter/material.dart'; import 'package:beacon/old/components/utilities/constants.dart'; -import 'package:beacon/old/components/views/auth_screen.dart'; -import 'package:beacon/old/components/views/group_screen.dart'; -import 'package:beacon/old/components/views/hike_screen.dart'; +import 'package:beacon/Bloc/presentation/screens/auth_screen.dart'; +import 'package:beacon/Bloc/presentation/screens/group_screen.dart'; +import 'package:beacon/Bloc/presentation/screens/hike_screen.dart'; part 'router.gr.dart'; Route generateRoute(RouteSettings settings) { @@ -17,7 +17,7 @@ Route generateRoute(RouteSettings settings) { builder: (context) => const AuthScreen(key: Key('auth'))); case Routes.mainScreen: return MaterialPageRoute( - builder: (context) => const HomeScreen(key: Key('MainScreen'))); + builder: (context) => HomeScreen(key: Key('MainScreen'))); case Routes.hikeScreen: HikeScreen? arguments = settings.arguments as HikeScreen?; return MaterialPageRoute( @@ -41,10 +41,13 @@ Route generateRoute(RouteSettings settings) { class AppRouter extends _$AppRouter { @override List get routes => [ - AutoRoute(page: SplashScreenRoute.page, initial: true), - AutoRoute(page: AuthScreenRoute.page), - AutoRoute(page: HomeScreenRoute.page), - AutoRoute(page: HikeScreenRoute.page), - AutoRoute(page: GroupScreenRoute.page), + AutoRoute(page: SplashScreenRoute.page, initial: true, path: '/'), + AutoRoute(page: AuthScreenRoute.page, path: '/auth'), + AutoRoute(page: HomeScreenRoute.page, path: '/home'), + AutoRoute( + page: HikeScreenRoute.page, + path: '/hike', + ), + AutoRoute(page: GroupScreenRoute.page, path: '/group'), ]; } diff --git a/lib/router.gr.dart b/lib/router.gr.dart index cb88f99..bac583f 100644 --- a/lib/router.gr.dart +++ b/lib/router.gr.dart @@ -71,7 +71,7 @@ class AuthScreenRoute extends PageRouteInfo { /// [GroupScreen] class GroupScreenRoute extends PageRouteInfo { GroupScreenRoute({ - required Group group, + required GroupEntity group, List? children, }) : super( GroupScreenRoute.name, @@ -88,7 +88,7 @@ class GroupScreenRoute extends PageRouteInfo { class GroupScreenRouteArgs { const GroupScreenRouteArgs({required this.group}); - final Group group; + final GroupEntity group; @override String toString() { diff --git a/lib/splash_screen.dart b/lib/splash_screen.dart deleted file mode 100644 index 1159d97..0000000 --- a/lib/splash_screen.dart +++ /dev/null @@ -1,101 +0,0 @@ -import 'dart:async'; -import 'package:auto_route/auto_route.dart'; -import 'package:beacon/old/components/views/hike_screen.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:beacon/locator.dart'; -import 'package:uni_links/uni_links.dart'; - -import 'old/components/loading_screen.dart'; - -@RoutePage() -class SplashScreen extends StatefulWidget { - const SplashScreen({super.key}); - - @override - _SplashScreenState createState() => _SplashScreenState(); -} - -class _SplashScreenState extends State { - Uri? _initialUri; - Uri? _latestUri; - late StreamSubscription _sub; - bool isCheckingUrl = false; - - Future _handleInitialUri() async { - _sub = uriLinkStream.listen((Uri? uri) { - if (!mounted) return; - setState(() { - _latestUri = uri; - }); - }, onError: (Object err) { - if (!mounted) return; - setState(() { - _latestUri = null; - }); - }); - try { - final uri = await getInitialUri(); - if (!mounted) return; - setState(() => _initialUri = uri); - } on PlatformException { - if (!mounted) return; - setState(() => _initialUri = null); - } on FormatException catch (err) { - debugPrint(err.toString()); - if (!mounted) return; - setState(() => _initialUri = null); - } - await databaseFunctions!.init(); - await userConfig!.userLoggedIn().then((value) async { - if (_latestUri == null && _initialUri == null) { - if (value || hiveDb!.currentUserBox.containsKey('user')) { - navigationService!.pushReplacementScreen('/main'); - } else { - navigationService!.pushReplacementScreen('/auth'); - } - } else { - if (_initialUri != null) { - var shortcode = _initialUri!.queryParameters['shortcode']; - if (value) { - await databaseFunctions!.joinBeacon(shortcode).then((val) { - if (val != null) { - navigationService!.pushScreen('/hikeScreen', - arguments: HikeScreen(val, isLeader: false)); - } else { - navigationService!.pushReplacementScreen('/main'); - } - }); - } else { - // login in anonymously and join hike - await databaseFunctions!.signup(name: "Anonymous"); - await databaseFunctions!.joinBeacon(shortcode).then((val) async { - navigationService!.pushScreen('/hikeScreen', - arguments: HikeScreen(val, isLeader: false)); - }); - } - } - } - }); - } - - @override - void initState() { - _handleInitialUri(); - super.initState(); - } - - @override - void dispose() { - _sub.cancel(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - key: const Key('SplashScreenScaffold'), - body: LoadingScreen(), - ); - } -} diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 02c0949..7e4f3ec 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -63,10 +63,10 @@ SPEC CHECKSUMS: geolocator_apple: 72a78ae3f3e4ec0db62117bd93e34523f5011d58 location: 7cdb0665bd6577d382b0a343acdadbcb7f964775 modal_progress_hud_nsn: 8099d46c2cf9de7af8fe0a3f8f5d2aa32cf956c3 - path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 + path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7 - shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126 + shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695 PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 4e2a377..36528b7 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -259,7 +259,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 331C80D4294CF70F00263BE5 = { diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 23349ce..6099c63 100644 --- a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ =3.2.3 <4.0.0" - flutter: ">=3.16.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.19.0" diff --git a/pubspec.yaml b/pubspec.yaml index 3328aee..9844c92 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -22,7 +22,7 @@ dependencies: flutter_polyline_points: ^1.0.0 flutter_spinkit: ^5.2.0 fluttertoast: ^8.2.4 - geolocator: ^10.1.0 + geolocator: any get_it: ^7.6.4 google_maps_flutter: ^2.5.3 graphql_flutter: ^5.1.0 @@ -51,6 +51,10 @@ dependencies: # for state management bloc: ^8.1.4 flutter_bloc: ^8.1.5 + freezed_annotation: ^2.4.1 + json_annotation: ^4.9.0 + dev: ^1.0.0 + json_serializable: ^6.8.0 @@ -62,6 +66,7 @@ dev_dependencies: flutter_launcher_icons: ^0.9.3 flutter_test: sdk: flutter + freezed: ^2.5.2 hive_generator: test: ^1.16.5