From 24058e2fc3555378b4101d85776eb7a1b2f00b81 Mon Sep 17 00:00:00 2001 From: Radhika Saliya <74301808+cp-radhika-s@users.noreply.github.com> Date: Fri, 20 Dec 2024 18:39:32 +0530 Subject: [PATCH] Fix map and home screen issues (#173) --- app/lib/main.dart | 8 +- .../ui/components/user_battery_status.dart | 6 +- app/lib/ui/flow/home/home_screen.dart | 16 +- .../ui/flow/home/home_screen_viewmodel.dart | 136 +++++++--- .../selected_member_detail_view.dart | 61 +++-- .../map/components/space_user_footer.dart | 12 +- app/lib/ui/flow/home/map/map_screen.dart | 82 +++--- app/lib/ui/flow/home/map/map_view_model.dart | 213 +++++++++------ .../flow/home/map/map_view_model.freezed.dart | 250 +++++++++--------- app/lib/ui/flow/intro/intro_screen.dart | 2 +- .../flow/setting/profile/profile_screen.dart | 2 +- .../setting/profile/profile_view_model.dart | 8 +- app/lib/ui/flow/setting/setting_screen.dart | 2 +- .../ui/flow/setting/setting_view_model.dart | 3 +- .../api/message/message_models.freezed.dart | 65 ++++- data/lib/repository/geofence_repository.dart | 21 -- data/lib/service/auth_service.dart | 32 +-- data/lib/service/location_manager.dart | 24 +- data/lib/service/space_service.dart | 13 +- 19 files changed, 561 insertions(+), 395 deletions(-) diff --git a/app/lib/main.dart b/app/lib/main.dart index 6f2be80a..5447de29 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -40,8 +40,8 @@ void main() async { if (Platform.isAndroid) _configureService(); - if (await Permission.location.isGranted) { - await locationMethodChannel.invokeMethod('startTracking'); + if (await Permission.location.isGranted && Platform.isIOS) { + await locationMethodChannel.invokeMethod('startTracking'); } locationMethodChannel.setMethodCallHandler(_handleLocationUpdates); @@ -111,7 +111,9 @@ Future _handleLocationUpdates(MethodCall call) async { locationData['timestamp'].toInt()), ); - await LocationManager.instance.updateUserLocation(locationPosition); + if (locationPosition.latitude != 0 && locationPosition.longitude != 0) { + await LocationManager.instance.updateUserLocation(locationPosition); + } } } diff --git a/app/lib/ui/components/user_battery_status.dart b/app/lib/ui/components/user_battery_status.dart index 27123841..9952eec1 100644 --- a/app/lib/ui/components/user_battery_status.dart +++ b/app/lib/ui/components/user_battery_status.dart @@ -8,13 +8,13 @@ import 'package:style/text/app_text_dart.dart'; import '../../gen/assets.gen.dart'; class UserBatteryStatus extends StatelessWidget { - final ApiUserInfo userInfo; + final ApiUser user; - const UserBatteryStatus({super.key, required this.userInfo}); + const UserBatteryStatus({super.key, required this.user}); @override Widget build(BuildContext context) { - final batteryPct = userInfo.user.battery_pct ?? 0; + final batteryPct = user.battery_pct ?? 0; String icon; Color color; diff --git a/app/lib/ui/flow/home/home_screen.dart b/app/lib/ui/flow/home/home_screen.dart index 1e1e33b9..38371c36 100644 --- a/app/lib/ui/flow/home/home_screen.dart +++ b/app/lib/ui/flow/home/home_screen.dart @@ -45,8 +45,6 @@ class _HomeScreenState extends ConsumerState { notifier = ref.watch(homeViewStateProvider.notifier); - geofenceRepository = ref.read(geofenceRepositoryProvider); - geofenceRepository.init(); GeofenceService.setGeofenceCallback( onEnter: (id) => geofenceRepository.makeHttpCall(id, GEOFENCE_ENTER), onExit: (id) => geofenceRepository.makeHttpCall(id, GEOFENCE_EXIT), @@ -71,7 +69,6 @@ class _HomeScreenState extends ConsumerState { final state = ref.watch(homeViewStateProvider); _observeNavigation(state); _observeError(); - _observeSelectedSpace(); if (Platform.isAndroid) _observeShowBatteryDialog(context); _observeSessionExpiredAlertPopup(context); _observeSessionExpired(); @@ -93,7 +90,7 @@ class _HomeScreenState extends ConsumerState { Widget _body(BuildContext context, HomeViewState state) { if (state.isNetworkOff) { return NoInternetScreen(onPressed: () { - notifier.setDate(); + notifier.fetchData(); }); } @@ -143,15 +140,6 @@ class _HomeScreenState extends ConsumerState { }); } - void _observeSelectedSpace() { - ref.listen(homeViewStateProvider.select((state) => state.selectedSpace), - (previous, next) { - if (previous?.space.id != next?.space.id) { - mapNotifier.loadData(next?.space.id); - } - }); - } - void _observeShowBatteryDialog(BuildContext context) { ref.listen(homeViewStateProvider.select((state) => state.showBatteryDialog), (_, next) { @@ -198,7 +186,7 @@ class _HomeScreenState extends ConsumerState { ref.listen(homeViewStateProvider.select((state) => state.popToSignIn), (_, next) { if (next != null) { - AppRoute.signInMethod.push(context); + AppRoute.signInMethod.go(context); } }); } diff --git a/app/lib/ui/flow/home/home_screen_viewmodel.dart b/app/lib/ui/flow/home/home_screen_viewmodel.dart index 1271f36e..b1b1bb84 100644 --- a/app/lib/ui/flow/home/home_screen_viewmodel.dart +++ b/app/lib/ui/flow/home/home_screen_viewmodel.dart @@ -1,28 +1,26 @@ import 'dart:async'; -import 'dart:io'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:data/api/auth/api_user_service.dart'; import 'package:data/api/auth/auth_models.dart'; -import 'package:data/api/location/location.dart'; +import 'package:data/api/place/api_place.dart'; import 'package:data/api/space/space_models.dart'; import 'package:data/log/logger.dart'; +import 'package:data/service/geofence_service.dart'; import 'package:data/service/permission_service.dart'; import 'package:data/service/space_service.dart'; import 'package:data/storage/app_preferences.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:geolocator/geolocator.dart'; import '../../components/no_internet_screen.dart'; part 'home_screen_viewmodel.freezed.dart'; final homeViewStateProvider = - StateNotifierProvider.autoDispose( - (ref) => HomeViewNotifier( + StateNotifierProvider.autoDispose((ref) { + final notifier = HomeViewNotifier( ref.read(spaceServiceProvider), ref.read(currentSpaceId.notifier), ref.read(permissionServiceProvider), @@ -30,15 +28,24 @@ final homeViewStateProvider = ref.read(currentUserPod), ref.read(apiUserServiceProvider), ref.read(currentUserSessionPod), - ), -); + ); + ref.listen(currentUserPod, (prev, user) { + notifier._onUpdateUser(prevUser: prev, currentUser: user); + }); + + ref.listen(currentSpaceId, (prev, newSpaceId) { + notifier._onUpdateSpace(prev: prev, current: newSpaceId); + }); + + return notifier; +}); class HomeViewNotifier extends StateNotifier { final SpaceService spaceService; final PermissionService permissionService; final StateController _currentSpaceIdController; final StateController _lastBatteryDialogDate; - final ApiUser? _currentUser; + ApiUser? _currentUser; final ApiUserService userService; final ApiSession? _userSession; @@ -51,48 +58,87 @@ class HomeViewNotifier extends StateNotifier { this.userService, this._userSession, ) : super(const HomeViewState()) { - setDate(); + fetchData(); + _listenPlaces(); } StreamSubscription>? _spacesSubscription; + StreamSubscription? _userSessionSubscription; + StreamSubscription?>? _spacePlacesSubscription; String? get currentSpaceId => _currentSpaceIdController.state; - void setDate() async { + void fetchData() async { final isNetworkOff = await _checkUserInternet(); if (isNetworkOff) return; listenSpaceMember(); updateCurrentUserNetworkState(); - if (_currentUser == null && _userSession == null) return; - listenUserSession(_currentUser!.id, _userSession!.id); + listenUserSession(); } - Future currentUserLocation() async { - if (Platform.isIOS) { - const platform = MethodChannel('com.grouptrack/current_location'); - final locationFromIOS = await platform.invokeMethod('getCurrentLocation'); - return LocationData( - latitude: locationFromIOS['latitude'], - longitude: locationFromIOS['longitude'], - timestamp: DateTime.fromMillisecondsSinceEpoch( - locationFromIOS['timestamp'].toInt()), - ); - } else { - var location = await Geolocator.getCurrentPosition(); - return LocationData( - latitude: location.latitude, - longitude: location.longitude, - timestamp: DateTime.now()); + void _listenPlaces() async { + if (_currentUser == null) return; + try { + _spacePlacesSubscription?.cancel(); + _spacePlacesSubscription = spaceService + .getStreamPlacesByUserId(_currentUser!.id) + .listen((places) { + if (places.isEmpty) { + logger.e('No places found for spaces.'); + return; + } + + GeofenceService.startMonitoring(places); + }); + } catch (error) { + logger.e('GeofenceRepository: error while get user space $error'); + } + } + + void _onUpdateSpace({String? prev, String? current}) { + if (current == null) { + _cancelSubscriptions(); + state = state.copyWith(selectedSpace: null); + fetchData(); + } else if (prev != current) { + state = state.copyWith( + selectedSpace: + state.spaceList.where((e) => e.space.id == current).firstOrNull); } } + void _onUpdateUser({ApiUser? prevUser, ApiUser? currentUser}) { + _currentUser = currentUser; + if (currentUser == null) { + _cancelSubscriptions(); + state = state.copyWith(spaceList: [], selectedSpace: null); + } else if (prevUser?.id != currentUser.id) { + fetchData(); + _listenPlaces(); + } + } + + void _cancelSubscriptions() { + _spacePlacesSubscription?.cancel(); + _spacesSubscription?.cancel(); + _userSessionSubscription?.cancel(); + } + void listenSpaceMember() async { - if (state.loading) return; + final userId = _currentUser?.id; + if (state.loading || userId == null) return; try { + _spacesSubscription?.cancel(); state = state.copyWith(loading: true); - _spacesSubscription = spaceService.streamAllSpace().listen((spaces) { + + _spacesSubscription = + spaceService.streamAllSpace(userId).listen((spaces) { + if ((currentSpaceId?.isEmpty ?? true) && spaces.isNotEmpty) { + spaceService.currentSpaceId = spaces.firstOrNull?.space.id; + } + if (spaces.isNotEmpty) { if (state.spaceList.length != spaces.length) { reorderSpaces(spaces); @@ -119,7 +165,7 @@ class HomeViewNotifier extends StateNotifier { try { var connectivityResult = await Connectivity().checkConnectivity(); final userState = await checkUserState(connectivityResult.first); - await userService.updateUserState(_currentUser.id, userState); + await userService.updateUserState(_currentUser!.id, userState); } catch (error, stack) { logger.e( 'HomeViewNotifier: error while update current user state', @@ -176,11 +222,6 @@ class HomeViewNotifier extends StateNotifier { state = state.copyWith(selectedSpace: selectedSpace); } } - if (currentSpaceId == null && sortedSpaces.isNotEmpty) { - _currentSpaceIdController.state = sortedSpaces.first.space.id; - updateSelectedSpace(sortedSpaces.first); - state = state.copyWith(selectedSpace: sortedSpaces.first); - } state = state.copyWith( selectedSpace: sortedSpaces.first, spaceList: sortedSpaces); } @@ -196,7 +237,8 @@ class HomeViewNotifier extends StateNotifier { ? _currentUser?.location_enabled ?? true : members.first.isLocationEnabled, ); - _currentSpaceIdController.state = space.space.id; + + spaceService.currentSpaceId = space.space.id; } } @@ -241,7 +283,7 @@ class HomeViewNotifier extends StateNotifier { state = state.copyWith(enablingLocation: true); await spaceService.enableLocation( currentSpaceId!, - _currentUser.id, + _currentUser!.id, isEnabled, ); state = state.copyWith( @@ -256,9 +298,15 @@ class HomeViewNotifier extends StateNotifier { } } - void listenUserSession(String userId, String sessionId) async { + void listenUserSession() async { + if (_currentUser == null && _userSession == null) return; try { - userService.getUserSessionByIdStream(userId, sessionId).listen((session) { + final userId = _currentUser!.id; + final sessionId = _userSession!.id; + _userSessionSubscription?.cancel(); + _userSessionSubscription = userService + .getUserSessionByIdStream(userId, sessionId) + .listen((session) { if (session != null && !session.session_active) { state = state.copyWith(isSessionExpired: true, error: null); } @@ -285,6 +333,12 @@ class HomeViewNotifier extends StateNotifier { if (isNetworkOff) _spacesSubscription?.cancel(); return isNetworkOff; } + + @override + void dispose() { + _cancelSubscriptions(); + super.dispose(); + } } @freezed diff --git a/app/lib/ui/flow/home/map/components/selected_member_detail_view.dart b/app/lib/ui/flow/home/map/components/selected_member_detail_view.dart index b925cea2..b6a3f9f2 100644 --- a/app/lib/ui/flow/home/map/components/selected_member_detail_view.dart +++ b/app/lib/ui/flow/home/map/components/selected_member_detail_view.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'package:data/api/auth/auth_models.dart'; -import 'package:data/api/location/location.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; @@ -17,13 +16,14 @@ import 'package:yourspace_flutter/domain/extenstions/lat_lng_extenstion.dart'; import 'package:yourspace_flutter/domain/extenstions/time_ago_extenstions.dart'; import 'package:yourspace_flutter/ui/app_route.dart'; import 'package:yourspace_flutter/ui/components/profile_picture.dart'; +import 'package:yourspace_flutter/ui/flow/home/map/map_view_model.dart'; import '../../../../../gen/assets.gen.dart'; import '../../../../components/user_battery_status.dart'; class SelectedMemberDetailView extends StatefulWidget { final int groupCreatedDate; - final ApiUserInfo? userInfo; + final MapUserInfo? userInfo; final void Function() onDismiss; final bool isCurrentUser; final LatLng currentUserLocation; @@ -49,7 +49,7 @@ class _SelectedMemberDetailViewState extends State { @override void initState() { super.initState(); - getAddressDebounced(widget.userInfo?.location); + getAddressDebounced(widget.userInfo?.latLng); } @override @@ -69,17 +69,17 @@ class _SelectedMemberDetailViewState extends State { address = ''; }); - getAddressDebounced(widget.userInfo?.location); + getAddressDebounced(widget.userInfo?.latLng); } } @override Widget build(BuildContext context) { final userInfo = widget.userInfo; - return userInfo != null ? _userDetailCardView(userInfo) : Container(); + return userInfo != null ? _userDetailCardView(userInfo.user) : Container(); } - Widget _userDetailCardView(ApiUserInfo userInfo) { + Widget _userDetailCardView(ApiUser user) { return Stack( alignment: Alignment.topCenter, children: [ @@ -93,9 +93,9 @@ class _SelectedMemberDetailViewState extends State { child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _userProfileView(userInfo), + _userProfileView(user), const SizedBox(width: 16), - Expanded(child: _userDetailView(userInfo)), + Expanded(child: _userDetailView(user)), ], ), ), @@ -132,43 +132,44 @@ class _SelectedMemberDetailViewState extends State { ); } - Widget _userProfileView(ApiUserInfo userInfo) { + Widget _userProfileView(ApiUser user) { return Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ ProfileImage( - profileImageUrl: userInfo.user.profile_image!, - firstLetter: userInfo.user.firstChar, + profileImageUrl: user.profile_image!, + firstLetter: user.firstChar, size: 48, backgroundColor: context.colorScheme.primary, ), const SizedBox(height: 2), - UserBatteryStatus(userInfo: userInfo) + UserBatteryStatus(user: user) ], ); } - Widget _userDetailView(ApiUserInfo userInfo) { - final (userState, textColor) = selectedUserState(userInfo.user); + Widget _userDetailView(ApiUser user) { + final (userState, textColor) = selectedUserState(user); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - userInfo.user.fullName, + user.fullName, style: AppTextStyle.subtitle2 .copyWith(color: context.colorScheme.textPrimary), ), const SizedBox(height: 4), Text(userState, style: AppTextStyle.caption.copyWith(color: textColor)), const SizedBox(height: 12), - _userAddressView(userInfo.location), + _userAddressView(), const SizedBox(height: 4), - _userTimeAgo(userInfo.location?.created_at ?? DateTime.now().millisecondsSinceEpoch) + _userTimeAgo(widget.userInfo?.updatedLocationAt ?? + DateTime.now().millisecondsSinceEpoch) ], ); } - Widget _userAddressView(ApiLocation? location) { + Widget _userAddressView() { return Text( address ?? '', style: AppTextStyle.body2.copyWith( @@ -182,7 +183,8 @@ class _SelectedMemberDetailViewState extends State { Widget _timeLineButtonView() { return IconPrimaryButton( onTap: () { - AppRoute.journeyTimeline(widget.userInfo!.user, widget.groupCreatedDate).push(context); + AppRoute.journeyTimeline(widget.userInfo!.user, widget.groupCreatedDate) + .push(context); }, icon: SvgPicture.asset( Assets.images.icTimeLineHistoryIcon, @@ -199,18 +201,24 @@ class _SelectedMemberDetailViewState extends State { onTap: () { if (widget.isCurrentUser) { final mapsLink = - 'https://www.google.com/maps/search/?api=1&query=${widget.userInfo!.location?.latitude},${widget.userInfo!.location?.longitude}'; + 'https://www.google.com/maps/search/?api=1&query=${widget.userInfo?.latitude},${widget.userInfo?.longitude}'; Share.share('Check out my location: $mapsLink'); } else { - final origin = '${widget.currentUserLocation.latitude}, ${widget.currentUserLocation.longitude}'; - final destination = '${widget.userInfo!.location?.latitude},${widget.userInfo!.location?.longitude}'; + final origin = + '${widget.currentUserLocation.latitude}, ${widget.currentUserLocation.longitude}'; + final destination = + '${widget.userInfo?.latitude},${widget.userInfo?.longitude}'; final mapsLink = 'https://www.google.com/maps/dir/?api=1&origin=$origin&destination=$destination'; launchUrl(Uri.parse(mapsLink), mode: LaunchMode.externalApplication); } }, icon: widget.isCurrentUser - ? Icon(CupertinoIcons.paperplane, color: context.colorScheme.textPrimary, size: 18,) + ? Icon( + CupertinoIcons.paperplane, + color: context.colorScheme.textPrimary, + size: 18, + ) : SvgPicture.asset( Assets.images.icShareTwoLocation, colorFilter: ColorFilter.mode( @@ -239,7 +247,7 @@ class _SelectedMemberDetailViewState extends State { ); } - void getAddressDebounced(ApiLocation? location) { + void getAddressDebounced(LatLng? location) { if (_debounce?.isActive ?? false) _debounce!.cancel(); _debounce = Timer(const Duration(milliseconds: 500), () { @@ -247,9 +255,8 @@ class _SelectedMemberDetailViewState extends State { }); } - void getAddress(ApiLocation? location) async { - if (location != null) { - final latLng = LatLng(location.latitude, location.longitude); + void getAddress(LatLng? latLng) async { + if (latLng != null) { final fetchedAddress = await latLng.getAddressFromLocation(); if (mounted) { diff --git a/app/lib/ui/flow/home/map/components/space_user_footer.dart b/app/lib/ui/flow/home/map/components/space_user_footer.dart index 01f2ab2d..31e998b7 100644 --- a/app/lib/ui/flow/home/map/components/space_user_footer.dart +++ b/app/lib/ui/flow/home/map/components/space_user_footer.dart @@ -1,4 +1,3 @@ -import 'package:data/api/auth/auth_models.dart'; import 'package:data/api/space/space_models.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; @@ -12,18 +11,19 @@ import 'package:yourspace_flutter/domain/extenstions/context_extenstions.dart'; import 'package:yourspace_flutter/ui/components/profile_picture.dart'; import '../../../../../gen/assets.gen.dart'; +import '../map_view_model.dart'; import 'selected_member_detail_view.dart'; class SpaceUserFooter extends StatefulWidget { final SpaceInfo? selectedSpace; - final List? members; - final ApiUserInfo? selectedUser; + final List? members; + final MapUserInfo? selectedUser; final bool isEnabled; final bool fetchingInviteCode; final bool isCurrentUser; final LatLng currentUserLocation; final void Function() onAddMemberTap; - final void Function(ApiUserInfo) onMemberTap; + final void Function(MapUserInfo) onMemberTap; final void Function() onRelocateTap; final void Function() onMapTypeTap; final void Function() onPlacesTap; @@ -162,7 +162,7 @@ class _SpaceUserFooterState extends State { Widget selectedSpaceMemberView({ required BuildContext context, - required List? members, + required List? members, }) { if (members == null || members.isEmpty) return Container(); return Container( @@ -239,7 +239,7 @@ class _SpaceUserFooterState extends State { Widget spaceMemberItem( BuildContext context, - ApiUserInfo userInfo, + MapUserInfo userInfo, ) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 6), diff --git a/app/lib/ui/flow/home/map/map_screen.dart b/app/lib/ui/flow/home/map/map_screen.dart index 52fffbb5..24e5077d 100644 --- a/app/lib/ui/flow/home/map/map_screen.dart +++ b/app/lib/ui/flow/home/map/map_screen.dart @@ -18,6 +18,7 @@ import '../../../../gen/assets.gen.dart'; import '../../../app_route.dart'; import '../../../components/action_bottom_sheet.dart'; import '../../../components/permission_dialog.dart'; +import '../../permission/enable_permission_view_model.dart'; import 'components/space_user_footer.dart'; import 'map_view_model.dart'; @@ -66,6 +67,7 @@ class _MapScreenState extends ConsumerState { _observeMarkerChange(); _observeShowEnableLocationPrompt(context); _observeMemberPlace(context); + _observePermissionChange(); _observeError(); notifier = ref.watch(mapViewStateProvider.notifier); @@ -104,8 +106,8 @@ class _MapScreenState extends ConsumerState { children: [ SpaceUserFooter( selectedSpace: widget.space, - members: state.userInfo, - selectedUser: state.selectedUser, + members: state.userInfos.values.toList(), + selectedUser: state.userInfos[state.selectedUser?.id], isEnabled: !state.loading, fetchingInviteCode: state.fetchingInviteCode, isCurrentUser: notifier.isCurrentUser(), @@ -117,7 +119,7 @@ class _MapScreenState extends ConsumerState { onMemberTap: (member) { notifier.showMemberDetail(member); }, - onRelocateTap: () => notifier.getUserLastLocation(), + onRelocateTap: () => notifier.relocateCameraPosition(), onMapTypeTap: () => _openMapTypeSheet(context), onPlacesTap: () { final space = widget.space; @@ -285,20 +287,6 @@ class _MapScreenState extends ConsumerState { }); } - void _observeMarkerChange() { - ref.listen(mapViewStateProvider.select((state) => state.markers), - (previous, next) { - if (previous?.length != next.length) { - _clearNonPlaceMarkers(); - } - if (next.isNotEmpty) { - for (final item in next) { - _buildMarker(item); - } - } - }); - } - void _observeShowEnableLocationPrompt(BuildContext context) { ref.listen(mapViewStateProvider.select((state) => state.showLocationDialog), (_, next) { @@ -321,29 +309,51 @@ class _MapScreenState extends ConsumerState { }); } - void _buildMarker(UserMarker item) async { - final marker = await _mapMarker( - item.isSelected, - item.userName, - item.imageUrl, - item.isSelected - ? context.colorScheme.secondary - : context.colorScheme.surface, - context.colorScheme.primary, - AppTextStyle.subtitle2.copyWith(color: context.colorScheme.onPrimary), - ); + void _observePermissionChange() { + ref.listen( + permissionStateProvider.select((state) => state.isLocationGranted), + (previous, next) { + if (previous != next && next) { + notifier.fetchCurrentUserLocation(); + } + }); + } - setState(() { - _markers.add( - Marker( + void _observeMarkerChange() { + ref.listen(mapViewStateProvider.select((state) => state.userInfos), + (previous, next) async { + if (previous?.length != next.length) { + _clearNonPlaceMarkers(); + } + if (next.isNotEmpty) { + final markers = await Future.wait(next.values.map((item) async { + final marker = await _mapMarker( + item.isSelected, + item.user.fullName, + item.imageUrl, + item.isSelected + ? context.colorScheme.secondary + : context.colorScheme.surface, + context.colorScheme.primary, + AppTextStyle.subtitle2 + .copyWith(color: context.colorScheme.onPrimary), + ); + + return Marker( markerId: MarkerId(item.userId), position: LatLng(item.latitude, item.longitude), anchor: const Offset(0.0, 1.0), icon: marker, onTap: () { notifier.onTapUserMarker(item.userId); - }), - ); + }, + ); + }).toList()); + + setState(() { + _markers.addAll(markers); + }); + } }); } @@ -478,8 +488,10 @@ class _MapScreenState extends ConsumerState { setState(() { _places.add(Circle( circleId: CircleId(place.id), - fillColor: context.colorScheme.primary.withAlpha((0.4 * 255).toInt()), - strokeColor: context.colorScheme.primary.withAlpha((0.6 * 255).toInt()), + fillColor: + context.colorScheme.primary.withAlpha((0.4 * 255).toInt()), + strokeColor: + context.colorScheme.primary.withAlpha((0.6 * 255).toInt()), strokeWidth: 1, center: latLng, radius: place.radius, diff --git a/app/lib/ui/flow/home/map/map_view_model.dart b/app/lib/ui/flow/home/map/map_view_model.dart index 29944fc6..af250071 100644 --- a/app/lib/ui/flow/home/map/map_view_model.dart +++ b/app/lib/ui/flow/home/map/map_view_model.dart @@ -27,8 +27,9 @@ const FOUR_MIN_SECONDS = 4 * 60 * 1000; final mapViewStateProvider = StateNotifierProvider.autoDispose((ref) { - return MapViewNotifier( + final notifier = MapViewNotifier( ref.read(currentUserPod), + ref.read(currentSpaceId), ref.read(spaceServiceProvider), ref.read(placeServiceProvider), ref.read(permissionServiceProvider), @@ -36,10 +37,21 @@ final mapViewStateProvider = ref.read(authServiceProvider), ref.read(googleMapType.notifier), ); + + ref.listen(currentUserPod, (prev, user) { + notifier._onUpdateUser(prevUser: prev, currentUser: user); + }); + + ref.listen(currentSpaceId, (prev, newSpaceId) { + notifier._onUpdateSpace(prev: prev, current: newSpaceId); + }); + + return notifier; }); class MapViewNotifier extends StateNotifier { final ApiUser? _currentUser; + String? _currentSpaceId; final SpaceService spaceService; final PlaceService placeService; final PermissionService permissionService; @@ -47,12 +59,12 @@ class MapViewNotifier extends StateNotifier { final AuthService authService; final StateController mapTypeController; - LatLng? _userLocation; StreamSubscription>? _userInfoSubscription; StreamSubscription>? _placeSubscription; MapViewNotifier( this._currentUser, + this._currentSpaceId, this.spaceService, this.placeService, this.permissionService, @@ -61,25 +73,45 @@ class MapViewNotifier extends StateNotifier { this.mapTypeController, ) : super(MapViewState(mapType: mapTypeController.state)) { checkUserPermission(); - getUserLastLocation(); + loadData(_currentSpaceId); } void loadData(String? spaceId) { - _onSelectedSpaceChange(); - + _resetState(); if (spaceId == null) return; - _listenMemberLocation(spaceId); _listenPlaces(spaceId); } - void _onSelectedSpaceChange() { + void _onUpdateUser({ApiUser? prevUser, ApiUser? currentUser}) { + _resetState(); + if (currentUser != null && prevUser?.id != currentUser.id) { + fetchCurrentUserLocation(); + } + } + + void _onUpdateSpace({String? prev, String? current}) { + _currentSpaceId = current; + if (current == null) { + _userInfoSubscription?.cancel(); + _placeSubscription?.cancel(); + return; + } + if (prev == current) return; + _resetState(); + loadData(current); + } + + void _resetState() { + _userInfoSubscription?.cancel(); + _placeSubscription?.cancel(); + state = state.copyWith( - userInfo: [], + userInfos: {}, places: [], - markers: [], defaultPosition: null, selectedUser: null, + currentUserLocation: null, ); } @@ -90,7 +122,7 @@ class MapViewNotifier extends StateNotifier { _userInfoSubscription?.cancel(); _userInfoSubscription = spaceService.getMemberWithLocation(spaceId).listen((userInfo) { - state = state.copyWith(userInfo: userInfo, loading: false); + state = state.copyWith(loading: false); _userMapPositions(userInfo); }); } catch (error, stack) { @@ -121,32 +153,50 @@ class MapViewNotifier extends StateNotifier { } void _userMapPositions(List userInfo) async { - final List markers = []; + final Map mapUserInfo = Map.of(state.userInfos); + for (final info in userInfo) { - if (info.user.id == _currentUser?.id && _userLocation == null) { + MapUserInfo? userInfo; + if (info.user.id == _currentUser?.id) { final latLng = LatLng( - info.location?.latitude ?? 0.0, - info.location?.longitude ?? 0.0, + info.location?.latitude ?? state.currentUserLocation?.latitude ?? 0.0, + info.location?.longitude ?? + state.currentUserLocation?.longitude ?? + 0.0, ); - _userLocation = latLng; + state = state.copyWith(currentUserLocation: latLng); + + userInfo = await _prepareMapUserInfo(info.user, + latitude: latLng.latitude, longitude: latLng.longitude); + _mapCameraPosition(latLng, defaultCameraZoom); + } else if (info.location != null && info.isLocationEnabled) { + userInfo = await _prepareMapUserInfo(info.user, + latitude: info.location!.latitude, + longitude: info.location!.longitude); } - if (info.location != null && info.isLocationEnabled) { - markers.add(UserMarker( - userId: info.user.id, - userName: info.user.fullName, - imageUrl: await _convertUrlToImage(info.user.profile_image), - latitude: info.location!.latitude, - longitude: info.location!.longitude, - isSelected: state.selectedUser == null - ? false - : state.selectedUser?.user.id == info.user.id, - )); - } + userInfo = userInfo?.copyWith( + updatedLocationAt: info.location?.created_at ?? + DateTime.now().millisecondsSinceEpoch); + if (userInfo != null) mapUserInfo[info.user.id] = userInfo; } - state = state.copyWith(markers: markers); + state = state.copyWith(userInfos: mapUserInfo); + } + + Future _prepareMapUserInfo(ApiUser user, + {required double latitude, required double longitude}) async { + return MapUserInfo( + userId: user.id, + user: user, + imageUrl: await _convertUrlToImage(user.profile_image), + latitude: latitude, + longitude: longitude, + isSelected: state.selectedUser == null + ? false + : state.selectedUser?.id == user.id, + ); } Future _convertUrlToImage(String? imageUrl) async { @@ -202,18 +252,20 @@ class MapViewNotifier extends StateNotifier { _onSelectUserMarker(null); } - void showMemberDetail(ApiUserInfo member) async { + void showMemberDetail(MapUserInfo member) async { final selectedMember = - (state.selectedUser?.user.id == member.user.id) ? null : member; - final position = (selectedMember != null && selectedMember.location != null) + (state.selectedUser?.id == member.user.id) ? null : member; + + final position = (selectedMember != null && + selectedMember.latitude != 0.0 && + selectedMember.longitude != 0.0) ? CameraPosition( - target: LatLng(selectedMember.location!.latitude, - selectedMember.location!.longitude), + target: LatLng(selectedMember.latitude, selectedMember.longitude), zoom: defaultCameraZoomForSelectedUser) : null; - state = - state.copyWith(selectedUser: selectedMember, defaultPosition: position); + state = state.copyWith( + selectedUser: selectedMember?.user, defaultPosition: position); _onSelectUserMarker(member.user.id); if (state.selectedUser != null) { getNetworkStatus(); @@ -221,24 +273,17 @@ class MapViewNotifier extends StateNotifier { } void _onSelectUserMarker(String? userId) { - final List updatedMarkers; - - if (userId == null) { - updatedMarkers = state.markers - .map((marker) => marker.copyWith(isSelected: false)) - .toList(); - } else { - updatedMarkers = state.markers.map((marker) { - return marker.userId == userId - ? marker.copyWith(isSelected: !marker.isSelected) - : marker.copyWith(isSelected: false); - }).toList(); - } - state = state.copyWith(markers: updatedMarkers); + final Map updatedInofs = + state.userInfos.map((key, value) { + return MapEntry(key, value.copyWith(isSelected: key == userId)); + }); + + state = state.copyWith(userInfos: updatedInofs); } void onTapUserMarker(String userId) { - final user = state.userInfo.firstWhere((user) => user.user.id == userId); + final user = + state.userInfos.values.firstWhere((user) => user.userId == userId); showMemberDetail(user); } @@ -272,24 +317,15 @@ class MapViewNotifier extends StateNotifier { state = state.copyWith(showLocationDialog: DateTime.now()); } - void getUserLastLocation() async { + void relocateCameraPosition() async { try { state = state.copyWith(defaultPosition: null); - final isEnabled = await permissionService.isLocationPermissionGranted(); - if (isEnabled) { - final position = await locationManager.getLastLocation(); - final latLng = LatLng(position!.latitude, position.longitude); + final userId = state.selectedUser?.id ?? _currentUser?.id; + + final info = state.userInfos[userId]; + if (info != null) { + final latLng = LatLng(info.latitude, info.longitude); _mapCameraPosition(latLng, defaultCameraZoom); - } else { - for (final info in state.userInfo) { - if (info.user.id == _currentUser?.id) { - final latLng = LatLng( - info.location?.latitude ?? 0.0, - info.location?.longitude ?? 0.0, - ); - _mapCameraPosition(latLng, defaultCameraZoom); - } - } } } catch (error, stack) { state = state.copyWith(error: error); @@ -311,10 +347,8 @@ class MapViewNotifier extends StateNotifier { void getNetworkStatus() async { try { - await authService.getUserNetworkStatus(state.selectedUser!.user.id, - (user) { - state = state.copyWith( - selectedUser: state.selectedUser?.copyWith(user: user)); + await authService.getUserNetworkStatus(state.selectedUser!.id, (user) { + state = state.copyWith(selectedUser: user); }); } catch (error, stack) { logger.e( @@ -325,6 +359,27 @@ class MapViewNotifier extends StateNotifier { } } + void fetchCurrentUserLocation() async { + try { + final location = await locationManager.getLastLocation(); + + final currentLocation = state.currentUserLocation; + final hasLocation = currentLocation != null && + currentLocation.longitude != 0.0 && + currentLocation.latitude != 0.0; + + if (_currentUser != null && location != null && !hasLocation) { + locationManager.saveLocation(LocationData( + latitude: location.latitude, + longitude: location.longitude, + timestamp: location.timestamp)); + } + } catch (error, stack) { + logger.e('MapViewNotifier: error while get current location', + error: error, stackTrace: stack); + } + } + void setMapType(String type) { mapTypeController.state = type; state = state.copyWith(mapType: type); @@ -349,7 +404,7 @@ class MapViewNotifier extends StateNotifier { } bool isCurrentUser() { - return _currentUser?.id == state.selectedUser?.user.id; + return _currentUser?.id == state.selectedUser?.id; } } @@ -362,10 +417,9 @@ class MapViewState with _$MapViewState { @Default(false) bool hasLocationServiceEnabled, @Default(false) bool hasNotificationEnabled, @Default(false) bool hasFineLocationEnabled, - @Default([]) List userInfo, @Default([]) List places, - @Default([]) List markers, - ApiUserInfo? selectedUser, + @Default({}) Map userInfos, + ApiUser? selectedUser, LatLng? currentUserLocation, CameraPosition? defaultPosition, @Default('') String spaceInvitationCode, @@ -376,13 +430,18 @@ class MapViewState with _$MapViewState { } @freezed -class UserMarker with _$UserMarker { - const factory UserMarker({ +class MapUserInfo with _$MapUserInfo { + const MapUserInfo._(); + + const factory MapUserInfo({ required String userId, - required String userName, + required ApiUser user, required ui.Image? imageUrl, required double latitude, required double longitude, required bool isSelected, - }) = _UserMarker; + @Default(0) int? updatedLocationAt, + }) = _MapUserInfo; + + LatLng get latLng => LatLng(latitude, longitude); } diff --git a/app/lib/ui/flow/home/map/map_view_model.freezed.dart b/app/lib/ui/flow/home/map/map_view_model.freezed.dart index 8ca1ff12..2d082e8a 100644 --- a/app/lib/ui/flow/home/map/map_view_model.freezed.dart +++ b/app/lib/ui/flow/home/map/map_view_model.freezed.dart @@ -22,10 +22,9 @@ mixin _$MapViewState { bool get hasLocationServiceEnabled => throw _privateConstructorUsedError; bool get hasNotificationEnabled => throw _privateConstructorUsedError; bool get hasFineLocationEnabled => throw _privateConstructorUsedError; - List get userInfo => throw _privateConstructorUsedError; List get places => throw _privateConstructorUsedError; - List get markers => throw _privateConstructorUsedError; - ApiUserInfo? get selectedUser => throw _privateConstructorUsedError; + Map get userInfos => throw _privateConstructorUsedError; + ApiUser? get selectedUser => throw _privateConstructorUsedError; LatLng? get currentUserLocation => throw _privateConstructorUsedError; CameraPosition? get defaultPosition => throw _privateConstructorUsedError; String get spaceInvitationCode => throw _privateConstructorUsedError; @@ -51,10 +50,9 @@ abstract class $MapViewStateCopyWith<$Res> { bool hasLocationServiceEnabled, bool hasNotificationEnabled, bool hasFineLocationEnabled, - List userInfo, List places, - List markers, - ApiUserInfo? selectedUser, + Map userInfos, + ApiUser? selectedUser, LatLng? currentUserLocation, CameraPosition? defaultPosition, String spaceInvitationCode, @@ -62,7 +60,7 @@ abstract class $MapViewStateCopyWith<$Res> { Object? error, DateTime? showLocationDialog}); - $ApiUserInfoCopyWith<$Res>? get selectedUser; + $ApiUserCopyWith<$Res>? get selectedUser; } /// @nodoc @@ -84,9 +82,8 @@ class _$MapViewStateCopyWithImpl<$Res, $Val extends MapViewState> Object? hasLocationServiceEnabled = null, Object? hasNotificationEnabled = null, Object? hasFineLocationEnabled = null, - Object? userInfo = null, Object? places = null, - Object? markers = null, + Object? userInfos = null, Object? selectedUser = freezed, Object? currentUserLocation = freezed, Object? defaultPosition = freezed, @@ -120,22 +117,18 @@ class _$MapViewStateCopyWithImpl<$Res, $Val extends MapViewState> ? _value.hasFineLocationEnabled : hasFineLocationEnabled // ignore: cast_nullable_to_non_nullable as bool, - userInfo: null == userInfo - ? _value.userInfo - : userInfo // ignore: cast_nullable_to_non_nullable - as List, places: null == places ? _value.places : places // ignore: cast_nullable_to_non_nullable as List, - markers: null == markers - ? _value.markers - : markers // ignore: cast_nullable_to_non_nullable - as List, + userInfos: null == userInfos + ? _value.userInfos + : userInfos // ignore: cast_nullable_to_non_nullable + as Map, selectedUser: freezed == selectedUser ? _value.selectedUser : selectedUser // ignore: cast_nullable_to_non_nullable - as ApiUserInfo?, + as ApiUser?, currentUserLocation: freezed == currentUserLocation ? _value.currentUserLocation : currentUserLocation // ignore: cast_nullable_to_non_nullable @@ -162,12 +155,12 @@ class _$MapViewStateCopyWithImpl<$Res, $Val extends MapViewState> @override @pragma('vm:prefer-inline') - $ApiUserInfoCopyWith<$Res>? get selectedUser { + $ApiUserCopyWith<$Res>? get selectedUser { if (_value.selectedUser == null) { return null; } - return $ApiUserInfoCopyWith<$Res>(_value.selectedUser!, (value) { + return $ApiUserCopyWith<$Res>(_value.selectedUser!, (value) { return _then(_value.copyWith(selectedUser: value) as $Val); }); } @@ -188,10 +181,9 @@ abstract class _$$MapViewStateImplCopyWith<$Res> bool hasLocationServiceEnabled, bool hasNotificationEnabled, bool hasFineLocationEnabled, - List userInfo, List places, - List markers, - ApiUserInfo? selectedUser, + Map userInfos, + ApiUser? selectedUser, LatLng? currentUserLocation, CameraPosition? defaultPosition, String spaceInvitationCode, @@ -200,7 +192,7 @@ abstract class _$$MapViewStateImplCopyWith<$Res> DateTime? showLocationDialog}); @override - $ApiUserInfoCopyWith<$Res>? get selectedUser; + $ApiUserCopyWith<$Res>? get selectedUser; } /// @nodoc @@ -220,9 +212,8 @@ class __$$MapViewStateImplCopyWithImpl<$Res> Object? hasLocationServiceEnabled = null, Object? hasNotificationEnabled = null, Object? hasFineLocationEnabled = null, - Object? userInfo = null, Object? places = null, - Object? markers = null, + Object? userInfos = null, Object? selectedUser = freezed, Object? currentUserLocation = freezed, Object? defaultPosition = freezed, @@ -253,22 +244,18 @@ class __$$MapViewStateImplCopyWithImpl<$Res> ? _value.hasFineLocationEnabled : hasFineLocationEnabled // ignore: cast_nullable_to_non_nullable as bool, - userInfo: null == userInfo - ? _value._userInfo - : userInfo // ignore: cast_nullable_to_non_nullable - as List, places: null == places ? _value._places : places // ignore: cast_nullable_to_non_nullable as List, - markers: null == markers - ? _value._markers - : markers // ignore: cast_nullable_to_non_nullable - as List, + userInfos: null == userInfos + ? _value._userInfos + : userInfos // ignore: cast_nullable_to_non_nullable + as Map, selectedUser: freezed == selectedUser ? _value.selectedUser : selectedUser // ignore: cast_nullable_to_non_nullable - as ApiUserInfo?, + as ApiUser?, currentUserLocation: freezed == currentUserLocation ? _value.currentUserLocation : currentUserLocation // ignore: cast_nullable_to_non_nullable @@ -304,9 +291,8 @@ class _$MapViewStateImpl implements _MapViewState { this.hasLocationServiceEnabled = false, this.hasNotificationEnabled = false, this.hasFineLocationEnabled = false, - final List userInfo = const [], final List places = const [], - final List markers = const [], + final Map userInfos = const {}, this.selectedUser, this.currentUserLocation, this.defaultPosition, @@ -314,9 +300,8 @@ class _$MapViewStateImpl implements _MapViewState { required this.mapType, this.error, this.showLocationDialog}) - : _userInfo = userInfo, - _places = places, - _markers = markers; + : _places = places, + _userInfos = userInfos; @override @JsonKey() @@ -336,15 +321,6 @@ class _$MapViewStateImpl implements _MapViewState { @override @JsonKey() final bool hasFineLocationEnabled; - final List _userInfo; - @override - @JsonKey() - List get userInfo { - if (_userInfo is EqualUnmodifiableListView) return _userInfo; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_userInfo); - } - final List _places; @override @JsonKey() @@ -354,17 +330,17 @@ class _$MapViewStateImpl implements _MapViewState { return EqualUnmodifiableListView(_places); } - final List _markers; + final Map _userInfos; @override @JsonKey() - List get markers { - if (_markers is EqualUnmodifiableListView) return _markers; + Map get userInfos { + if (_userInfos is EqualUnmodifiableMapView) return _userInfos; // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_markers); + return EqualUnmodifiableMapView(_userInfos); } @override - final ApiUserInfo? selectedUser; + final ApiUser? selectedUser; @override final LatLng? currentUserLocation; @override @@ -381,7 +357,7 @@ class _$MapViewStateImpl implements _MapViewState { @override String toString() { - return 'MapViewState(loading: $loading, fetchingInviteCode: $fetchingInviteCode, hasLocationEnabled: $hasLocationEnabled, hasLocationServiceEnabled: $hasLocationServiceEnabled, hasNotificationEnabled: $hasNotificationEnabled, hasFineLocationEnabled: $hasFineLocationEnabled, userInfo: $userInfo, places: $places, markers: $markers, selectedUser: $selectedUser, currentUserLocation: $currentUserLocation, defaultPosition: $defaultPosition, spaceInvitationCode: $spaceInvitationCode, mapType: $mapType, error: $error, showLocationDialog: $showLocationDialog)'; + return 'MapViewState(loading: $loading, fetchingInviteCode: $fetchingInviteCode, hasLocationEnabled: $hasLocationEnabled, hasLocationServiceEnabled: $hasLocationServiceEnabled, hasNotificationEnabled: $hasNotificationEnabled, hasFineLocationEnabled: $hasFineLocationEnabled, places: $places, userInfos: $userInfos, selectedUser: $selectedUser, currentUserLocation: $currentUserLocation, defaultPosition: $defaultPosition, spaceInvitationCode: $spaceInvitationCode, mapType: $mapType, error: $error, showLocationDialog: $showLocationDialog)'; } @override @@ -401,9 +377,9 @@ class _$MapViewStateImpl implements _MapViewState { other.hasNotificationEnabled == hasNotificationEnabled) && (identical(other.hasFineLocationEnabled, hasFineLocationEnabled) || other.hasFineLocationEnabled == hasFineLocationEnabled) && - const DeepCollectionEquality().equals(other._userInfo, _userInfo) && const DeepCollectionEquality().equals(other._places, _places) && - const DeepCollectionEquality().equals(other._markers, _markers) && + const DeepCollectionEquality() + .equals(other._userInfos, _userInfos) && (identical(other.selectedUser, selectedUser) || other.selectedUser == selectedUser) && (identical(other.currentUserLocation, currentUserLocation) || @@ -427,9 +403,8 @@ class _$MapViewStateImpl implements _MapViewState { hasLocationServiceEnabled, hasNotificationEnabled, hasFineLocationEnabled, - const DeepCollectionEquality().hash(_userInfo), const DeepCollectionEquality().hash(_places), - const DeepCollectionEquality().hash(_markers), + const DeepCollectionEquality().hash(_userInfos), selectedUser, currentUserLocation, defaultPosition, @@ -453,10 +428,9 @@ abstract class _MapViewState implements MapViewState { final bool hasLocationServiceEnabled, final bool hasNotificationEnabled, final bool hasFineLocationEnabled, - final List userInfo, final List places, - final List markers, - final ApiUserInfo? selectedUser, + final Map userInfos, + final ApiUser? selectedUser, final LatLng? currentUserLocation, final CameraPosition? defaultPosition, final String spaceInvitationCode, @@ -477,13 +451,11 @@ abstract class _MapViewState implements MapViewState { @override bool get hasFineLocationEnabled; @override - List get userInfo; - @override List get places; @override - List get markers; + Map get userInfos; @override - ApiUserInfo? get selectedUser; + ApiUser? get selectedUser; @override LatLng? get currentUserLocation; @override @@ -503,38 +475,42 @@ abstract class _MapViewState implements MapViewState { } /// @nodoc -mixin _$UserMarker { +mixin _$MapUserInfo { String get userId => throw _privateConstructorUsedError; - String get userName => throw _privateConstructorUsedError; + ApiUser get user => throw _privateConstructorUsedError; ui.Image? get imageUrl => throw _privateConstructorUsedError; double get latitude => throw _privateConstructorUsedError; double get longitude => throw _privateConstructorUsedError; bool get isSelected => throw _privateConstructorUsedError; + int? get updatedLocationAt => throw _privateConstructorUsedError; @JsonKey(ignore: true) - $UserMarkerCopyWith get copyWith => + $MapUserInfoCopyWith get copyWith => throw _privateConstructorUsedError; } /// @nodoc -abstract class $UserMarkerCopyWith<$Res> { - factory $UserMarkerCopyWith( - UserMarker value, $Res Function(UserMarker) then) = - _$UserMarkerCopyWithImpl<$Res, UserMarker>; +abstract class $MapUserInfoCopyWith<$Res> { + factory $MapUserInfoCopyWith( + MapUserInfo value, $Res Function(MapUserInfo) then) = + _$MapUserInfoCopyWithImpl<$Res, MapUserInfo>; @useResult $Res call( {String userId, - String userName, + ApiUser user, ui.Image? imageUrl, double latitude, double longitude, - bool isSelected}); + bool isSelected, + int? updatedLocationAt}); + + $ApiUserCopyWith<$Res> get user; } /// @nodoc -class _$UserMarkerCopyWithImpl<$Res, $Val extends UserMarker> - implements $UserMarkerCopyWith<$Res> { - _$UserMarkerCopyWithImpl(this._value, this._then); +class _$MapUserInfoCopyWithImpl<$Res, $Val extends MapUserInfo> + implements $MapUserInfoCopyWith<$Res> { + _$MapUserInfoCopyWithImpl(this._value, this._then); // ignore: unused_field final $Val _value; @@ -545,21 +521,22 @@ class _$UserMarkerCopyWithImpl<$Res, $Val extends UserMarker> @override $Res call({ Object? userId = null, - Object? userName = null, + Object? user = null, Object? imageUrl = freezed, Object? latitude = null, Object? longitude = null, Object? isSelected = null, + Object? updatedLocationAt = freezed, }) { return _then(_value.copyWith( userId: null == userId ? _value.userId : userId // ignore: cast_nullable_to_non_nullable as String, - userName: null == userName - ? _value.userName - : userName // ignore: cast_nullable_to_non_nullable - as String, + user: null == user + ? _value.user + : user // ignore: cast_nullable_to_non_nullable + as ApiUser, imageUrl: freezed == imageUrl ? _value.imageUrl : imageUrl // ignore: cast_nullable_to_non_nullable @@ -576,54 +553,71 @@ class _$UserMarkerCopyWithImpl<$Res, $Val extends UserMarker> ? _value.isSelected : isSelected // ignore: cast_nullable_to_non_nullable as bool, + updatedLocationAt: freezed == updatedLocationAt + ? _value.updatedLocationAt + : updatedLocationAt // ignore: cast_nullable_to_non_nullable + as int?, ) as $Val); } + + @override + @pragma('vm:prefer-inline') + $ApiUserCopyWith<$Res> get user { + return $ApiUserCopyWith<$Res>(_value.user, (value) { + return _then(_value.copyWith(user: value) as $Val); + }); + } } /// @nodoc -abstract class _$$UserMarkerImplCopyWith<$Res> - implements $UserMarkerCopyWith<$Res> { - factory _$$UserMarkerImplCopyWith( - _$UserMarkerImpl value, $Res Function(_$UserMarkerImpl) then) = - __$$UserMarkerImplCopyWithImpl<$Res>; +abstract class _$$MapUserInfoImplCopyWith<$Res> + implements $MapUserInfoCopyWith<$Res> { + factory _$$MapUserInfoImplCopyWith( + _$MapUserInfoImpl value, $Res Function(_$MapUserInfoImpl) then) = + __$$MapUserInfoImplCopyWithImpl<$Res>; @override @useResult $Res call( {String userId, - String userName, + ApiUser user, ui.Image? imageUrl, double latitude, double longitude, - bool isSelected}); + bool isSelected, + int? updatedLocationAt}); + + @override + $ApiUserCopyWith<$Res> get user; } /// @nodoc -class __$$UserMarkerImplCopyWithImpl<$Res> - extends _$UserMarkerCopyWithImpl<$Res, _$UserMarkerImpl> - implements _$$UserMarkerImplCopyWith<$Res> { - __$$UserMarkerImplCopyWithImpl( - _$UserMarkerImpl _value, $Res Function(_$UserMarkerImpl) _then) +class __$$MapUserInfoImplCopyWithImpl<$Res> + extends _$MapUserInfoCopyWithImpl<$Res, _$MapUserInfoImpl> + implements _$$MapUserInfoImplCopyWith<$Res> { + __$$MapUserInfoImplCopyWithImpl( + _$MapUserInfoImpl _value, $Res Function(_$MapUserInfoImpl) _then) : super(_value, _then); @pragma('vm:prefer-inline') @override $Res call({ Object? userId = null, - Object? userName = null, + Object? user = null, Object? imageUrl = freezed, Object? latitude = null, Object? longitude = null, Object? isSelected = null, + Object? updatedLocationAt = freezed, }) { - return _then(_$UserMarkerImpl( + return _then(_$MapUserInfoImpl( userId: null == userId ? _value.userId : userId // ignore: cast_nullable_to_non_nullable as String, - userName: null == userName - ? _value.userName - : userName // ignore: cast_nullable_to_non_nullable - as String, + user: null == user + ? _value.user + : user // ignore: cast_nullable_to_non_nullable + as ApiUser, imageUrl: freezed == imageUrl ? _value.imageUrl : imageUrl // ignore: cast_nullable_to_non_nullable @@ -640,25 +634,31 @@ class __$$UserMarkerImplCopyWithImpl<$Res> ? _value.isSelected : isSelected // ignore: cast_nullable_to_non_nullable as bool, + updatedLocationAt: freezed == updatedLocationAt + ? _value.updatedLocationAt + : updatedLocationAt // ignore: cast_nullable_to_non_nullable + as int?, )); } } /// @nodoc -class _$UserMarkerImpl implements _UserMarker { - const _$UserMarkerImpl( +class _$MapUserInfoImpl extends _MapUserInfo { + const _$MapUserInfoImpl( {required this.userId, - required this.userName, + required this.user, required this.imageUrl, required this.latitude, required this.longitude, - required this.isSelected}); + required this.isSelected, + this.updatedLocationAt = 0}) + : super._(); @override final String userId; @override - final String userName; + final ApiUser user; @override final ui.Image? imageUrl; @override @@ -667,20 +667,22 @@ class _$UserMarkerImpl implements _UserMarker { final double longitude; @override final bool isSelected; + @override + @JsonKey() + final int? updatedLocationAt; @override String toString() { - return 'UserMarker(userId: $userId, userName: $userName, imageUrl: $imageUrl, latitude: $latitude, longitude: $longitude, isSelected: $isSelected)'; + return 'MapUserInfo(userId: $userId, user: $user, imageUrl: $imageUrl, latitude: $latitude, longitude: $longitude, isSelected: $isSelected, updatedLocationAt: $updatedLocationAt)'; } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _$UserMarkerImpl && + other is _$MapUserInfoImpl && (identical(other.userId, userId) || other.userId == userId) && - (identical(other.userName, userName) || - other.userName == userName) && + (identical(other.user, user) || other.user == user) && (identical(other.imageUrl, imageUrl) || other.imageUrl == imageUrl) && (identical(other.latitude, latitude) || @@ -688,33 +690,37 @@ class _$UserMarkerImpl implements _UserMarker { (identical(other.longitude, longitude) || other.longitude == longitude) && (identical(other.isSelected, isSelected) || - other.isSelected == isSelected)); + other.isSelected == isSelected) && + (identical(other.updatedLocationAt, updatedLocationAt) || + other.updatedLocationAt == updatedLocationAt)); } @override - int get hashCode => Object.hash( - runtimeType, userId, userName, imageUrl, latitude, longitude, isSelected); + int get hashCode => Object.hash(runtimeType, userId, user, imageUrl, latitude, + longitude, isSelected, updatedLocationAt); @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - _$$UserMarkerImplCopyWith<_$UserMarkerImpl> get copyWith => - __$$UserMarkerImplCopyWithImpl<_$UserMarkerImpl>(this, _$identity); + _$$MapUserInfoImplCopyWith<_$MapUserInfoImpl> get copyWith => + __$$MapUserInfoImplCopyWithImpl<_$MapUserInfoImpl>(this, _$identity); } -abstract class _UserMarker implements UserMarker { - const factory _UserMarker( +abstract class _MapUserInfo extends MapUserInfo { + const factory _MapUserInfo( {required final String userId, - required final String userName, + required final ApiUser user, required final ui.Image? imageUrl, required final double latitude, required final double longitude, - required final bool isSelected}) = _$UserMarkerImpl; + required final bool isSelected, + final int? updatedLocationAt}) = _$MapUserInfoImpl; + const _MapUserInfo._() : super._(); @override String get userId; @override - String get userName; + ApiUser get user; @override ui.Image? get imageUrl; @override @@ -724,7 +730,9 @@ abstract class _UserMarker implements UserMarker { @override bool get isSelected; @override + int? get updatedLocationAt; + @override @JsonKey(ignore: true) - _$$UserMarkerImplCopyWith<_$UserMarkerImpl> get copyWith => + _$$MapUserInfoImplCopyWith<_$MapUserInfoImpl> get copyWith => throw _privateConstructorUsedError; } diff --git a/app/lib/ui/flow/intro/intro_screen.dart b/app/lib/ui/flow/intro/intro_screen.dart index e579cb39..4f246cfd 100644 --- a/app/lib/ui/flow/intro/intro_screen.dart +++ b/app/lib/ui/flow/intro/intro_screen.dart @@ -77,7 +77,7 @@ class _IntroScreenState extends ConsumerState { onPressed: () { if (_controller.page == _items.length - 1) { ref.read(isIntroScreenShownPod.notifier).state = true; - AppRoute.signInMethod.push(context); + AppRoute.signInMethod.pushReplacement(context); } else { _controller.nextPage( duration: const Duration(milliseconds: 300), diff --git a/app/lib/ui/flow/setting/profile/profile_screen.dart b/app/lib/ui/flow/setting/profile/profile_screen.dart index 20eae143..abd9ce0e 100644 --- a/app/lib/ui/flow/setting/profile/profile_screen.dart +++ b/app/lib/ui/flow/setting/profile/profile_screen.dart @@ -306,7 +306,7 @@ class _ProfileScreenState extends ConsumerState { editProfileViewStateProvider.select((state) => state.accountDeleted), (previous, next) { if (next) { - AppRoute.signInMethod.push(context); + AppRoute.signInMethod.go(context); } }); } diff --git a/app/lib/ui/flow/setting/profile/profile_view_model.dart b/app/lib/ui/flow/setting/profile/profile_view_model.dart index 24c39f30..c7afe178 100644 --- a/app/lib/ui/flow/setting/profile/profile_view_model.dart +++ b/app/lib/ui/flow/setting/profile/profile_view_model.dart @@ -44,10 +44,12 @@ class EditProfileViewNotifier extends StateNotifier { void deleteAccount() async { try { + if (user?.id == null) return; state = state.copyWith(deletingAccount: true); - await spaceService.deleteUserSpaces(); - await authService.deleteAccount(currentUserId: user?.id); - state = state.copyWith(deletingAccount: false, accountDeleted: true, error: null); + await spaceService.deleteUserSpaces(user!.id); + await authService.deleteAccount(currentUserId: user!.id); + state = state.copyWith( + deletingAccount: false, accountDeleted: true, error: null); locationManager.stopTrackingService(); } catch (error, stack) { logger.e( diff --git a/app/lib/ui/flow/setting/setting_screen.dart b/app/lib/ui/flow/setting/setting_screen.dart index 6543c417..ada8759d 100644 --- a/app/lib/ui/flow/setting/setting_screen.dart +++ b/app/lib/ui/flow/setting/setting_screen.dart @@ -312,7 +312,7 @@ class _SettingScreenState extends ConsumerState { ref.listen(settingViewStateProvider.select((state) => state.logOut), (previous, next) { if (next) { - AppRoute.signInMethod.push(context); + AppRoute.signInMethod.go(context); } }); } diff --git a/app/lib/ui/flow/setting/setting_view_model.dart b/app/lib/ui/flow/setting/setting_view_model.dart index db363893..bcee60d2 100644 --- a/app/lib/ui/flow/setting/setting_view_model.dart +++ b/app/lib/ui/flow/setting/setting_view_model.dart @@ -44,9 +44,10 @@ class SettingViewNotifier extends StateNotifier { } void getUser() { + if(user == null) return; state = state.copyWith(currentUser: user); _userSubscription = authService - .getUserStream(currentUserId: state.currentUser?.id ?? '') + .getUserStream(currentUserId: user!.id) .listen((user) { state = state.copyWith(currentUser: user); }); diff --git a/data/lib/api/message/message_models.freezed.dart b/data/lib/api/message/message_models.freezed.dart index 6af1af06..0635438f 100644 --- a/data/lib/api/message/message_models.freezed.dart +++ b/data/lib/api/message/message_models.freezed.dart @@ -30,8 +30,12 @@ mixin _$ApiThread { @ServerTimestampConverter() DateTime? get last_message_at => throw _privateConstructorUsedError; + /// Serializes this ApiThread to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of ApiThread + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $ApiThreadCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -62,6 +66,8 @@ class _$ApiThreadCopyWithImpl<$Res, $Val extends ApiThread> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of ApiThread + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -138,6 +144,8 @@ class __$$ApiThreadImplCopyWithImpl<$Res> _$ApiThreadImpl _value, $Res Function(_$ApiThreadImpl) _then) : super(_value, _then); + /// Create a copy of ApiThread + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -265,7 +273,7 @@ class _$ApiThreadImpl extends _ApiThread { other.last_message_at == last_message_at)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash( runtimeType, @@ -278,7 +286,9 @@ class _$ApiThreadImpl extends _ApiThread { last_message, last_message_at); - @JsonKey(ignore: true) + /// Create a copy of ApiThread + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$ApiThreadImplCopyWith<_$ApiThreadImpl> get copyWith => @@ -325,8 +335,11 @@ abstract class _ApiThread extends ApiThread { @override @ServerTimestampConverter() DateTime? get last_message_at; + + /// Create a copy of ApiThread + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$ApiThreadImplCopyWith<_$ApiThreadImpl> get copyWith => throw _privateConstructorUsedError; } @@ -346,8 +359,12 @@ mixin _$ApiThreadMessage { @ServerTimestampConverter() DateTime? get created_at => throw _privateConstructorUsedError; + /// Serializes this ApiThreadMessage to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of ApiThreadMessage + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $ApiThreadMessageCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -378,6 +395,8 @@ class _$ApiThreadMessageCopyWithImpl<$Res, $Val extends ApiThreadMessage> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of ApiThreadMessage + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -448,6 +467,8 @@ class __$$ApiThreadMessageImplCopyWithImpl<$Res> $Res Function(_$ApiThreadMessageImpl) _then) : super(_value, _then); + /// Create a copy of ApiThreadMessage + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -563,7 +584,7 @@ class _$ApiThreadMessageImpl extends _ApiThreadMessage { other.created_at == created_at)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash( runtimeType, @@ -575,7 +596,9 @@ class _$ApiThreadMessageImpl extends _ApiThreadMessage { const DeepCollectionEquality().hash(_archived_for), created_at); - @JsonKey(ignore: true) + /// Create a copy of ApiThreadMessage + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$ApiThreadMessageImplCopyWith<_$ApiThreadMessageImpl> get copyWith => @@ -620,8 +643,11 @@ abstract class _ApiThreadMessage extends ApiThreadMessage { @override @ServerTimestampConverter() DateTime? get created_at; + + /// Create a copy of ApiThreadMessage + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$ApiThreadMessageImplCopyWith<_$ApiThreadMessageImpl> get copyWith => throw _privateConstructorUsedError; } @@ -637,8 +663,12 @@ mixin _$ThreadInfo { throw _privateConstructorUsedError; List get members => throw _privateConstructorUsedError; + /// Serializes this ThreadInfo to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of ThreadInfo + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $ThreadInfoCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -667,6 +697,8 @@ class _$ThreadInfoCopyWithImpl<$Res, $Val extends ThreadInfo> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of ThreadInfo + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -690,6 +722,8 @@ class _$ThreadInfoCopyWithImpl<$Res, $Val extends ThreadInfo> ) as $Val); } + /// Create a copy of ThreadInfo + /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $ApiThreadCopyWith<$Res> get thread { @@ -724,6 +758,8 @@ class __$$ThreadInfoImplCopyWithImpl<$Res> _$ThreadInfoImpl _value, $Res Function(_$ThreadInfoImpl) _then) : super(_value, _then); + /// Create a copy of ThreadInfo + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -796,7 +832,7 @@ class _$ThreadInfoImpl extends _ThreadInfo { const DeepCollectionEquality().equals(other._members, _members)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash( runtimeType, @@ -804,7 +840,9 @@ class _$ThreadInfoImpl extends _ThreadInfo { const DeepCollectionEquality().hash(_threadMessage), const DeepCollectionEquality().hash(_members)); - @JsonKey(ignore: true) + /// Create a copy of ThreadInfo + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$ThreadInfoImplCopyWith<_$ThreadInfoImpl> get copyWith => @@ -834,8 +872,11 @@ abstract class _ThreadInfo extends ThreadInfo { List get threadMessage; @override List get members; + + /// Create a copy of ThreadInfo + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$ThreadInfoImplCopyWith<_$ThreadInfoImpl> get copyWith => throw _privateConstructorUsedError; } diff --git a/data/lib/repository/geofence_repository.dart b/data/lib/repository/geofence_repository.dart index 876ef11b..cad20f9c 100644 --- a/data/lib/repository/geofence_repository.dart +++ b/data/lib/repository/geofence_repository.dart @@ -6,7 +6,6 @@ import 'package:data/service/space_service.dart'; import 'package:data/storage/app_preferences.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../api/auth/auth_models.dart'; -import '../service/geofence_service.dart'; import '../service/place_service.dart'; const GEOFENCE_ENTER = 1; @@ -25,26 +24,6 @@ class GeofenceRepository { GeofenceRepository(this.placeService, this.spaceService, this._currentUser); - void init() { - _listenForSpaceChange(_currentUser?.id ?? ''); - } - - void _listenForSpaceChange(String currentUserId) { - if (currentUserId.isEmpty) return; - try { - spaceService.getStreamPlacesByUserId(currentUserId).listen((places) { - if (places.isEmpty) { - logger.e('No places found for spaces.'); - return; - } - - GeofenceService.startMonitoring(places); - }); - } catch (error) { - logger.e('GeofenceRepository: error while get user space $error'); - } - } - void makeHttpCall(String placeId, int status) async { try { final spaces = await spaceService.getUserSpaces(_currentUser?.id ?? ''); diff --git a/data/lib/service/auth_service.dart b/data/lib/service/auth_service.dart index 94652ca8..c7605eb5 100644 --- a/data/lib/service/auth_service.dart +++ b/data/lib/service/auth_service.dart @@ -32,14 +32,14 @@ class AuthService { String? profileImg, int authType = LOGIN_TYPE_GOOGLE}) async { final data = await userService.saveUser( - uid: uid, - firebaseToken: firebaseToken, - phone: phone, - email: email, - firstName: firstName, - lastName: lastName, - profileImage: profileImg, - authType: authType, + uid: uid, + firebaseToken: firebaseToken, + phone: phone, + email: email, + firstName: firstName, + lastName: lastName, + profileImage: profileImg, + authType: authType, ); userJsonNotifier.state = (data['user'] as ApiUser).toJsonString(); userSessionJsonNotifier.state = @@ -53,17 +53,17 @@ class AuthService { userJsonNotifier.state = user.toJsonString(); } - Future getUser(String? userId) { - return userService.getUser(userId ?? _currentUser?.id ?? ''); + Future getUser(String userId) { + return userService.getUser(userId); } - Stream getUserStream({String? currentUserId}) { - return userService.getUserStream(currentUserId ?? _currentUser?.id ?? ''); + Stream getUserStream({required String currentUserId}) { + return userService.getUserStream(currentUserId); } - Future deleteAccount({String? currentUserId}) async { + Future deleteAccount({required String currentUserId}) async { userService.clearPreference(); - await userService.deleteUser(currentUserId ?? _currentUser?.id ?? ''); + await userService.deleteUser(currentUserId); } Future getUserNetworkStatus( @@ -74,8 +74,8 @@ class AuthService { if (user?.updated_at == null || DateTime.now() - .difference( - DateTime.fromMillisecondsSinceEpoch(user?.updated_at ?? DateTime.now().millisecondsSinceEpoch)) + .difference(DateTime.fromMillisecondsSinceEpoch( + user?.updated_at ?? DateTime.now().millisecondsSinceEpoch)) .inMinutes >= 3) { final callable = FirebaseFunctions.instanceFor(region: 'asia-south1') diff --git a/data/lib/service/location_manager.dart b/data/lib/service/location_manager.dart index 5af659aa..c10739cd 100644 --- a/data/lib/service/location_manager.dart +++ b/data/lib/service/location_manager.dart @@ -60,15 +60,19 @@ class LocationManager { } void startService() async { - await locationMethodChannel.invokeMethod('startTracking'); - await bgService.startService(); + if (Platform.isIOS) { + await locationMethodChannel.invokeMethod('startTracking'); + } + if (Platform.isAndroid) await bgService.startService(); } void stopTrackingService() async { - await locationMethodChannel.invokeMethod('stopTracking'); + if (Platform.isIOS) { + await locationMethodChannel.invokeMethod('stopTracking'); + } positionSubscription?.cancel(); positionSubscription = null; - bgService.invoke("stopService"); + if (Platform.isAndroid) bgService.invoke("stopService"); } Future _getUserIdFromPreferences() async { @@ -123,7 +127,10 @@ class LocationManager { } void _updateUserLocation(Position? position) { - if (position == null) return; + if (position == null || + (position.latitude == 0 && position.longitude == 0)) { + return; + } final locationData = LocationData( latitude: position.latitude, longitude: position.longitude, @@ -151,6 +158,13 @@ class LocationManager { } } + Future saveLocation(LocationData locationPosition) async { + final userId = await _getUserIdFromPreferences(); + if (userId != null) { + await _locationService.saveCurrentLocation(userId, locationPosition); + } + } + void onCurrentStateChangeRequest(String userId) async { final lastKnownJourney = await _journeyRepository.getLastKnownLocation(userId, null); diff --git a/data/lib/service/space_service.dart b/data/lib/service/space_service.dart index 1bf10367..42d3f8e9 100644 --- a/data/lib/service/space_service.dart +++ b/data/lib/service/space_service.dart @@ -61,8 +61,8 @@ class SpaceService { currentSpaceId = spaceId; } - Stream> streamAllSpace() { - return streamUserSpaces(currentUser?.id ?? '').switchMap((spaces) { + Stream> streamAllSpace(String userId) { + return streamUserSpaces(userId).switchMap((spaces) { if (spaces.isEmpty) { return Stream.value([]); } @@ -171,8 +171,7 @@ class SpaceService { await spaceService.enableLocation(spaceId, userId, locationEnabled); } - Future deleteUserSpaces() async { - final userId = currentUser?.id ?? ''; + Future deleteUserSpaces(String userId) async { final allSpace = await getUserSpaces(userId); if (allSpace.isEmpty) return; final ownSpace = @@ -187,19 +186,19 @@ class SpaceService { for (final space in joinedSpace) { await spaceService.removeUserFromSpace(space!.id, userId); } - _currentSpaceIdController.state = null; + currentSpaceId = null; } Future deleteSpace(String spaceId) async { await spaceInvitationService.deleteInvitations(spaceId); await spaceService.deleteSpace(spaceId); - _currentSpaceIdController.state = null; + currentSpaceId = null; } Future leaveSpace(String spaceId, {String? userId}) async { final currentUserId = currentUser?.id ?? ''; await spaceService.removeUserFromSpace(spaceId, userId ?? currentUserId); - _currentSpaceIdController.state = null; + currentSpaceId = null; } Future updateSpace(ApiSpace newSpace) async {