Skip to content

Commit e53d688

Browse files
committed
feat: Implement EditHeadlineBloc
- Manages headline editing state - Handles loading data - Handles form submissions - Implements state updates
1 parent b231dd8 commit e53d688

File tree

1 file changed

+200
-0
lines changed

1 file changed

+200
-0
lines changed
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
import 'package:bloc/bloc.dart';
2+
import 'package:equatable/equatable.dart';
3+
import 'package:flutter/foundation.dart' hide Category;
4+
import 'package:ht_data_repository/ht_data_repository.dart';
5+
import 'package:ht_shared/ht_shared.dart';
6+
7+
part 'edit_headline_event.dart';
8+
part 'edit_headline_state.dart';
9+
10+
/// A BLoC to manage the state of editing a single headline.
11+
class EditHeadlineBloc extends Bloc<EditHeadlineEvent, EditHeadlineState> {
12+
/// {@macro edit_headline_bloc}
13+
EditHeadlineBloc({
14+
required HtDataRepository<Headline> headlinesRepository,
15+
required HtDataRepository<Source> sourcesRepository,
16+
required HtDataRepository<Category> categoriesRepository,
17+
required String headlineId,
18+
}) : _headlinesRepository = headlinesRepository,
19+
_sourcesRepository = sourcesRepository,
20+
_categoriesRepository = categoriesRepository,
21+
_headlineId = headlineId,
22+
super(const EditHeadlineState()) {
23+
on<EditHeadlineLoaded>(_onLoaded);
24+
on<EditHeadlineTitleChanged>(_onTitleChanged);
25+
on<EditHeadlineDescriptionChanged>(_onDescriptionChanged);
26+
on<EditHeadlineUrlChanged>(_onUrlChanged);
27+
on<EditHeadlineImageUrlChanged>(_onImageUrlChanged);
28+
on<EditHeadlineSourceChanged>(_onSourceChanged);
29+
on<EditHeadlineCategoryChanged>(_onCategoryChanged);
30+
on<EditHeadlineSubmitted>(_onSubmitted);
31+
}
32+
33+
final HtDataRepository<Headline> _headlinesRepository;
34+
final HtDataRepository<Source> _sourcesRepository;
35+
final HtDataRepository<Category> _categoriesRepository;
36+
final String _headlineId;
37+
38+
Future<void> _onLoaded(
39+
EditHeadlineLoaded event,
40+
Emitter<EditHeadlineState> emit,
41+
) async {
42+
emit(state.copyWith(status: EditHeadlineStatus.loading));
43+
try {
44+
final [
45+
headlineResponse,
46+
sourcesResponse,
47+
categoriesResponse,
48+
] = await Future.wait([
49+
_headlinesRepository.read(id: _headlineId),
50+
_sourcesRepository.readAll(),
51+
_categoriesRepository.readAll(),
52+
]);
53+
54+
final headline = headlineResponse as Headline;
55+
final sources = (sourcesResponse as PaginatedResponse<Source>).items;
56+
final categories =
57+
(categoriesResponse as PaginatedResponse<Category>).items;
58+
59+
emit(
60+
state.copyWith(
61+
status: EditHeadlineStatus.initial,
62+
initialHeadline: headline,
63+
title: headline.title,
64+
description: headline.description ?? '',
65+
url: headline.url ?? '',
66+
imageUrl: headline.imageUrl ?? '',
67+
source: () => headline.source,
68+
category: () => headline.category,
69+
sources: sources,
70+
categories: categories,
71+
),
72+
);
73+
} on HtHttpException catch (e) {
74+
emit(
75+
state.copyWith(
76+
status: EditHeadlineStatus.failure,
77+
errorMessage: e.message,
78+
),
79+
);
80+
} catch (e) {
81+
emit(
82+
state.copyWith(
83+
status: EditHeadlineStatus.failure,
84+
errorMessage: e.toString(),
85+
),
86+
);
87+
}
88+
}
89+
90+
void _onTitleChanged(
91+
EditHeadlineTitleChanged event,
92+
Emitter<EditHeadlineState> emit,
93+
) {
94+
emit(
95+
state.copyWith(title: event.title, status: EditHeadlineStatus.initial),
96+
);
97+
}
98+
99+
void _onDescriptionChanged(
100+
EditHeadlineDescriptionChanged event,
101+
Emitter<EditHeadlineState> emit,
102+
) {
103+
emit(
104+
state.copyWith(
105+
description: event.description,
106+
status: EditHeadlineStatus.initial,
107+
),
108+
);
109+
}
110+
111+
void _onUrlChanged(
112+
EditHeadlineUrlChanged event,
113+
Emitter<EditHeadlineState> emit,
114+
) {
115+
emit(state.copyWith(url: event.url, status: EditHeadlineStatus.initial));
116+
}
117+
118+
void _onImageUrlChanged(
119+
EditHeadlineImageUrlChanged event,
120+
Emitter<EditHeadlineState> emit,
121+
) {
122+
emit(
123+
state.copyWith(
124+
imageUrl: event.imageUrl,
125+
status: EditHeadlineStatus.initial,
126+
),
127+
);
128+
}
129+
130+
void _onSourceChanged(
131+
EditHeadlineSourceChanged event,
132+
Emitter<EditHeadlineState> emit,
133+
) {
134+
emit(
135+
state.copyWith(
136+
source: () => event.source,
137+
status: EditHeadlineStatus.initial,
138+
),
139+
);
140+
}
141+
142+
void _onCategoryChanged(
143+
EditHeadlineCategoryChanged event,
144+
Emitter<EditHeadlineState> emit,
145+
) {
146+
emit(
147+
state.copyWith(
148+
category: () => event.category,
149+
status: EditHeadlineStatus.initial,
150+
),
151+
);
152+
}
153+
154+
Future<void> _onSubmitted(
155+
EditHeadlineSubmitted event,
156+
Emitter<EditHeadlineState> emit,
157+
) async {
158+
if (!state.isFormValid) return;
159+
160+
final initialHeadline = state.initialHeadline;
161+
if (initialHeadline == null) {
162+
emit(
163+
state.copyWith(
164+
status: EditHeadlineStatus.failure,
165+
errorMessage: 'Cannot update: Original headline data not loaded.',
166+
),
167+
);
168+
return;
169+
}
170+
171+
emit(state.copyWith(status: EditHeadlineStatus.submitting));
172+
try {
173+
final updatedHeadline = initialHeadline.copyWith(
174+
title: state.title,
175+
description: state.description.isNotEmpty ? state.description : null,
176+
url: state.url.isNotEmpty ? state.url : null,
177+
imageUrl: state.imageUrl.isNotEmpty ? state.imageUrl : null,
178+
source: state.source,
179+
category: state.category,
180+
);
181+
182+
await _headlinesRepository.update(id: _headlineId, item: updatedHeadline);
183+
emit(state.copyWith(status: EditHeadlineStatus.success));
184+
} on HtHttpException catch (e) {
185+
emit(
186+
state.copyWith(
187+
status: EditHeadlineStatus.failure,
188+
errorMessage: e.message,
189+
),
190+
);
191+
} catch (e) {
192+
emit(
193+
state.copyWith(
194+
status: EditHeadlineStatus.failure,
195+
errorMessage: e.toString(),
196+
),
197+
);
198+
}
199+
}
200+
}

0 commit comments

Comments
 (0)