Skip to content

Commit b714841

Browse files
authored
Merge pull request #21 from headlines-toolkit/feature_dashboard
Feature dashboard
2 parents 86e3ac6 + 40ce039 commit b714841

12 files changed

+853
-12
lines changed

lib/app/view/app.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import 'package:ht_dashboard/app/config/app_environment.dart';
1111
import 'package:ht_dashboard/app_configuration/bloc/app_configuration_bloc.dart';
1212
import 'package:ht_dashboard/authentication/bloc/authentication_bloc.dart';
1313
import 'package:ht_dashboard/content_management/bloc/content_management_bloc.dart';
14+
import 'package:ht_dashboard/dashboard/bloc/dashboard_bloc.dart';
1415
import 'package:ht_dashboard/l10n/app_localizations.dart';
1516
import 'package:ht_dashboard/router/router.dart';
1617
// Import for app_theme.dart
@@ -30,6 +31,7 @@ class App extends StatelessWidget {
3031
required HtDataRepository<UserContentPreferences>
3132
htUserContentPreferencesRepository,
3233
required HtDataRepository<AppConfig> htAppConfigRepository,
34+
required HtDataRepository<DashboardSummary> htDashboardSummaryRepository,
3335
required HtKVStorageService kvStorageService,
3436
required AppEnvironment environment,
3537
super.key,
@@ -42,6 +44,7 @@ class App extends StatelessWidget {
4244
_htUserContentPreferencesRepository = htUserContentPreferencesRepository,
4345
_htAppConfigRepository = htAppConfigRepository,
4446
_kvStorageService = kvStorageService,
47+
_htDashboardSummaryRepository = htDashboardSummaryRepository,
4548
_environment = environment;
4649

4750
final HtAuthRepository _htAuthenticationRepository;
@@ -53,6 +56,7 @@ class App extends StatelessWidget {
5356
final HtDataRepository<UserContentPreferences>
5457
_htUserContentPreferencesRepository;
5558
final HtDataRepository<AppConfig> _htAppConfigRepository;
59+
final HtDataRepository<DashboardSummary> _htDashboardSummaryRepository;
5660
final HtKVStorageService _kvStorageService;
5761
final AppEnvironment _environment;
5862

@@ -68,6 +72,7 @@ class App extends StatelessWidget {
6872
RepositoryProvider.value(value: _htUserAppSettingsRepository),
6973
RepositoryProvider.value(value: _htUserContentPreferencesRepository),
7074
RepositoryProvider.value(value: _htAppConfigRepository),
75+
RepositoryProvider.value(value: _htDashboardSummaryRepository),
7176
RepositoryProvider.value(value: _kvStorageService),
7277
],
7378
child: MultiBlocProvider(
@@ -98,6 +103,14 @@ class App extends StatelessWidget {
98103
sourcesRepository: context.read<HtDataRepository<Source>>(),
99104
),
100105
),
106+
BlocProvider(
107+
create: (context) => DashboardBloc(
108+
dashboardSummaryRepository: context
109+
.read<HtDataRepository<DashboardSummary>>(),
110+
appConfigRepository: context.read<HtDataRepository<AppConfig>>(),
111+
headlinesRepository: context.read<HtDataRepository<Headline>>(),
112+
),
113+
),
101114
],
102115
child: _AppView(
103116
htAuthenticationRepository: _htAuthenticationRepository,

lib/bootstrap.dart

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ Future<Widget> bootstrap(
6161
HtDataClient<UserContentPreferences> userContentPreferencesClient;
6262
HtDataClient<UserAppSettings> userAppSettingsClient;
6363
HtDataClient<AppConfig> appConfigClient;
64+
HtDataClient<DashboardSummary> dashboardSummaryClient;
6465

6566
if (appConfig.environment == app_config.AppEnvironment.demo) {
6667
headlinesClient = HtDataInMemory<Headline>(
@@ -96,6 +97,13 @@ Future<Widget> bootstrap(
9697
getId: (i) => i.id,
9798
initialData: [AppConfig.fromJson(appConfigFixtureData)],
9899
);
100+
dashboardSummaryClient = HtDataInMemory<DashboardSummary>(
101+
toJson: (i) => i.toJson(),
102+
getId: (i) => i.id,
103+
initialData: [
104+
DashboardSummary.fromJson(dashboardSummaryFixtureData),
105+
],
106+
);
99107
} else if (appConfig.environment == app_config.AppEnvironment.development) {
100108
headlinesClient = HtDataApi<Headline>(
101109
httpClient: httpClient!,
@@ -139,6 +147,12 @@ Future<Widget> bootstrap(
139147
fromJson: AppConfig.fromJson,
140148
toJson: (config) => config.toJson(),
141149
);
150+
dashboardSummaryClient = HtDataApi<DashboardSummary>(
151+
httpClient: httpClient,
152+
modelName: 'dashboard_summary',
153+
fromJson: DashboardSummary.fromJson,
154+
toJson: (summary) => summary.toJson(),
155+
);
142156
} else {
143157
headlinesClient = HtDataApi<Headline>(
144158
httpClient: httpClient!,
@@ -182,6 +196,12 @@ Future<Widget> bootstrap(
182196
fromJson: AppConfig.fromJson,
183197
toJson: (config) => config.toJson(),
184198
);
199+
dashboardSummaryClient = HtDataApi<DashboardSummary>(
200+
httpClient: httpClient,
201+
modelName: 'dashboard_summary',
202+
fromJson: DashboardSummary.fromJson,
203+
toJson: (summary) => summary.toJson(),
204+
);
185205
}
186206

187207
final headlinesRepository = HtDataRepository<Headline>(
@@ -204,6 +224,9 @@ Future<Widget> bootstrap(
204224
final appConfigRepository = HtDataRepository<AppConfig>(
205225
dataClient: appConfigClient,
206226
);
227+
final dashboardSummaryRepository = HtDataRepository<DashboardSummary>(
228+
dataClient: dashboardSummaryClient,
229+
);
207230

208231
return App(
209232
htAuthenticationRepository: authenticationRepository,
@@ -214,6 +237,7 @@ Future<Widget> bootstrap(
214237
htUserAppSettingsRepository: userAppSettingsRepository,
215238
htUserContentPreferencesRepository: userContentPreferencesRepository,
216239
htAppConfigRepository: appConfigRepository,
240+
htDashboardSummaryRepository: dashboardSummaryRepository,
217241
kvStorageService: kvStorage,
218242
environment: environment,
219243
);
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import 'package:bloc/bloc.dart';
2+
import 'package:equatable/equatable.dart';
3+
import 'package:ht_data_repository/ht_data_repository.dart';
4+
import 'package:ht_shared/ht_shared.dart';
5+
6+
part 'dashboard_event.dart';
7+
part 'dashboard_state.dart';
8+
9+
/// A BLoC to manage the state of the dashboard.
10+
class DashboardBloc extends Bloc<DashboardEvent, DashboardState> {
11+
/// {@macro dashboard_bloc}
12+
DashboardBloc({
13+
required HtDataRepository<DashboardSummary> dashboardSummaryRepository,
14+
required HtDataRepository<AppConfig> appConfigRepository,
15+
required HtDataRepository<Headline> headlinesRepository,
16+
}) : _dashboardSummaryRepository = dashboardSummaryRepository,
17+
_appConfigRepository = appConfigRepository,
18+
_headlinesRepository = headlinesRepository,
19+
super(const DashboardState()) {
20+
on<DashboardSummaryLoaded>(_onDashboardSummaryLoaded);
21+
}
22+
23+
final HtDataRepository<DashboardSummary> _dashboardSummaryRepository;
24+
final HtDataRepository<AppConfig> _appConfigRepository;
25+
final HtDataRepository<Headline> _headlinesRepository;
26+
27+
Future<void> _onDashboardSummaryLoaded(
28+
DashboardSummaryLoaded event,
29+
Emitter<DashboardState> emit,
30+
) async {
31+
emit(state.copyWith(status: DashboardStatus.loading));
32+
try {
33+
// Fetch summary, app config, and recent headlines concurrently
34+
final [
35+
summaryResponse,
36+
appConfigResponse,
37+
recentHeadlinesResponse,
38+
] = await Future.wait([
39+
_dashboardSummaryRepository.read(id: 'dashboard_summary'),
40+
_appConfigRepository.read(id: 'app_config'),
41+
_headlinesRepository.readAll(
42+
sortBy: 'createdAt',
43+
sortOrder: SortOrder.desc,
44+
limit: 5,
45+
),
46+
]);
47+
48+
final summary = summaryResponse as DashboardSummary;
49+
final appConfig = appConfigResponse as AppConfig;
50+
final recentHeadlines =
51+
(recentHeadlinesResponse as PaginatedResponse<Headline>).items;
52+
emit(
53+
state.copyWith(
54+
status: DashboardStatus.success,
55+
summary: summary,
56+
appConfig: appConfig,
57+
recentHeadlines: recentHeadlines,
58+
),
59+
);
60+
} on HtHttpException catch (e) {
61+
emit(
62+
state.copyWith(
63+
status: DashboardStatus.failure,
64+
errorMessage: e.message,
65+
),
66+
);
67+
} catch (e) {
68+
emit(
69+
state.copyWith(
70+
status: DashboardStatus.failure,
71+
errorMessage: 'An unknown error occurred: $e',
72+
),
73+
);
74+
}
75+
}
76+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
part of 'dashboard_bloc.dart';
2+
3+
/// Base class for dashboard events.
4+
sealed class DashboardEvent extends Equatable {
5+
const DashboardEvent();
6+
7+
@override
8+
List<Object> get props => [];
9+
}
10+
11+
/// Event to load the dashboard summary data.
12+
final class DashboardSummaryLoaded extends DashboardEvent {}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
part of 'dashboard_bloc.dart';
2+
3+
/// Represents the status of the dashboard data loading.
4+
enum DashboardStatus {
5+
/// Initial state.
6+
initial,
7+
8+
/// Data is being loaded.
9+
loading,
10+
11+
/// Data has been successfully loaded.
12+
success,
13+
14+
/// An error occurred while loading data.
15+
failure,
16+
}
17+
18+
/// The state for the [DashboardBloc].
19+
final class DashboardState extends Equatable {
20+
const DashboardState({
21+
this.status = DashboardStatus.initial,
22+
this.summary,
23+
this.appConfig,
24+
this.recentHeadlines = const [],
25+
this.errorMessage,
26+
});
27+
28+
final DashboardStatus status;
29+
final DashboardSummary? summary;
30+
final AppConfig? appConfig;
31+
final List<Headline> recentHeadlines;
32+
final String? errorMessage;
33+
34+
DashboardState copyWith({
35+
DashboardStatus? status,
36+
DashboardSummary? summary,
37+
AppConfig? appConfig,
38+
List<Headline>? recentHeadlines,
39+
String? errorMessage,
40+
}) {
41+
return DashboardState(
42+
status: status ?? this.status,
43+
summary: summary ?? this.summary,
44+
appConfig: appConfig ?? this.appConfig,
45+
recentHeadlines: recentHeadlines ?? this.recentHeadlines,
46+
errorMessage: errorMessage ?? this.errorMessage,
47+
);
48+
}
49+
50+
@override
51+
List<Object?> get props => [
52+
status,
53+
summary,
54+
appConfig,
55+
recentHeadlines,
56+
errorMessage,
57+
];
58+
}

0 commit comments

Comments
 (0)