Line data Source code
1 : // ignore_for_file: avoid_bool_literals_in_conditional_expressions, always_specify_types
2 :
3 : import 'dart:async';
4 : import 'dart:io';
5 :
6 : import 'package:flutter_map/flutter_map.dart';
7 : import 'package:equatable/equatable.dart';
8 : import 'package:flutter_bloc/flutter_bloc.dart';
9 : import 'package:latlong2/latlong.dart';
10 : import 'package:memories_app/routes/create_story/create_story_repository.dart';
11 : import 'package:memories_app/routes/create_story/model/story_request_model.dart';
12 : import 'package:memories_app/routes/create_story/model/create_story_response_model.dart';
13 : import 'package:memories_app/routes/story_detail/model/tag_model.dart';
14 :
15 : part 'create_story_event.dart';
16 :
17 : part 'create_story_state.dart';
18 :
19 : class _Constants {
20 : static const String offlineMessage =
21 : 'You are currently offline.\n Please check your internet connection!';
22 : }
23 :
24 : class CreateStoryBloc extends Bloc<CreateStoryEvent, CreateStoryState> {
25 : final CreateStoryRepository _repository;
26 :
27 1 : CreateStoryBloc({required CreateStoryRepository repository})
28 : : _repository = repository,
29 1 : super(const CreateStoryState()) {
30 2 : on<CreateStoryCreateStoryEvent>(_createStoryEvent);
31 2 : on<CreateStoryErrorPopupClosedEvent>(_onErrorPopupClosed);
32 : }
33 :
34 : late StoryRequestModel storyModel;
35 :
36 1 : Future<void> _createStoryEvent(
37 : CreateStoryCreateStoryEvent event, Emitter<CreateStoryState> emit) async {
38 2 : storyModel = _createStoryModel(event);
39 : CreateStoryResponseModel? response;
40 : try {
41 3 : response = await _repository.createStory(storyModel);
42 0 : } on SocketException {
43 0 : emit(const CreateStoryOffline(offlineMessage: _Constants.offlineMessage));
44 : } catch (error) {
45 0 : emit(CreateStoryFailure(error: error.toString()));
46 : }
47 : if (response != null) {
48 2 : if (response.success == true) {
49 1 : emit(const CreateStorySuccess());
50 : } else {
51 4 : emit(CreateStoryFailure(error: response.msg.toString()));
52 : }
53 : }
54 : }
55 :
56 1 : StoryRequestModel _createStoryModel(CreateStoryCreateStoryEvent event) {
57 3 : final String dateType = mapDateTypeToValue(event.dateType.toLowerCase());
58 1 : StoryRequestModel createStoryModel = StoryRequestModel(
59 1 : title: event.title,
60 1 : content: event.content,
61 1 : storyTags: event.storyTags,
62 1 : locationIds: createLocationId(
63 1 : event.markersForPoint,
64 1 : event.circleMarkers,
65 1 : event.polygons,
66 1 : event.polyLines,
67 1 : event.pointAdresses,
68 1 : event.circleAdresses,
69 1 : event.polylineAdresses),
70 : dateType: dateType,
71 1 : date: dateType == "normal_date" ? event.date : null,
72 1 : decade: dateType == "decade" ? extractDecade(event.decade) : null,
73 1 : endDate: dateType == "interval_date" ? event.endDate : null,
74 1 : endYear: dateType == "year_interval" ? event.endYear : null,
75 1 : includeTime: _includeTime(dateType, event) ? true : false,
76 1 : seasonName: dateType == "year" || dateType == "year_interval"
77 1 : ? event.seasonName
78 : : null,
79 1 : startDate: dateType == "interval_date" ? event.startDate : null,
80 1 : startYear: dateType == "year_interval" ? event.startYear : null,
81 2 : year: dateType == "year" ? event.year : null,
82 : );
83 : return createStoryModel;
84 : }
85 :
86 1 : bool _includeTime(String dateType, CreateStoryCreateStoryEvent event) {
87 1 : return dateType.contains("normal_date") &&
88 0 : (event.startDate != null &&
89 0 : event.startDate!.isNotEmpty &&
90 0 : event.startDate!.contains(" ")) ||
91 1 : (event.date != null &&
92 0 : event.date!.isNotEmpty &&
93 0 : event.date!.contains(" ")) ||
94 1 : (event.endDate != null &&
95 0 : event.endDate!.isNotEmpty &&
96 0 : event.endDate!.contains(" "));
97 : }
98 :
99 1 : List<LocationId> createLocationId(
100 : List<Marker>? markersForPoint,
101 : List<CircleMarker>? circleMarkers,
102 : List<Polygon>? polygons,
103 : List<Polyline>? polyLines,
104 : List<String>? pointAdresses,
105 : List<String>? circleAdresses,
106 : List<String>? polylineAdresses) {
107 1 : List<LocationId> locationIds = [];
108 1 : if (markersForPoint != null && markersForPoint.isNotEmpty) {
109 1 : createPointLocations(markersForPoint, pointAdresses, locationIds);
110 : }
111 :
112 1 : if (circleMarkers != null && circleMarkers.isNotEmpty) {
113 1 : createCircleLocations(circleMarkers, circleAdresses, locationIds);
114 : }
115 1 : if (polygons != null && polygons.isNotEmpty) {
116 1 : createPolygonLocations(polygons, locationIds);
117 : }
118 :
119 1 : if (polyLines != null && polyLines.isNotEmpty) {
120 1 : createPolylineLocations(polyLines, polylineAdresses, locationIds);
121 : }
122 :
123 : return locationIds;
124 : }
125 :
126 1 : void createPolylineLocations(List<Polyline> polyLines,
127 : List<String>? polylineAdresses, List<LocationId> locationIds) {
128 3 : for (int i = 0; i < polyLines.length; i++) {
129 1 : List<List<double>> coordinates = [];
130 :
131 3 : if (polyLines[i].points.isNotEmpty) {
132 3 : for (LatLng point in polyLines[i].points) {
133 3 : List<double> coordinatesList = [point.longitude, point.latitude];
134 1 : coordinates.add(coordinatesList);
135 : }
136 : }
137 :
138 1 : LocationId locationId = LocationId(
139 1 : name: polylineAdresses != null && polylineAdresses.isNotEmpty
140 1 : ? polylineAdresses[i]
141 : : "Address not found",
142 1 : line: LineStringLocation(coordinates: coordinates),
143 : );
144 :
145 1 : locationIds.add(locationId);
146 : }
147 : }
148 :
149 1 : void createPolygonLocations(
150 : List<Polygon> polygons, List<LocationId> locationIds) {
151 3 : for (int i = 0; i < polygons.length; i++) {
152 1 : List<List<List<double>>> coordinates = [];
153 :
154 3 : if (polygons[i].points.isNotEmpty) {
155 1 : List<List<double>> singlePolygon = [];
156 :
157 3 : LatLng firstPoint = polygons[i].points.first;
158 1 : List<double> firstCoordinates = [
159 1 : firstPoint.longitude,
160 1 : firstPoint.latitude
161 : ];
162 :
163 3 : for (LatLng point in polygons[i].points) {
164 3 : List<double> coordinatesList = [point.longitude, point.latitude];
165 1 : singlePolygon.add(coordinatesList);
166 : }
167 :
168 : // Add the first coordinate again to close the polygon
169 1 : singlePolygon.add(firstCoordinates);
170 :
171 1 : coordinates.add(singlePolygon);
172 : }
173 :
174 1 : LocationId polygon = LocationId(
175 2 : name: polygons[i].label ?? "Address not found",
176 1 : polygon: PolygonLocation(coordinates: coordinates),
177 : );
178 :
179 1 : locationIds.add(polygon);
180 : }
181 : }
182 :
183 1 : void createCircleLocations(List<CircleMarker> circleMarkers,
184 : List<String>? circleAdresses, List<LocationId> locationIds) {
185 3 : for (int i = 0; i < circleMarkers.length; i++) {
186 1 : LocationId circleLocation = LocationId(
187 1 : name: circleAdresses != null && circleAdresses.isNotEmpty
188 1 : ? circleAdresses[i]
189 : : "Adress not found",
190 1 : circle: PointLocation(
191 1 : coordinates: [
192 3 : circleMarkers[i].point.longitude,
193 3 : circleMarkers[i].point.latitude
194 : ],
195 : ),
196 4 : radius: double.parse((circleMarkers[i].radius).toStringAsFixed(10)),
197 : );
198 1 : locationIds.add(circleLocation);
199 : }
200 : }
201 :
202 1 : void createPointLocations(List<Marker> markersForPoint,
203 : List<String>? pointAdresses, List<LocationId> locationIds) {
204 3 : for (int i = 0; i < markersForPoint.length; i++) {
205 1 : LocationId pointLocation = LocationId(
206 1 : name: pointAdresses != null && pointAdresses.isNotEmpty
207 1 : ? pointAdresses[i]
208 : : "Adress not found",
209 1 : point: PointLocation(
210 1 : coordinates: [
211 3 : markersForPoint[i].point.longitude,
212 3 : markersForPoint[i].point.latitude
213 : ],
214 : ),
215 : );
216 1 : locationIds.add(pointLocation);
217 : }
218 : }
219 :
220 1 : void _onErrorPopupClosed(
221 : CreateStoryErrorPopupClosedEvent event, Emitter<CreateStoryState> emit) {
222 1 : emit(const CreateStoryState());
223 : }
224 :
225 1 : int? extractDecade(String? decade) {
226 1 : if (decade == null || decade.isEmpty) {
227 : return null;
228 : }
229 :
230 : // Remove trailing 's' and parse the remaining part as an integer
231 3 : String numericPart = decade.substring(0, decade.length - 1);
232 1 : return int.tryParse(numericPart);
233 : }
234 :
235 1 : String mapDateTypeToValue(String selectedDateType) {
236 1 : switch (selectedDateType.toLowerCase()) {
237 1 : case "year":
238 : return "year";
239 1 : case "interval year":
240 : return "year_interval";
241 1 : case "normal date":
242 : return "normal_date";
243 1 : case "interval date":
244 : return "interval_date";
245 1 : case "decade":
246 : return "decade";
247 : default:
248 : return "year";
249 : }
250 : }
251 : }
252 :
253 : enum StoryDateType {
254 : year,
255 : yearInterval,
256 : normalDate,
257 : intervalDate,
258 : decade,
259 : }
|