Skip to content

Commit 3721a64

Browse files
authored
Merge pull request #405 from DevKor-github/codexd/fix-alarm-screen-step-order
Normalize preparation step order before alarm rendering
2 parents f37f517 + 62d0fdb commit 3721a64

5 files changed

Lines changed: 147 additions & 64 deletions

File tree

lib/data/models/get_preparation_step_response_model.dart

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -48,38 +48,7 @@ class GetPreparationStepResponseModel {
4848
extension PreparationResponseModelListExtension
4949
on List<GetPreparationStepResponseModel> {
5050
PreparationEntity toPreparationEntity() {
51-
if (isEmpty) {
52-
return PreparationEntity(preparationStepList: []);
53-
}
54-
55-
final modelsById = {
56-
for (final model in this) model.id: model,
57-
};
58-
final referencedIds = {
59-
for (final model in this)
60-
if (model.nextPreparationId != null) model.nextPreparationId!,
61-
};
62-
final firstModel = firstWhere(
63-
(model) => !referencedIds.contains(model.id),
64-
orElse: () => first,
65-
);
66-
67-
final orderedModels = <GetPreparationStepResponseModel>[];
68-
final visitedIds = <String>{};
69-
GetPreparationStepResponseModel? currentModel = firstModel;
70-
while (currentModel != null && visitedIds.add(currentModel.id)) {
71-
orderedModels.add(currentModel);
72-
final nextId = currentModel.nextPreparationId;
73-
currentModel = nextId == null ? null : modelsById[nextId];
74-
}
75-
76-
for (final model in this) {
77-
if (visitedIds.add(model.id)) {
78-
orderedModels.add(model);
79-
}
80-
}
81-
82-
final steps = orderedModels.map((model) => model.toEntity()).toList();
83-
return PreparationEntity(preparationStepList: steps);
51+
final steps = map((model) => model.toEntity()).toList();
52+
return PreparationEntity(preparationStepList: steps).ordered;
8453
}
8554
}

lib/domain/entities/preparation_entity.dart

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,46 @@ class PreparationEntity extends Equatable {
1515
);
1616
}
1717

18+
PreparationEntity get ordered {
19+
if (preparationStepList.length <= 1) {
20+
return this;
21+
}
22+
23+
final stepById = {
24+
for (final step in preparationStepList) step.id: step,
25+
};
26+
final referencedIds = preparationStepList
27+
.map((step) => step.nextPreparationId)
28+
.whereType<String>()
29+
.toSet();
30+
31+
final firstStep = preparationStepList.firstWhere(
32+
(step) => !referencedIds.contains(step.id),
33+
orElse: () => preparationStepList.first,
34+
);
35+
36+
final orderedSteps = <PreparationStepEntity>[];
37+
final visitedIds = <String>{};
38+
PreparationStepEntity? currentStep = firstStep;
39+
40+
while (currentStep != null && visitedIds.add(currentStep.id)) {
41+
orderedSteps.add(currentStep);
42+
currentStep = stepById[currentStep.nextPreparationId];
43+
}
44+
45+
if (orderedSteps.length == preparationStepList.length) {
46+
return PreparationEntity(preparationStepList: orderedSteps);
47+
}
48+
49+
for (final step in preparationStepList) {
50+
if (visitedIds.add(step.id)) {
51+
orderedSteps.add(step);
52+
}
53+
}
54+
55+
return PreparationEntity(preparationStepList: orderedSteps);
56+
}
57+
1858
@override
1959
String toString() {
2060
return 'PreparationEntity(preparationStepList: ${preparationStepList.toString()})';

lib/domain/entities/preparation_with_time_entity.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ class PreparationWithTimeEntity extends PreparationEntity implements Equatable {
1010

1111
factory PreparationWithTimeEntity.fromPreparation(
1212
PreparationEntity preparation) {
13+
final orderedPreparation = preparation.ordered;
1314
return PreparationWithTimeEntity(
14-
preparationStepList: preparation.preparationStepList
15+
preparationStepList: orderedPreparation.preparationStepList
1516
.map(
1617
(step) => PreparationStepWithTimeEntity(
1718
id: step.id,

test/data/data_sources/preparation_remote_data_source_test.dart

Lines changed: 68 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -158,38 +158,76 @@ void main() {
158158
});
159159

160160
group('getPreparationByScheduleId', () {
161-
test('should return PreparationEntity when the response is successful',
161+
test('should return PreparationEntity ordered by nextPreparationId',
162162
() async {
163163
// arrange
164-
// when(dio.get(Endpoint.getPreparationByScheduleId(scheduleId))).thenAnswer(
165-
// (_) async => Response(
166-
// statusCode: 200,
167-
// data: [
168-
// {
169-
// "preparationId": preparationStep1.id,
170-
// "preparationName": preparationStep1.preparationName,
171-
// "preparationTime": preparationStep1.preparationTime.inMinutes,
172-
// "nextPreparationId": preparationStep1.nextPreparationId,
173-
// },
174-
// {
175-
// "preparationId": preparationStep2.id,
176-
// "preparationName": preparationStep2.preparationName,
177-
// "preparationTime": preparationStep2.preparationTime.inMinutes,
178-
// "nextPreparationId": preparationStep2.nextPreparationId,
179-
// },
180-
// ],
181-
// requestOptions: RequestOptions(
182-
// path: Endpoint.getPreparationByScheduleId(scheduleId),
183-
// ),
184-
// ),
185-
// );
186-
187-
// // act
188-
// final result =
189-
// await remoteDataSource.getPreparationByScheduleId(scheduleId);
190-
191-
// // assert
192-
// expect(result.preparationStepList.length, 2);
164+
final orderedFirstStep = PreparationStepEntity(
165+
id: uuid.v7(),
166+
preparationName: 'Shower',
167+
preparationTime: const Duration(minutes: 10),
168+
nextPreparationId: null,
169+
);
170+
final orderedSecondStep = PreparationStepEntity(
171+
id: uuid.v7(),
172+
preparationName: 'Dress',
173+
preparationTime: const Duration(minutes: 5),
174+
nextPreparationId: null,
175+
);
176+
final orderedThirdStep = PreparationStepEntity(
177+
id: uuid.v7(),
178+
preparationName: 'Pack bag',
179+
preparationTime: const Duration(minutes: 3),
180+
nextPreparationId: null,
181+
);
182+
final linkedFirstStep =
183+
orderedFirstStep.copyWith(nextPreparationId: orderedSecondStep.id);
184+
final linkedSecondStep =
185+
orderedSecondStep.copyWith(nextPreparationId: orderedThirdStep.id);
186+
187+
when(dio.get(Endpoint.getPreparationByScheduleId(scheduleId))).thenAnswer(
188+
(_) async => Response(
189+
statusCode: 200,
190+
data: {
191+
'data': [
192+
{
193+
'preparationId': orderedThirdStep.id,
194+
'preparationName': orderedThirdStep.preparationName,
195+
'preparationTime': orderedThirdStep.preparationTime.inMinutes,
196+
'nextPreparationId': orderedThirdStep.nextPreparationId,
197+
},
198+
{
199+
'preparationId': linkedFirstStep.id,
200+
'preparationName': linkedFirstStep.preparationName,
201+
'preparationTime': linkedFirstStep.preparationTime.inMinutes,
202+
'nextPreparationId': linkedFirstStep.nextPreparationId,
203+
},
204+
{
205+
'preparationId': linkedSecondStep.id,
206+
'preparationName': linkedSecondStep.preparationName,
207+
'preparationTime': linkedSecondStep.preparationTime.inMinutes,
208+
'nextPreparationId': linkedSecondStep.nextPreparationId,
209+
},
210+
],
211+
},
212+
requestOptions: RequestOptions(
213+
path: Endpoint.getPreparationByScheduleId(scheduleId),
214+
),
215+
),
216+
);
217+
218+
// act
219+
final result =
220+
await remoteDataSource.getPreparationByScheduleId(scheduleId);
221+
222+
// assert
223+
expect(
224+
result.preparationStepList.map((step) => step.id).toList(),
225+
[
226+
orderedFirstStep.id,
227+
orderedSecondStep.id,
228+
orderedThirdStep.id,
229+
],
230+
);
193231
});
194232

195233
test('should throw an exception when the response code is not 200',

test/domain/entities/preparation_timing_entity_test.dart

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,47 @@
11
import 'package:flutter_test/flutter_test.dart';
22
import 'package:on_time_front/domain/entities/place_entity.dart';
3+
import 'package:on_time_front/domain/entities/preparation_entity.dart';
4+
import 'package:on_time_front/domain/entities/preparation_step_entity.dart';
35
import 'package:on_time_front/domain/entities/preparation_step_with_time_entity.dart';
46
import 'package:on_time_front/domain/entities/preparation_with_time_entity.dart';
57
import 'package:on_time_front/domain/entities/schedule_with_preparation_entity.dart';
68
import 'package:on_time_front/presentation/app/bloc/schedule/schedule_bloc.dart';
79

810
void main() {
911
group('Preparation timing entities', () {
12+
test('orders linked preparation steps before timing conversion', () {
13+
final unordered = PreparationEntity(
14+
preparationStepList: const [
15+
PreparationStepEntity(
16+
id: 's3',
17+
preparationName: 'pack',
18+
preparationTime: Duration(minutes: 5),
19+
nextPreparationId: null,
20+
),
21+
PreparationStepEntity(
22+
id: 's1',
23+
preparationName: 'wash',
24+
preparationTime: Duration(minutes: 10),
25+
nextPreparationId: 's2',
26+
),
27+
PreparationStepEntity(
28+
id: 's2',
29+
preparationName: 'dress',
30+
preparationTime: Duration(minutes: 10),
31+
nextPreparationId: 's3',
32+
),
33+
],
34+
);
35+
36+
final preparation = PreparationWithTimeEntity.fromPreparation(unordered);
37+
38+
expect(
39+
preparation.preparationStepList.map((step) => step.id).toList(),
40+
['s1', 's2', 's3'],
41+
);
42+
expect(preparation.currentStep?.id, 's1');
43+
});
44+
1045
final preparation = PreparationWithTimeEntity(
1146
preparationStepList: const [
1247
PreparationStepWithTimeEntity(

0 commit comments

Comments
 (0)