Skip to content

Commit

Permalink
Merge pull request #198 from DELTSV/enzo-fixes
Browse files Browse the repository at this point in the history
Fixing users tracking
  • Loading branch information
enzoSoa authored Jul 14, 2024
2 parents 10d30f1 + f11436a commit b413561
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 143 deletions.
166 changes: 23 additions & 143 deletions packages/app/lib/event/widgets/map/journey_map.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hollybike/journey/service/journey_position_manager.dart';
import 'package:hollybike/journey/type/minimal_journey.dart';
import 'package:hollybike/positions/bloc/user_positions/user_positions_bloc.dart';
import 'package:hollybike/positions/bloc/user_positions/user_positions_state.dart';
import 'package:hollybike/shared/types/geojson.dart';
import 'package:hollybike/shared/utils/waiter.dart';
import 'package:hollybike/shared/websocket/recieve/websocket_receive_position.dart';
Expand All @@ -32,7 +31,6 @@ class JourneyMap extends StatefulWidget {
}

class _JourneyMapState extends State<JourneyMap> {
late final List<int> placeholderProfilePicture;
Map<int, PointAnnotation> currentPositions = {};
bool _mapLoading = true;

Expand Down Expand Up @@ -71,13 +69,6 @@ class _JourneyMapState extends State<JourneyMap> {
@override
void initState() {
super.initState();
rootBundle
.load(
"assets/images/placeholder_map_pin.png",
)
.then((ressource) {
placeholderProfilePicture = ressource.buffer.asUint8List();
});
}

Future<String> _getGeoJsonData(String fileUrl) async {
Expand Down Expand Up @@ -155,28 +146,30 @@ class _JourneyMapState extends State<JourneyMap> {
'[ "interpolate", ["linear"], ["zoom"], 15, 0, 15.05, ["get", "min_height"] ]',
),
map.annotations.createPointAnnotationManager().then(
(pointManager) {
final userPositionsBloc =
BlocProvider.of<UserPositionsBloc>(context);
_updateMap(
pointManager,
userPositionsBloc.state,
(pointManager) async {
final journeyPositionManager = JourneyPositionManager(
pointManager: pointManager,
context: context,
);
userPositionsBloc.stream.listen(
(state) {
_updateMap(
pointManager,
state,
);

_setCameraOptions(
geoJsonRaw,
state.userPositions,
map,
updateMode: true,
);
},
final userPositionsBloc = BlocProvider.of<UserPositionsBloc>(
context,
);

Timer(const Duration(seconds: 1), () {
journeyPositionManager.updatePositions(userPositionsBloc.state.userPositions);

userPositionsBloc.stream.listen(
(state) {
journeyPositionManager.updatePositions(state.userPositions);
_setCameraOptions(
geoJsonRaw,
state.userPositions,
map,
updateMode: true,
);
},
);
});
},
),
]);
Expand Down Expand Up @@ -270,117 +263,4 @@ class _JourneyMapState extends State<JourneyMap> {

return cameraOptions;
}

void _updateMap(
PointAnnotationManager pointManager,
UserPositionsState userPositionsState,
) async {
final missingPositions = <WebsocketReceivePosition>[];
final alreadyAddedPositions = <WebsocketReceivePosition>[];

for (final position in userPositionsState.userPositions) {
if (currentPositions.containsKey(position.userId)) {
alreadyAddedPositions.add(position);
} else {
missingPositions.add(position);
}
}

for (final userId in currentPositions.keys) {
if (!userPositionsState.userPositions.any((p) => p.userId == userId)) {
final point = currentPositions[userId];

if (point != null) {
await pointManager.delete(point);
}
}
}

final addedPositions = (await Future.wait(
missingPositions
.map((position) => _addMapPosition(pointManager, position)),
))
.whereType<MapEntry<int, PointAnnotation>>();

final updatedPositions = await Future.wait(
alreadyAddedPositions
.map((position) => _updateMapPosition(pointManager, position)),
);

setState(() {
currentPositions = Map.fromEntries(
[...addedPositions, ...updatedPositions],
);
});
}

Future<MapEntry<int, PointAnnotation>?> _addMapPosition(
PointAnnotationManager pointManager,
WebsocketReceivePosition missingPosition,
) async {
final colorScheme = Theme.of(context).colorScheme;

final user = BlocProvider.of<UserPositionsBloc>(context)
.getPositionUser(missingPosition);
if (user is! UserLoadSuccessEvent) return null;

final profilePicture =
BlocProvider.of<UserPositionsBloc>(context).getUserPicture(user);

final options = PointAnnotationOptions(
geometry: Point(
coordinates: Position(
missingPosition.longitude,
missingPosition.latitude,
),
),
image: (profilePicture is UserPictureLoadSuccessEvent
? profilePicture.image
: placeholderProfilePicture) as Uint8List,
iconAnchor: IconAnchor.BOTTOM,
textField:
'${user.user.username}\n${(missingPosition.speed * 3.6).round()} km/h',
textAnchor: TextAnchor.TOP,
textSize: 12,
textHaloWidth: 2,
textHaloColor: colorScheme.primary.value,
textColor: colorScheme.onPrimary.value,
);
final point = await pointManager.create(options);

return MapEntry(missingPosition.userId, point);
}

Future<MapEntry<int, PointAnnotation>> _updateMapPosition(
PointAnnotationManager pointManager,
WebsocketReceivePosition positionToUpdate,
) async {
final point = currentPositions[positionToUpdate.userId];
if (point == null) {
throw Exception("Cannot find point for key $positionToUpdate");
}

point.geometry = Point(
coordinates: Position(
positionToUpdate.longitude,
positionToUpdate.latitude,
),
);

final user = BlocProvider.of<UserPositionsBloc>(context)
.getPositionUser(positionToUpdate);
if (user is UserLoadSuccessEvent) {
final profilePicture =
BlocProvider.of<UserPositionsBloc>(context).getUserPicture(user);
point.image = (profilePicture is UserPictureLoadSuccessEvent
? profilePicture.image
: placeholderProfilePicture) as Uint8List;

point.textField =
'${user.user.username}\n${(positionToUpdate.speed * 3.6).round()} km/h';
}

await pointManager.update(point);
return MapEntry(positionToUpdate.userId, point);
}
}
134 changes: 134 additions & 0 deletions packages/app/lib/journey/service/journey_position_manager.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hollybike/shared/websocket/recieve/websocket_receive_position.dart';
import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart';

import '../../positions/bloc/user_positions/user_positions_bloc.dart';

class JourneyPositionManager {
late final Map<int, PointAnnotation?> _points;
late final List<int> _placeholderProfilePicture;
final PointAnnotationManager pointManager;
final BuildContext context;

JourneyPositionManager({required this.pointManager, required this.context}) {
_points = {};

rootBundle
.load(
"assets/images/placeholder_map_pin.png",
)
.then(
(ressource) {
_placeholderProfilePicture = ressource.buffer.asUint8List();
},
);
}

void updatePositions(List<WebsocketReceivePosition> positions) async {
final missingPositions = <WebsocketReceivePosition>[];
final alreadyAddedPositions = <WebsocketReceivePosition>[];

for (final position in positions) {
if (_points.containsKey(position.userId) &&
_points[position.userId] is PointAnnotation) {
alreadyAddedPositions.add(position);
} else {
missingPositions.add(position);
}
}

for (final userId in _points.keys) {
if (!positions.any((p) => p.userId == userId)) {
final point = _points[userId];
if (point != null) {
await pointManager.delete(point);
_points[userId] = null;
}
}
}

await Future.wait(
missingPositions.map((position) => _addMapPosition(position)),
);

await Future.wait(
alreadyAddedPositions.map(
(position) => _updateMapPosition(position),
),
);

for (final position in positions) {
_updateTitles(position);
}
}

Future<void> _addMapPosition(WebsocketReceivePosition missingPosition) async {
final colorScheme = Theme.of(context).colorScheme;

final user = BlocProvider.of<UserPositionsBloc>(context)
.getPositionUser(missingPosition);
if (user is! UserLoadSuccessEvent) return;

final profilePicture =
BlocProvider.of<UserPositionsBloc>(context).getUserPicture(user);
if (user.user.profilePicture != null &&
profilePicture is! UserPictureLoadSuccessEvent) return;

final options = PointAnnotationOptions(
geometry: Point(
coordinates: Position(
missingPosition.longitude,
missingPosition.latitude,
),
),
iconSize: profilePicture is UserPictureLoadSuccessEvent ? 0.390625 : 1,
iconAnchor: IconAnchor.BOTTOM,
textField: "",
textAnchor: TextAnchor.TOP,
textSize: 12,
textHaloWidth: 2,
textHaloColor: colorScheme.primary.value,
textColor: colorScheme.onPrimary.value,
);

_points[missingPosition.userId] = await pointManager.create(options);
}

Future<void> _updateMapPosition(
WebsocketReceivePosition positionToUpdate,
) async {
final point = _points[positionToUpdate.userId];
if (point == null) return;

point.geometry.coordinates = Position(
positionToUpdate.longitude,
positionToUpdate.latitude,
);

await pointManager.update(point);
}

Future<void> _updateTitles(WebsocketReceivePosition position) async {
final user =
BlocProvider.of<UserPositionsBloc>(context).getPositionUser(position);
if (user is! UserLoadSuccessEvent) return;

final point = _points[user.user.id];
if (point == null) return;

final profilePicture =
BlocProvider.of<UserPositionsBloc>(context).getUserPicture(user);
if (user.user.profilePicture != null &&
profilePicture is! UserPictureLoadSuccessEvent) return;

point.textField =
'${user.user.username}\n${(position.speed * 3.6).round()} km/h';
point.image = (profilePicture is UserPictureLoadSuccessEvent
? profilePicture.image
: _placeholderProfilePicture) as Uint8List;

pointManager.update(point);
}
}

0 comments on commit b413561

Please sign in to comment.