diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml
index afbf2ef..38bd0aa 100644
--- a/.github/dependabot.yaml
+++ b/.github/dependabot.yaml
@@ -10,4 +10,16 @@ updates:
schedule:
interval: "weekly"
ignore:
- - dependency-name: "ht_*"
+ - dependency-name: "auth_api"
+ - dependency-name: "auth_client"
+ - dependency-name: "auth_inmemory"
+ - dependency-name: "auth_repository"
+ - dependency-name: "core"
+ - dependency-name: "data_api"
+ - dependency-name: "data_client"
+ - dependency-name: "data_inmemory"
+ - dependency-name: "data_repository"
+ - dependency-name: "http_client"
+ - dependency-name: "kv_storage_service"
+ - dependency-name: "kv_storage_shared_preferences"
+ - dependency-name: "ui_kit"
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 76d9c2b..fb0560f 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -1,4 +1,4 @@
-name: deploy
+name: cd
# Run this workflow when a new release is published
on:
@@ -11,7 +11,7 @@ jobs:
steps:
# 1. Checkout the repository code
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
# 2. Setup Flutter SDK
- name: Setup Flutter
@@ -26,11 +26,11 @@ jobs:
# 4. Build the Flutter web app
- name: Build Web App
- run: flutter build web --release --base-href /ht-dashboard/
+ run: flutter build web --release --base-href /flutter-news-app-web-dashboard-full-source-code/
# 5. Deploy the built app to GitHub Pages
- name: Deploy
- uses: peaceiris/actions-gh-pages@v3
+ uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./build/web
\ No newline at end of file
diff --git a/README.md b/README.md
index 1dd4603..c8620c2 100644
--- a/README.md
+++ b/README.md
@@ -1,89 +1,108 @@
-# 📊✨ ht_dashboard
+
+
-
-[](https://headlines-toolkit.github.io/ht-dashboard/)
-[](https://pub.dev/packages/very_good_analysis)
-[](https://polyformproject.org/licenses/free-trial/1.0.0)
+# Flutter News App - Web Dashboard Full Source Code
-`ht_dashboard` is a Flutter web application designed as a comprehensive content management and administration interface for the [Headlines Toolkit](https://github.com/headlines-toolkit) ecosystem. It provides a powerful, intuitive platform for managing news content, application configurations, and user-related settings, complementing the [mobile app](https://github.com/headlines-toolkit/ht-main) and the [backend service](https://github.com/headlines-toolkit/ht-api).
+
+
+
+
+
+
+
-## ⭐ Features & Capabilities
+This is a complete and fully-functional Flutter web application for comprehensive content management and administration. It is a key component of the [**flutter news app full source code toolkit**](https://github.com/flutter-news-app-full-source-code), an ecosystem that also includes a dart frog [backend API](https://github.com/flutter-news-app-full-source-code/flutter-news-app-api-server-full-source-code) and a [mobile client](https://github.com/flutter-news-app-full-source-code/flutter-news-app-mobile-client-full-source-code).
-`ht_dashboard` offers a robust set of tools for efficient administration:
+## ⭐ Everything You Get, Ready to Go
+
+This dashboard comes packed with all the features you need to manage a professional news application.
#### 📰 **Comprehensive Content Management**
-Effortlessly manage all aspects of your news content, including:
-* **Headlines:** Create, edit, and delete news articles.
-* **Categories:** Organize and define news categories.
+Effortlessly manage all aspects of your news content with full CRUD (Create, Read, Update, Delete) capabilities:
+* **Headlines:** Create, edit, and publish news articles.
+* **Topics:** Organize and define news topics.
* **Sources:** Maintain and update news sources.
-* **Benefit for you:** Centralized control over your content, ensuring accuracy and consistency across your news platform. ✍️
+> **Your Advantage:** You get a powerful, centralized system to control your content, ensuring accuracy and consistency across your news platform. ✍️
#### ⚙️ **Dynamic Application Configuration**
-Control the behavior and appearance of the `ht_main` mobile application remotely:
-* **User Preference Limits:** Set limits for followed items and saved headlines
- based on user roles (Guest, Authenticated, Premium).
-* **Ad Settings:** Configure ad frequency and placement intervals for
- different user tiers.
-* **In-App Prompts:** Manage the display frequency of account-related
- calls-to-action.
-* **Operational Status (Kill Switch):** Remotely enable maintenance mode or
- disable the app.
-* **Force Update:** Mandate or suggest app updates to users.
-* **Benefit for you:** Granular control over app features and monetization
- strategies without requiring app store updates. 🚀
-
-#### 📊 **Intuitive User Interface**
-Built with Flutter, the dashboard provides a responsive and user-friendly
-experience across various web browsers and screen sizes.
-* **User-Friendly Error Handling:** Displays clear, localized error messages for a smooth and understandable user experience when issues arise.
-* **Benefit for you:** A modern, maintainable, and visually appealing
- interface for your administrative tasks. ✨
+Control the behavior and appearance of the mobile application remotely:
+* **User Preference Limits:** Set limits for followed items and saved headlines based on user roles (Guest, Authenticated, Premium).
+* **Ad Settings:** Configure ad frequency and placement intervals for different user tiers.
+* **In-App Prompts:** Manage the display frequency of account-related calls-to-action.
+* **Operational Status:** Remotely enable maintenance mode or suggest app updates to users.
+> **Your Advantage:** Granular control over app features and monetization strategies without requiring app store updates. 🚀
+
+#### 📊 **Dashboard Analytics**
+Get a quick overview of your content ecosystem with key statistics:
+* **Content Counts:** View the total number of headlines, topics, and sources at a glance.
+> **Your Advantage:** Instantly understand the scale of your content operation. 📈
+
+#### 🔐 **Robust User Authentication**
+Secure and flexible authentication flows are built-in for administrators and publishers using Email + Code (Passwordless) Sign-In.
+> **Your Advantage:** All the complex security and user management for your administrative team is already done for you. ✅
+
+#### 🖥️ **Responsive Web UI**
+Built with `flutter_adaptive_scaffold`, the dashboard offers a clean, responsive interface that looks great on any screen size.
+> **Your Advantage:** Deliver a consistent and optimized administrative experience on desktops, tablets, and even mobile browsers. ↔️
#### 🏗️ **Clean & Modern Architecture**
Developed with best practices for a maintainable and scalable codebase:
* **Flutter & Dart:** Cutting-edge web development.
* **BLoC Pattern:** Predictable and robust state management.
* **GoRouter:** Well-structured and powerful navigation.
-* **Adaptive UI:** Responsive layouts using `flutter_adaptive_scaffold` for
- optimal experience on different screen sizes.
-* **Benefit for you:** An easy-to-understand, extendable, and testable
- foundation for your project. 📈
+> **Your Advantage:** The dashboard is built on a clean, modern architecture that's easy to understand and maintain. It's solid and built to last. 📈
+
+#### ⚙️ **Flexible Environment Configuration**
+Easily switch between development (in-memory data or local API) and production environments with a simple code change. This empowers rapid prototyping, robust testing, and seamless deployment.
+> **Your Advantage:** A flexible setup that speeds up your development cycle and makes deployment simple. 🚀
#### 🌍 **Localization Ready**
-Fully internationalized with working English and Arabic localizations (`.arb` files).
-* **Benefit for you:** Easily adapt your dashboard for a global team. 🌐
+Fully internationalized with working English and Arabic localizations (`.arb` files). Adding more languages is straightforward.
+> **Your Advantage:** Easily adapt your dashboard for a global team. 🌐
---
-## 🔑 Access and Licensing
+## 🔑 License: Source-Available with a Free Trial
-`ht_dashboard` is source-available as part of the Headlines Toolkit ecosystem.
+Get started for free and purchase when you're ready to launch!
-To acquire a commercial license for building unlimited news applications, please visit
-the [Headlines Toolkit GitHub organization page](https://github.com/headlines-toolkit)
-for more details.
+* **TRY IT:** Download and explore the full source code under the polyForm free trial [license](LICENSE). Perfect for evaluation.
+* **BUY IT:** Get an unlimited commercial lifetime license with a **one-time payment**. No subscriptions!
+* **GET YOURS:** [**Purchase via GitHub Sponsors**](https://github.com/sponsors/flutter-news-app-full-source-code).
+> *
Note: The single purchase provides a comprehensive commercial license covering every repository within the [Flutter News App - Full Source Code Toolkit](https://github.com/flutter-news-app-full-source-code) organization. No separate purchases are needed for the API or mobile client.
*
---
-## 🚀 Getting Started
+## 🚀 Getting Started & Running Locally
1. **Ensure Flutter is installed.** (See [Flutter documentation](https://flutter.dev/docs/get-started/install))
2. **Clone the repository:**
```bash
- git clone https://github.com/headlines-toolkit/ht-dashboard.git
- cd ht-dashboard
+ git clone https://github.com/flutter-news-app-full-source-code/flutter-news-app-web-dashboard-full-source-code.git
+ cd flutter-news-app-web-dashboard-full-source-code
```
3. **Get dependencies:**
```bash
flutter pub get
```
-4. **Run the app for web:**
+4. **Run the app:**
+
+ To run the app, first select your desired environment in `lib/main.dart`:
+
+ ```dart
+ // lib/main.dart
+
+ // Use `AppEnvironment.demo` to run with in-memory data (no API needed).
+ // Use `AppEnvironment.development` to connect to a local backend API.
+ // Use `AppEnvironment.production` to connect to a live backend API.
+ const appEnvironment = AppEnvironment.demo;
+ ```
+
+ Then, run the app from your terminal:
```bash
flutter run -d chrome
- # Or to run on a web server (e.g., for CORS testing with ht_api)
- # flutter run -d web-server --web-hostname 0.0.0.0 --web-port 3000
```
- *(Note: For full functionality, ensure the `ht_api` backend service is running and accessible. If running `ht_api` locally, ensure its `CORS_ALLOWED_ORIGIN` environment variable is configured to allow requests from the dashboard's origin, e.g., `http://localhost:3000` if using the web-server option.)*
+ *(Note: For `development/production` environment, ensure the [backend service](https://github.com/flutter-news-app-full-source-code/flutter-news-app-api-server-full-source-code) is running.)*
---
@@ -94,5 +113,3 @@ This project aims for high test coverage to ensure quality and reliability.
* Run tests with:
```bash
flutter test
- ```
- *(Note: Ensure `very_good_cli` is activated: `dart pub global activate very_good_cli`)*
diff --git a/lib/app/bloc/app_bloc.dart b/lib/app/bloc/app_bloc.dart
index b04b137..bf43a96 100644
--- a/lib/app/bloc/app_bloc.dart
+++ b/lib/app/bloc/app_bloc.dart
@@ -2,12 +2,13 @@
import 'dart:async';
+import 'package:auth_repository/auth_repository.dart';
import 'package:bloc/bloc.dart';
+import 'package:core/core.dart';
+import 'package:data_repository/data_repository.dart';
import 'package:equatable/equatable.dart';
-import 'package:ht_auth_repository/ht_auth_repository.dart';
-import 'package:ht_dashboard/app/config/config.dart' as local_config;
-import 'package:ht_data_repository/ht_data_repository.dart';
-import 'package:ht_shared/ht_shared.dart';
+import 'package:flutter_news_app_web_dashboard_full_source_code/app/config/config.dart'
+ as local_config;
import 'package:logging/logging.dart';
part 'app_event.dart';
@@ -15,18 +16,16 @@ part 'app_state.dart';
class AppBloc extends Bloc {
AppBloc({
- required HtAuthRepository authenticationRepository,
- required HtDataRepository userAppSettingsRepository,
- required HtDataRepository appConfigRepository,
+ required AuthRepository authenticationRepository,
+ required DataRepository userAppSettingsRepository,
+ required DataRepository appConfigRepository,
required local_config.AppEnvironment environment,
Logger? logger,
}) : _authenticationRepository = authenticationRepository,
_userAppSettingsRepository = userAppSettingsRepository,
_appConfigRepository = appConfigRepository,
_logger = logger ?? Logger('AppBloc'),
- super(
- AppState(environment: environment),
- ) {
+ super(AppState(environment: environment)) {
on(_onAppUserChanged);
on(_onLogoutRequested);
on(_onAppUserAppSettingsChanged);
@@ -36,9 +35,9 @@ class AppBloc extends Bloc {
);
}
- final HtAuthRepository _authenticationRepository;
- final HtDataRepository _userAppSettingsRepository;
- final HtDataRepository _appConfigRepository;
+ final AuthRepository _authenticationRepository;
+ final DataRepository _userAppSettingsRepository;
+ final DataRepository _appConfigRepository;
final Logger _logger;
late final StreamSubscription _userSubscription;
@@ -97,7 +96,7 @@ class AppBloc extends Bloc {
);
await _userAppSettingsRepository.create(item: defaultSettings);
emit(state.copyWith(userAppSettings: defaultSettings));
- } on HtHttpException catch (e, s) {
+ } on HttpException catch (e, s) {
// Handle HTTP exceptions during settings load
_logger.severe(
'Error loading user app settings for user ${user.id}: ${e.message}',
diff --git a/lib/app/config/app_config.dart b/lib/app/config/app_config.dart
index 289f1e4..1c1f793 100644
--- a/lib/app/config/app_config.dart
+++ b/lib/app/config/app_config.dart
@@ -1,20 +1,15 @@
-import 'package:ht_dashboard/app/config/app_environment.dart';
+import 'package:flutter_news_app_web_dashboard_full_source_code/app/config/app_environment.dart';
class AppConfig {
- const AppConfig({
- required this.environment,
- required this.baseUrl,
- });
+ const AppConfig({required this.environment, required this.baseUrl});
factory AppConfig.production() => const AppConfig(
environment: AppEnvironment.production,
baseUrl: 'http://api.yourproductiondomain.com',
);
- factory AppConfig.demo() => const AppConfig(
- environment: AppEnvironment.demo,
- baseUrl: '',
- );
+ factory AppConfig.demo() =>
+ const AppConfig(environment: AppEnvironment.demo, baseUrl: '');
factory AppConfig.development() => const AppConfig(
environment: AppEnvironment.development,
diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart
index 9c86a5f..b270aa7 100644
--- a/lib/app/view/app.dart
+++ b/lib/app/view/app.dart
@@ -1,122 +1,121 @@
//
// ignore_for_file: deprecated_member_use
+import 'package:auth_repository/auth_repository.dart';
+import 'package:core/core.dart' hide AppStatus;
+// Import for app_theme.dart
+import 'package:data_repository/data_repository.dart';
import 'package:flex_color_scheme/flex_color_scheme.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:flutter_news_app_web_dashboard_full_source_code/app/bloc/app_bloc.dart';
+import 'package:flutter_news_app_web_dashboard_full_source_code/app/config/app_environment.dart';
+import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/bloc/app_configuration_bloc.dart';
+import 'package:flutter_news_app_web_dashboard_full_source_code/authentication/bloc/authentication_bloc.dart';
+import 'package:flutter_news_app_web_dashboard_full_source_code/content_management/bloc/content_management_bloc.dart';
+import 'package:flutter_news_app_web_dashboard_full_source_code/dashboard/bloc/dashboard_bloc.dart';
+import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/app_localizations.dart';
+import 'package:flutter_news_app_web_dashboard_full_source_code/router/router.dart';
import 'package:go_router/go_router.dart';
-import 'package:ht_auth_repository/ht_auth_repository.dart';
-import 'package:ht_dashboard/app/bloc/app_bloc.dart';
-import 'package:ht_dashboard/app/config/app_environment.dart';
-import 'package:ht_dashboard/app_configuration/bloc/app_configuration_bloc.dart';
-import 'package:ht_dashboard/authentication/bloc/authentication_bloc.dart';
-import 'package:ht_dashboard/content_management/bloc/content_management_bloc.dart';
-import 'package:ht_dashboard/dashboard/bloc/dashboard_bloc.dart';
-import 'package:ht_dashboard/l10n/app_localizations.dart';
-import 'package:ht_dashboard/router/router.dart';
-// Import for app_theme.dart
-import 'package:ht_data_repository/ht_data_repository.dart';
-import 'package:ht_kv_storage_service/ht_kv_storage_service.dart';
-import 'package:ht_shared/ht_shared.dart' hide AppStatus;
-import 'package:ht_ui_kit/ht_ui_kit.dart';
+import 'package:kv_storage_service/kv_storage_service.dart';
import 'package:logging/logging.dart';
+import 'package:ui_kit/ui_kit.dart';
class App extends StatelessWidget {
const App({
- required HtAuthRepository htAuthenticationRepository,
- required HtDataRepository htHeadlinesRepository,
- required HtDataRepository htTopicsRepository,
- required HtDataRepository htCountriesRepository,
- required HtDataRepository htSourcesRepository,
- required HtDataRepository htUserAppSettingsRepository,
- required HtDataRepository
- htUserContentPreferencesRepository,
- required HtDataRepository htRemoteConfigRepository,
- required HtDataRepository htDashboardSummaryRepository,
- required HtKVStorageService kvStorageService,
+ required AuthRepository authenticationRepository,
+ required DataRepository headlinesRepository,
+ required DataRepository topicsRepository,
+ required DataRepository countriesRepository,
+ required DataRepository sourcesRepository,
+ required DataRepository userAppSettingsRepository,
+ required DataRepository
+ userContentPreferencesRepository,
+ required DataRepository remoteConfigRepository,
+ required DataRepository dashboardSummaryRepository,
+ required KVStorageService storageService,
required AppEnvironment environment,
super.key,
- }) : _htAuthenticationRepository = htAuthenticationRepository,
- _htHeadlinesRepository = htHeadlinesRepository,
- _htTopicsRepository = htTopicsRepository,
- _htCountriesRepository = htCountriesRepository,
- _htSourcesRepository = htSourcesRepository,
- _htUserAppSettingsRepository = htUserAppSettingsRepository,
- _htUserContentPreferencesRepository = htUserContentPreferencesRepository,
- _htRemoteConfigRepository = htRemoteConfigRepository,
- _kvStorageService = kvStorageService,
- _htDashboardSummaryRepository = htDashboardSummaryRepository,
+ }) : _authenticationRepository = authenticationRepository,
+ _headlinesRepository = headlinesRepository,
+ _topicsRepository = topicsRepository,
+ _countriesRepository = countriesRepository,
+ _sourcesRepository = sourcesRepository,
+ _userAppSettingsRepository = userAppSettingsRepository,
+ _userContentPreferencesRepository = userContentPreferencesRepository,
+ _remoteConfigRepository = remoteConfigRepository,
+ _kvStorageService = storageService,
+ _dashboardSummaryRepository = dashboardSummaryRepository,
_environment = environment;
- final HtAuthRepository _htAuthenticationRepository;
- final HtDataRepository _htHeadlinesRepository;
- final HtDataRepository _htTopicsRepository;
- final HtDataRepository _htCountriesRepository;
- final HtDataRepository _htSourcesRepository;
- final HtDataRepository _htUserAppSettingsRepository;
- final HtDataRepository
- _htUserContentPreferencesRepository;
- final HtDataRepository _htRemoteConfigRepository;
- final HtDataRepository _htDashboardSummaryRepository;
- final HtKVStorageService _kvStorageService;
+ final AuthRepository _authenticationRepository;
+ final DataRepository _headlinesRepository;
+ final DataRepository _topicsRepository;
+ final DataRepository _countriesRepository;
+ final DataRepository _sourcesRepository;
+ final DataRepository _userAppSettingsRepository;
+ final DataRepository
+ _userContentPreferencesRepository;
+ final DataRepository _remoteConfigRepository;
+ final DataRepository _dashboardSummaryRepository;
+ final KVStorageService _kvStorageService;
final AppEnvironment _environment;
@override
Widget build(BuildContext context) {
return MultiRepositoryProvider(
providers: [
- RepositoryProvider.value(value: _htAuthenticationRepository),
- RepositoryProvider.value(value: _htHeadlinesRepository),
- RepositoryProvider.value(value: _htTopicsRepository),
- RepositoryProvider.value(value: _htCountriesRepository),
- RepositoryProvider.value(value: _htSourcesRepository),
- RepositoryProvider.value(value: _htUserAppSettingsRepository),
- RepositoryProvider.value(value: _htUserContentPreferencesRepository),
- RepositoryProvider.value(value: _htRemoteConfigRepository),
- RepositoryProvider.value(value: _htDashboardSummaryRepository),
+ RepositoryProvider.value(value: _authenticationRepository),
+ RepositoryProvider.value(value: _headlinesRepository),
+ RepositoryProvider.value(value: _topicsRepository),
+ RepositoryProvider.value(value: _countriesRepository),
+ RepositoryProvider.value(value: _sourcesRepository),
+ RepositoryProvider.value(value: _userAppSettingsRepository),
+ RepositoryProvider.value(value: _userContentPreferencesRepository),
+ RepositoryProvider.value(value: _remoteConfigRepository),
+ RepositoryProvider.value(value: _dashboardSummaryRepository),
RepositoryProvider.value(value: _kvStorageService),
],
child: MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => AppBloc(
- authenticationRepository: context.read(),
+ authenticationRepository: context.read(),
userAppSettingsRepository: context
- .read>(),
- appConfigRepository: context
- .read>(),
+ .read>(),
+ appConfigRepository: context.read>(),
environment: _environment,
logger: Logger('AppBloc'),
),
),
BlocProvider(
create: (context) => AuthenticationBloc(
- authenticationRepository: context.read(),
+ authenticationRepository: context.read(),
),
),
BlocProvider(
create: (context) => AppConfigurationBloc(
remoteConfigRepository: context
- .read>(),
+ .read>(),
),
),
BlocProvider(
create: (context) => ContentManagementBloc(
- headlinesRepository: context.read>(),
- topicsRepository: context.read>(),
- sourcesRepository: context.read>(),
+ headlinesRepository: context.read>(),
+ topicsRepository: context.read>(),
+ sourcesRepository: context.read>(),
),
),
BlocProvider(
create: (context) => DashboardBloc(
dashboardSummaryRepository: context
- .read>(),
- headlinesRepository: context.read>(),
+ .read>(),
+ headlinesRepository: context.read>(),
),
),
],
child: _AppView(
- htAuthenticationRepository: _htAuthenticationRepository,
+ authenticationRepository: _authenticationRepository,
environment: _environment,
),
),
@@ -127,11 +126,11 @@ class App extends StatelessWidget {
class _AppView extends StatefulWidget {
/// {@macro app_view}
const _AppView({
- required this.htAuthenticationRepository,
+ required this.authenticationRepository,
required this.environment,
});
- final HtAuthRepository htAuthenticationRepository;
+ final AuthRepository authenticationRepository;
final AppEnvironment environment;
@override
@@ -149,7 +148,7 @@ class _AppViewState extends State<_AppView> {
_statusNotifier = ValueNotifier(appBloc.state.status);
_router = createRouter(
authStatusNotifier: _statusNotifier,
- htAuthenticationRepository: widget.htAuthenticationRepository,
+ authenticationRepository: widget.authenticationRepository,
environment: widget.environment,
);
}
@@ -210,10 +209,10 @@ class _AppViewState extends State<_AppView> {
debugShowCheckedModeBanner: false,
routerConfig: _router,
localizationsDelegates: const [
- HtUiKitLocalizations.delegate,
+ UiKitLocalizations.delegate,
...AppLocalizations.localizationsDelegates,
],
- supportedLocales: HtUiKitLocalizations.supportedLocales,
+ supportedLocales: UiKitLocalizations.supportedLocales,
theme: baseTheme == AppBaseTheme.dark
? darkThemeData
: lightThemeData,
diff --git a/lib/app/view/app_shell.dart b/lib/app/view/app_shell.dart
index 5945047..ac054f2 100644
--- a/lib/app/view/app_shell.dart
+++ b/lib/app/view/app_shell.dart
@@ -1,11 +1,11 @@
import 'package:flutter/material.dart';
import 'package:flutter_adaptive_scaffold/flutter_adaptive_scaffold.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:flutter_news_app_web_dashboard_full_source_code/app/bloc/app_bloc.dart';
+import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart';
+import 'package:flutter_news_app_web_dashboard_full_source_code/router/routes.dart';
import 'package:go_router/go_router.dart';
-import 'package:ht_dashboard/app/bloc/app_bloc.dart';
-import 'package:ht_dashboard/l10n/l10n.dart';
-import 'package:ht_dashboard/router/routes.dart';
-import 'package:ht_ui_kit/ht_ui_kit.dart';
+import 'package:ui_kit/ui_kit.dart';
/// A responsive scaffold shell for the main application sections.
///
diff --git a/lib/app_configuration/bloc/app_configuration_bloc.dart b/lib/app_configuration/bloc/app_configuration_bloc.dart
index 5a70521..1618ab1 100644
--- a/lib/app_configuration/bloc/app_configuration_bloc.dart
+++ b/lib/app_configuration/bloc/app_configuration_bloc.dart
@@ -1,7 +1,7 @@
import 'package:bloc/bloc.dart';
+import 'package:core/core.dart';
+import 'package:data_repository/data_repository.dart';
import 'package:equatable/equatable.dart';
-import 'package:ht_data_repository/ht_data_repository.dart';
-import 'package:ht_shared/ht_shared.dart';
part 'app_configuration_event.dart';
part 'app_configuration_state.dart';
@@ -9,18 +9,16 @@ part 'app_configuration_state.dart';
class AppConfigurationBloc
extends Bloc {
AppConfigurationBloc({
- required HtDataRepository remoteConfigRepository,
+ required DataRepository remoteConfigRepository,
}) : _remoteConfigRepository = remoteConfigRepository,
- super(
- const AppConfigurationState(),
- ) {
+ super(const AppConfigurationState()) {
on(_onAppConfigurationLoaded);
on(_onAppConfigurationUpdated);
on(_onAppConfigurationFieldChanged);
on(_onAppConfigurationDiscarded);
}
- final HtDataRepository _remoteConfigRepository;
+ final DataRepository _remoteConfigRepository;
Future _onAppConfigurationLoaded(
AppConfigurationLoaded event,
@@ -41,12 +39,9 @@ class AppConfigurationBloc
true, // Clear any previous success snackbar flag
),
);
- } on HtHttpException catch (e) {
+ } on HttpException catch (e) {
emit(
- state.copyWith(
- status: AppConfigurationStatus.failure,
- exception: e,
- ),
+ state.copyWith(status: AppConfigurationStatus.failure, exception: e),
);
} catch (e) {
emit(
@@ -77,12 +72,9 @@ class AppConfigurationBloc
showSaveSuccess: true, // Set flag to show success snackbar
),
);
- } on HtHttpException catch (e) {
+ } on HttpException catch (e) {
emit(
- state.copyWith(
- status: AppConfigurationStatus.failure,
- exception: e,
- ),
+ state.copyWith(status: AppConfigurationStatus.failure, exception: e),
);
} catch (e) {
emit(
diff --git a/lib/app_configuration/bloc/app_configuration_state.dart b/lib/app_configuration/bloc/app_configuration_state.dart
index c3cbe49..e40cd77 100644
--- a/lib/app_configuration/bloc/app_configuration_state.dart
+++ b/lib/app_configuration/bloc/app_configuration_state.dart
@@ -39,7 +39,7 @@ class AppConfigurationState extends Equatable {
final RemoteConfig? originalRemoteConfig;
/// An error exception if an operation failed.
- final HtHttpException? exception;
+ final HttpException? exception;
/// Indicates if there are unsaved changes to the configuration.
final bool isDirty;
@@ -52,7 +52,7 @@ class AppConfigurationState extends Equatable {
AppConfigurationStatus? status,
RemoteConfig? remoteConfig,
RemoteConfig? originalRemoteConfig,
- HtHttpException? exception,
+ HttpException? exception,
bool? isDirty,
bool clearException = false,
bool? showSaveSuccess,
diff --git a/lib/app_configuration/view/app_configuration_page.dart b/lib/app_configuration/view/app_configuration_page.dart
index 488d7c1..2f15050 100644
--- a/lib/app_configuration/view/app_configuration_page.dart
+++ b/lib/app_configuration/view/app_configuration_page.dart
@@ -1,10 +1,10 @@
+import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:ht_dashboard/app_configuration/bloc/app_configuration_bloc.dart';
-import 'package:ht_dashboard/l10n/app_localizations.dart';
-import 'package:ht_dashboard/l10n/l10n.dart';
-import 'package:ht_shared/ht_shared.dart';
-import 'package:ht_ui_kit/ht_ui_kit.dart';
+import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/bloc/app_configuration_bloc.dart';
+import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/app_localizations.dart';
+import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart';
+import 'package:ui_kit/ui_kit.dart';
/// {@template app_configuration_page}
/// A page for managing the application's remote configuration.
@@ -176,9 +176,7 @@ class _AppConfigurationPageState extends State
childrenPadding: const EdgeInsets.symmetric(
horizontal: AppSpacing.xxl,
),
- children: [
- _buildAdConfigSection(context, remoteConfig),
- ],
+ children: [_buildAdConfigSection(context, remoteConfig)],
),
],
),
@@ -316,9 +314,7 @@ class _AppConfigurationPageState extends State
remoteConfig: remoteConfig,
onConfigChanged: (newConfig) {
context.read().add(
- AppConfigurationFieldChanged(
- remoteConfig: newConfig,
- ),
+ AppConfigurationFieldChanged(remoteConfig: newConfig),
);
},
buildIntField: _buildIntField,
@@ -336,9 +332,7 @@ class _AppConfigurationPageState extends State
remoteConfig: remoteConfig,
onConfigChanged: (newConfig) {
context.read().add(
- AppConfigurationFieldChanged(
- remoteConfig: newConfig,
- ),
+ AppConfigurationFieldChanged(remoteConfig: newConfig),
);
},
buildIntField: _buildIntField,
@@ -356,9 +350,7 @@ class _AppConfigurationPageState extends State
remoteConfig: remoteConfig,
onConfigChanged: (newConfig) {
context.read().add(
- AppConfigurationFieldChanged(
- remoteConfig: newConfig,
- ),
+ AppConfigurationFieldChanged(remoteConfig: newConfig),
);
},
buildIntField: _buildIntField,
@@ -395,9 +387,7 @@ class _AppConfigurationPageState extends State
remoteConfig: remoteConfig,
onConfigChanged: (newConfig) {
context.read().add(
- AppConfigurationFieldChanged(
- remoteConfig: newConfig,
- ),
+ AppConfigurationFieldChanged(remoteConfig: newConfig),
);
},
buildIntField: _buildIntField,
@@ -415,9 +405,7 @@ class _AppConfigurationPageState extends State
remoteConfig: remoteConfig,
onConfigChanged: (newConfig) {
context.read().add(
- AppConfigurationFieldChanged(
- remoteConfig: newConfig,
- ),
+ AppConfigurationFieldChanged(remoteConfig: newConfig),
);
},
buildIntField: _buildIntField,
@@ -435,9 +423,7 @@ class _AppConfigurationPageState extends State
remoteConfig: remoteConfig,
onConfigChanged: (newConfig) {
context.read().add(
- AppConfigurationFieldChanged(
- remoteConfig: newConfig,
- ),
+ AppConfigurationFieldChanged(remoteConfig: newConfig),
);
},
buildIntField: _buildIntField,
@@ -474,9 +460,7 @@ class _AppConfigurationPageState extends State
remoteConfig: remoteConfig,
onConfigChanged: (newConfig) {
context.read().add(
- AppConfigurationFieldChanged(
- remoteConfig: newConfig,
- ),
+ AppConfigurationFieldChanged(remoteConfig: newConfig),
);
},
buildIntField: _buildIntField,
@@ -494,9 +478,7 @@ class _AppConfigurationPageState extends State
remoteConfig: remoteConfig,
onConfigChanged: (newConfig) {
context.read().add(
- AppConfigurationFieldChanged(
- remoteConfig: newConfig,
- ),
+ AppConfigurationFieldChanged(remoteConfig: newConfig),
);
},
buildIntField: _buildIntField,
@@ -659,10 +641,7 @@ class _AppConfigurationPageState extends State
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Text(
- label,
- style: Theme.of(context).textTheme.titleMedium,
- ),
+ Text(label, style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: AppSpacing.xs),
Text(
description,
@@ -704,10 +683,7 @@ class _AppConfigurationPageState extends State
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Text(
- label,
- style: Theme.of(context).textTheme.titleMedium,
- ),
+ Text(label, style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: AppSpacing.xs),
Text(
description,
@@ -1326,9 +1302,7 @@ class _AccountActionConfigFormState extends State<_AccountActionConfigForm> {
return widget.buildIntField(
context,
label: '$localizedActionType ${l10n.daysSuffix}',
- description: l10n.daysBetweenPromptDescription(
- localizedActionType,
- ),
+ description: l10n.daysBetweenPromptDescription(localizedActionType),
value: _getDaysMap(accountActionConfig)[actionType] ?? 0,
onChanged: (value) {
final currentMap = Map.from(
diff --git a/lib/authentication/bloc/authentication_bloc.dart b/lib/authentication/bloc/authentication_bloc.dart
index 5a25421..aeb92bc 100644
--- a/lib/authentication/bloc/authentication_bloc.dart
+++ b/lib/authentication/bloc/authentication_bloc.dart
@@ -1,13 +1,12 @@
import 'dart:async';
+import 'package:auth_repository/auth_repository.dart';
import 'package:bloc/bloc.dart';
-import 'package:equatable/equatable.dart';
-import 'package:ht_auth_repository/ht_auth_repository.dart';
-import 'package:ht_shared/ht_shared.dart'
+import 'package:core/core.dart'
show
AuthenticationException,
ForbiddenException,
- HtHttpException,
+ HttpException,
InvalidInputException,
NetworkException,
NotFoundException,
@@ -16,6 +15,7 @@ import 'package:ht_shared/ht_shared.dart'
UnauthorizedException,
UnknownException,
User;
+import 'package:equatable/equatable.dart';
part 'authentication_event.dart';
part 'authentication_state.dart';
@@ -26,7 +26,7 @@ part 'authentication_state.dart';
class AuthenticationBloc
extends Bloc {
/// {@macro authentication_bloc}
- AuthenticationBloc({required HtAuthRepository authenticationRepository})
+ AuthenticationBloc({required AuthRepository authenticationRepository})
: _authenticationRepository = authenticationRepository,
super(const AuthenticationState()) {
// Listen to authentication state changes from the repository
@@ -42,7 +42,7 @@ class AuthenticationBloc
on(_onAuthenticationSignOutRequested);
}
- final HtAuthRepository _authenticationRepository;
+ final AuthRepository _authenticationRepository;
late final StreamSubscription _userAuthSubscription;
/// Handles [_AuthenticationStatusChanged] events.
@@ -85,55 +85,20 @@ class AuthenticationBloc
),
);
} on InvalidInputException catch (e) {
- emit(
- state.copyWith(
- status: AuthenticationStatus.failure,
- exception: e,
- ),
- );
+ emit(state.copyWith(status: AuthenticationStatus.failure, exception: e));
} on UnauthorizedException catch (e) {
- emit(
- state.copyWith(
- status: AuthenticationStatus.failure,
- exception: e,
- ),
- );
+ emit(state.copyWith(status: AuthenticationStatus.failure, exception: e));
} on ForbiddenException catch (e) {
- emit(
- state.copyWith(
- status: AuthenticationStatus.failure,
- exception: e,
- ),
- );
+ emit(state.copyWith(status: AuthenticationStatus.failure, exception: e));
} on NetworkException catch (e) {
- emit(
- state.copyWith(
- status: AuthenticationStatus.failure,
- exception: e,
- ),
- );
+ emit(state.copyWith(status: AuthenticationStatus.failure, exception: e));
} on ServerException catch (e) {
- emit(
- state.copyWith(
- status: AuthenticationStatus.failure,
- exception: e,
- ),
- );
+ emit(state.copyWith(status: AuthenticationStatus.failure, exception: e));
} on OperationFailedException catch (e) {
- emit(
- state.copyWith(
- status: AuthenticationStatus.failure,
- exception: e,
- ),
- );
- } on HtHttpException catch (e) {
- // Catch any other HtHttpException subtypes
- emit(
- state.copyWith(
- status: AuthenticationStatus.failure,
- exception: e,
- ),
- );
+ emit(state.copyWith(status: AuthenticationStatus.failure, exception: e));
+ } on HttpException catch (e) {
+ // Catch any other HttpException subtypes
+ emit(state.copyWith(status: AuthenticationStatus.failure, exception: e));
} catch (e) {
// Catch any other unexpected errors
emit(
@@ -161,55 +126,20 @@ class AuthenticationBloc
// On success, the _AuthenticationStatusChanged listener will handle
// emitting AuthenticationAuthenticated.
} on InvalidInputException catch (e) {
- emit(
- state.copyWith(
- status: AuthenticationStatus.failure,
- exception: e,
- ),
- );
+ emit(state.copyWith(status: AuthenticationStatus.failure, exception: e));
} on AuthenticationException catch (e) {
- emit(
- state.copyWith(
- status: AuthenticationStatus.failure,
- exception: e,
- ),
- );
+ emit(state.copyWith(status: AuthenticationStatus.failure, exception: e));
} on NotFoundException catch (e) {
- emit(
- state.copyWith(
- status: AuthenticationStatus.failure,
- exception: e,
- ),
- );
+ emit(state.copyWith(status: AuthenticationStatus.failure, exception: e));
} on NetworkException catch (e) {
- emit(
- state.copyWith(
- status: AuthenticationStatus.failure,
- exception: e,
- ),
- );
+ emit(state.copyWith(status: AuthenticationStatus.failure, exception: e));
} on ServerException catch (e) {
- emit(
- state.copyWith(
- status: AuthenticationStatus.failure,
- exception: e,
- ),
- );
+ emit(state.copyWith(status: AuthenticationStatus.failure, exception: e));
} on OperationFailedException catch (e) {
- emit(
- state.copyWith(
- status: AuthenticationStatus.failure,
- exception: e,
- ),
- );
- } on HtHttpException catch (e) {
- // Catch any other HtHttpException subtypes
- emit(
- state.copyWith(
- status: AuthenticationStatus.failure,
- exception: e,
- ),
- );
+ emit(state.copyWith(status: AuthenticationStatus.failure, exception: e));
+ } on HttpException catch (e) {
+ // Catch any other HttpException subtypes
+ emit(state.copyWith(status: AuthenticationStatus.failure, exception: e));
} catch (e) {
// Catch any other unexpected errors
emit(
@@ -233,34 +163,14 @@ class AuthenticationBloc
// On success, the _AuthenticationStatusChanged listener will handle
// emitting AuthenticationUnauthenticated.
} on NetworkException catch (e) {
- emit(
- state.copyWith(
- status: AuthenticationStatus.failure,
- exception: e,
- ),
- );
+ emit(state.copyWith(status: AuthenticationStatus.failure, exception: e));
} on ServerException catch (e) {
- emit(
- state.copyWith(
- status: AuthenticationStatus.failure,
- exception: e,
- ),
- );
+ emit(state.copyWith(status: AuthenticationStatus.failure, exception: e));
} on OperationFailedException catch (e) {
- emit(
- state.copyWith(
- status: AuthenticationStatus.failure,
- exception: e,
- ),
- );
- } on HtHttpException catch (e) {
- // Catch any other HtHttpException subtypes
- emit(
- state.copyWith(
- status: AuthenticationStatus.failure,
- exception: e,
- ),
- );
+ emit(state.copyWith(status: AuthenticationStatus.failure, exception: e));
+ } on HttpException catch (e) {
+ // Catch any other HttpException subtypes
+ emit(state.copyWith(status: AuthenticationStatus.failure, exception: e));
} catch (e) {
emit(
state.copyWith(
diff --git a/lib/authentication/bloc/authentication_state.dart b/lib/authentication/bloc/authentication_state.dart
index b12250e..14449f1 100644
--- a/lib/authentication/bloc/authentication_state.dart
+++ b/lib/authentication/bloc/authentication_state.dart
@@ -48,7 +48,7 @@ final class AuthenticationState extends Equatable {
final String? email;
/// The error describing an authentication failure, if any.
- final HtHttpException? exception;
+ final HttpException? exception;
@override
List