6177214e-ce7c-49e3-99de-ff9721b26f63 — Commit 429a476f
Changed files
azure/templates/env-vars.yml | 6 +- comwell_key_app/android/app/build.gradle | 11 ++- comwell_key_app/assets/translations/da-DK.json | 5 +- comwell_key_app/assets/translations/en-US.json | 5 +- comwell_key_app/lib/common/const.dart | 5 + .../lib/force_update/force_update_cubit.dart | 39 ++++++++ .../lib/force_update/force_update_page.dart | 101 +++++++++++++++++++++ .../lib/force_update/force_update_state.dart | 18 ++++ comwell_key_app/lib/main.dart | 1 + comwell_key_app/lib/routing/app_router.dart | 8 ++ comwell_key_app/lib/routing/app_routes.dart | 1 + .../interceptors/response_handle_interceptor.dart | 13 +++ comwell_key_app/lib/utils/locator.dart | 3 + comwell_key_app/pubspec.yaml | 1 + 14 files changed, 209 insertions(+), 8 deletions(-)
Diff
diff --git a/azure/templates/env-vars.yml b/azure/templates/env-vars.yml
index 55670436..b68573a5 100644
--- a/azure/templates/env-vars.yml
+++ b/azure/templates/env-vars.yml
@@ -20,11 +20,11 @@ variables:
value: 'com.comwell.phoenix.dev'
- name: flavor
${{ if eq(parameters.env, 'production') }}:
- value: 'prod'
+ value: 'Prod'
${{ if eq(parameters.env, 'staging') }}:
- value: 'stage'
+ value: 'Stage'
${{ if eq(parameters.env, 'develop') }}:
- value: 'develop'
+ value: 'Develop'
# iOS
- name: iosBundleId
${{ if eq(parameters.env, 'production') }}:
diff --git a/comwell_key_app/android/app/build.gradle b/comwell_key_app/android/app/build.gradle
index dc0a82e4..3968574d 100644
--- a/comwell_key_app/android/app/build.gradle
+++ b/comwell_key_app/android/app/build.gradle
@@ -90,17 +90,22 @@ android {
productFlavors{
- develop {
+ Develop {
dimension = "env"
resValue "string", "app_name", "Comwell Phoenix Dev"
applicationIdSuffix = ".dev"
}
- stage {
+ Test {
+ dimension = "env"
+ resValue "string", "app_name", "Comwell Phoenix Test"
+ applicationIdSuffix = ".test"
+ }
+ Stage {
dimension = "env"
resValue "string", "app_name", "Comwell Phoenix Stage"
applicationIdSuffix = ".stage"
}
- prod {
+ Prod {
dimension = "env"
resValue "string", "app_name", "Comwell Phoenix"
}
diff --git a/comwell_key_app/assets/translations/da-DK.json b/comwell_key_app/assets/translations/da-DK.json
index 39a0e2df..e575072f 100644
--- a/comwell_key_app/assets/translations/da-DK.json
+++ b/comwell_key_app/assets/translations/da-DK.json
@@ -313,5 +313,8 @@
"share_room_page_subtitle": "Her kan du dele dit værelse med en anden gæst og give dem adgang til bookinginformation, nøglekort og Concierge",
"share_room_page_button": "Del dit værelse",
"addon": "Tilkøbt",
- "added_to_room": "Tilkøbt på værelse"
+ "added_to_room": "Tilkøbt på værelse",
+ "force_update_title": "Opdater for at bruge appen",
+ "force_update_description": "Der er en nødvendig opdatering til appen, der gør at du skal opdatere for at kunne fortsætte.",
+ "force_update_button": "Gå til App Store"
}
\ No newline at end of file
diff --git a/comwell_key_app/assets/translations/en-US.json b/comwell_key_app/assets/translations/en-US.json
index ef23b31e..317c7b2e 100644
--- a/comwell_key_app/assets/translations/en-US.json
+++ b/comwell_key_app/assets/translations/en-US.json
@@ -316,5 +316,8 @@
"share_room_page_subtitle": "Here you can share your room with another guest and give them access to booking information, keycard and Concierge",
"share_room_page_button": "Share your room",
"addon": "Added purchase",
- "added_to_room": "Added to room"
+ "added_to_room": "Added to room",
+ "force_update_title": "Update required",
+ "force_update_description": "To continue using the app, please update to the latest version.",
+ "force_update_button": "Go to App Store"
}
diff --git a/comwell_key_app/lib/common/const.dart b/comwell_key_app/lib/common/const.dart
index c63011b8..c06ae354 100644
--- a/comwell_key_app/lib/common/const.dart
+++ b/comwell_key_app/lib/common/const.dart
@@ -15,4 +15,9 @@ class AdyenConstants {
static const merchantAccount = "ComwellHotelsECOM";
}
+// App Store
+class AppStoreConstants {
+ static const appStoreUrl = 'https://apps.apple.com/app/idYOUR_APP_ID';
+}
+
diff --git a/comwell_key_app/lib/force_update/force_update_cubit.dart b/comwell_key_app/lib/force_update/force_update_cubit.dart
new file mode 100644
index 00000000..e79daf14
--- /dev/null
+++ b/comwell_key_app/lib/force_update/force_update_cubit.dart
@@ -0,0 +1,39 @@
+import 'package:bloc/bloc.dart';
+import 'package:comwell_key_app/common/const.dart';
+
+import 'package:in_app_update/in_app_update.dart';
+import 'package:url_launcher/url_launcher.dart';
+import 'package:flutter/foundation.dart';
+
+part 'force_update_state.dart';
+
+class ForceUpdateCubit extends Cubit<ForceUpdateState> {
+ ForceUpdateCubit() : super(ForceUpdateState.initial());
+
+ Future<void> startAndroidUpdate() async {
+ emit(state.copyWith(isLoading: true, error: null));
+ try {
+ final result = await InAppUpdate.checkForUpdate();
+ if (result.updateAvailability == UpdateAvailability.updateAvailable) {
+ await InAppUpdate.performImmediateUpdate();
+ } else {
+ emit(state.copyWith(error: 'No update available.'));
+ }
+ } catch (e) {
+ emit(state.copyWith(error: 'Failed to update: $e'));
+ } finally {
+ emit(state.copyWith(isLoading: false));
+ }
+ }
+
+ Future<void> openAppStore() async {
+ emit(state.copyWith(isLoading: true, error: null));
+ //TODO: Add app store url to CONSTS
+ if (await canLaunchUrl(Uri.parse(AppStoreConstants.appStoreUrl))) {
+ await launchUrl(Uri.parse(AppStoreConstants.appStoreUrl), mode: LaunchMode.externalApplication);
+ } else {
+ emit(state.copyWith(error: 'Could not open App Store.'));
+ }
+ emit(state.copyWith(isLoading: false));
+ }
+}
\ No newline at end of file
diff --git a/comwell_key_app/lib/force_update/force_update_page.dart b/comwell_key_app/lib/force_update/force_update_page.dart
new file mode 100644
index 00000000..49fe201c
--- /dev/null
+++ b/comwell_key_app/lib/force_update/force_update_page.dart
@@ -0,0 +1,101 @@
+import 'package:comwell_key_app/force_update/force_update_cubit.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'dart:io' show Platform;
+
+
+class ForceUpdatePage extends StatelessWidget {
+ const ForceUpdatePage({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ final textTheme = Theme.of(context).textTheme;
+ return BlocProvider(
+ create: (_) => ForceUpdateCubit(),
+ child: BlocConsumer<ForceUpdateCubit, ForceUpdateState>(
+ listener: (context, state) {
+ if (state.error != null) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text(state.error!)),
+ );
+ }
+ },
+ builder: (context, state) {
+ return Scaffold(
+ body: Container(
+ constraints: const BoxConstraints.expand(),
+ decoration: const BoxDecoration(
+ image: DecorationImage(
+ image: AssetImage('assets/images/login_screen_background.png'),
+ fit: BoxFit.cover,
+ ),
+ ),
+ child: SafeArea(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ // Logo
+ Expanded(
+ flex: 3,
+ child: Image.asset('assets/images/Logo.png',
+ width: 175, height: 50),
+ ),
+ const SizedBox(height: 32),
+ // Force Update Message
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 40.0),
+ child: Column(
+ children: [
+ Text(
+ 'force_update_title'.tr(),
+ textAlign: TextAlign.center,
+ style: textTheme.headlineMedium?.copyWith(color: Colors.white),
+ ),
+ const SizedBox(height: 8),
+ Text(
+ 'force_update_description'.tr(),
+ textAlign: TextAlign.center,
+ style: textTheme.bodySmall?.copyWith(color: Colors.white.withValues(alpha: 0.65)),
+ ),
+ ],
+ ),
+ ),
+ const SizedBox(height: 36),
+ // Update Button
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 32.0),
+ child: ElevatedButton(
+ style: ElevatedButton.styleFrom(
+ backgroundColor: Colors.white,
+ textStyle: textTheme.headlineSmall?.copyWith(color: Colors.black),
+ ),
+ onPressed: state.isLoading
+ ? null
+ : () {
+ if (Platform.isAndroid) {
+ context.read<ForceUpdateCubit>().startAndroidUpdate();
+ } else if (Platform.isIOS) {
+ context.read<ForceUpdateCubit>().openAppStore();
+ }
+ },
+ child: state.isLoading
+ ? const SizedBox(
+ width: 24,
+ height: 24,
+ child: CircularProgressIndicator(strokeWidth: 2),
+ )
+ : Text('force_update_button'.tr()),
+ ),
+ ),
+ const SizedBox(height: 20),
+ ],
+ ),
+ ),
+ ),
+ );
+ },
+ ),
+ );
+ }
+}
diff --git a/comwell_key_app/lib/force_update/force_update_state.dart b/comwell_key_app/lib/force_update/force_update_state.dart
new file mode 100644
index 00000000..eb0dd702
--- /dev/null
+++ b/comwell_key_app/lib/force_update/force_update_state.dart
@@ -0,0 +1,18 @@
+part of 'force_update_cubit.dart';
+
+@immutable
+class ForceUpdateState {
+ final bool isLoading;
+ final String? error;
+
+ const ForceUpdateState({required this.isLoading, this.error});
+
+ factory ForceUpdateState.initial() => const ForceUpdateState(isLoading: false, error: null);
+
+ ForceUpdateState copyWith({bool? isLoading, String? error}) {
+ return ForceUpdateState(
+ isLoading: isLoading ?? this.isLoading,
+ error: error,
+ );
+ }
+}
\ No newline at end of file
diff --git a/comwell_key_app/lib/main.dart b/comwell_key_app/lib/main.dart
index cd79032a..f7f70d74 100644
--- a/comwell_key_app/lib/main.dart
+++ b/comwell_key_app/lib/main.dart
@@ -12,6 +12,7 @@ import 'firebase_options_stage.dart' as fb_stage;
import 'firebase_options_prod.dart' as fb_prod;
void main() async {
+
if (appFlavor == 'Develop') {
runMainApp(fb_dev.DefaultFirebaseOptions.currentPlatform, '.env.dev');
} else if (appFlavor == 'Stage') {
diff --git a/comwell_key_app/lib/routing/app_router.dart b/comwell_key_app/lib/routing/app_router.dart
index 05912bfa..c3cf395a 100644
--- a/comwell_key_app/lib/routing/app_router.dart
+++ b/comwell_key_app/lib/routing/app_router.dart
@@ -16,6 +16,7 @@ import 'package:comwell_key_app/contact/cubit/contact_cubit.dart';
import 'package:comwell_key_app/contact/repository/contact_repository.dart';
import 'package:comwell_key_app/find_booking/find_booking_page.dart';
import 'package:comwell_key_app/find_booking/loading_page.dart';
+import 'package:comwell_key_app/force_update/force_update_page.dart';
import 'package:comwell_key_app/hotel_information/models/facilities.dart';
import 'package:comwell_key_app/hotel_information/pages/facility_page.dart';
import 'package:comwell_key_app/hotel_information/pages/hotel_information_menu.dart';
@@ -75,6 +76,7 @@ import '../booking_details/booking_details_page.dart';
import '../booking_details/booking_details_repository.dart';
final _rootNavigatorKey = GlobalKey<NavigatorState>();
+final rootNavigatorKey = _rootNavigatorKey;
final _shellNavigatorKey = GlobalKey<NavigatorState>();
GoRouter goRouter() {
@@ -477,6 +479,12 @@ GoRouter goRouter() {
);
},
),
+ GoRoute(
+ path: "/${AppRoutes.forceUpdate.name}",
+ name: AppRoutes.forceUpdate.name,
+ builder: (context, state) {
+ return const ForceUpdatePage();
+ }),
/* GoRoute(
path: "/keys",
name: AppRoutes.keys.name,
diff --git a/comwell_key_app/lib/routing/app_routes.dart b/comwell_key_app/lib/routing/app_routes.dart
index 4620275a..483f5c6b 100644
--- a/comwell_key_app/lib/routing/app_routes.dart
+++ b/comwell_key_app/lib/routing/app_routes.dart
@@ -35,4 +35,5 @@ enum AppRoutes {
chooseShareRoom,
roomInfo,
shareRoom,
+ forceUpdate,
}
diff --git a/comwell_key_app/lib/services/interceptors/response_handle_interceptor.dart b/comwell_key_app/lib/services/interceptors/response_handle_interceptor.dart
index 6be17bd8..bbf83fb1 100644
--- a/comwell_key_app/lib/services/interceptors/response_handle_interceptor.dart
+++ b/comwell_key_app/lib/services/interceptors/response_handle_interceptor.dart
@@ -5,6 +5,9 @@ import 'package:dio/dio.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:comwell_key_app/common/const.dart' as constants;
+import 'package:comwell_key_app/routing/app_routes.dart';
+import 'package:go_router/go_router.dart';
+import 'package:flutter/material.dart';
class ResponseHandleInterceptor extends Interceptor {
final Dio _dio;
@@ -87,6 +90,16 @@ class ResponseHandleInterceptor extends Interceptor {
_authenticationRepository.logOut(forced: true);
return handler.reject(err);
}
+ } else if (statusCode == 426) {
+ // Navigate to force update page
+ final navigatorKey = locator<GlobalKey<NavigatorState>>();
+ navigatorKey.currentState?.pushNamedAndRemoveUntil(
+ '/force_update',
+ (route) => false,
+ );
+ final err = DioException(
+ requestOptions: response.requestOptions, error: 'Update required');
+ handler.next(err);
} else {
final err = DioException(
requestOptions: response.requestOptions,
diff --git a/comwell_key_app/lib/utils/locator.dart b/comwell_key_app/lib/utils/locator.dart
index f2557758..8047c776 100644
--- a/comwell_key_app/lib/utils/locator.dart
+++ b/comwell_key_app/lib/utils/locator.dart
@@ -12,6 +12,8 @@ import 'package:comwell_key_app/profile_settings/repostiory/profile_settings_rep
import 'package:comwell_key_app/tracking/comwell_tracking.dart';
import 'package:comwell_key_app/up_sales/up_sales_repository.dart';
import 'package:comwell_key_app/utils/seos_repository.dart';
+import 'package:comwell_key_app/routing/app_router.dart';
+import 'package:flutter/material.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:get_it/get_it.dart';
@@ -49,5 +51,6 @@ void setupLocator() {
locator.registerFactory<UpSalesRepository>(() => UpSalesRepository());
locator.registerFactory<ChooseShareRoomRepository>(
() => ChooseShareRoomRepository());
+ locator.registerSingleton<GlobalKey<NavigatorState>>(rootNavigatorKey);
}
}
diff --git a/comwell_key_app/pubspec.yaml b/comwell_key_app/pubspec.yaml
index 0d7fc9af..712401dd 100644
--- a/comwell_key_app/pubspec.yaml
+++ b/comwell_key_app/pubspec.yaml
@@ -57,6 +57,7 @@ dependencies:
shared_preferences: ^2.5.3
snapping_sheet: ^3.1.0
shimmer: ^3.0.0
+ in_app_update: ^4.2.3
dependency_overrides:
#Remove override when slider button updates