Files
rog_app/lib/pages/index/index_controller.dart
2024-09-10 08:12:33 +09:00

1093 lines
36 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:async';
import 'dart:io';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_polyline_points/flutter_polyline_points.dart';
import 'package:geojson_vi/geojson_vi.dart';
import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
import 'package:latlong2/latlong.dart';
import 'package:gifunavi/model/destination.dart';
import 'package:gifunavi/model/entry.dart';
import 'package:gifunavi/pages/destination/destination_controller.dart';
import 'package:gifunavi/pages/team/team_controller.dart';
import 'package:gifunavi/routes/app_pages.dart';
import 'package:gifunavi/services/auth_service.dart';
import 'package:gifunavi/services/location_service.dart';
import 'package:gifunavi/utils/database_helper.dart';
import 'package:gifunavi/widgets/debug_widget.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:gifunavi/services/api_service.dart';
import 'package:gifunavi/model/user.dart';
import 'package:gifunavi/main.dart';
import 'package:gifunavi/widgets/helper_dialog.dart';
import 'package:timezone/timezone.dart' as tz;
import 'package:timezone/data/latest.dart' as tz;
import '../permission/permission.dart';
class IndexController extends GetxController with WidgetsBindingObserver {
List<GeoJSONFeatureCollection> locations = <GeoJSONFeatureCollection>[].obs;
List<GeoJSONFeature> currentFeature = <GeoJSONFeature>[].obs;
List<Destination> currentDestinationFeature = <Destination>[].obs;
List<dynamic> perfectures = <dynamic>[].obs;
List<LatLngBounds> currentBound = <LatLngBounds>[].obs;
List<dynamic> subPerfs = <dynamic>[].obs;
List<dynamic> areas = <dynamic>[].obs;
List<dynamic> customAreas = <dynamic>[].obs;
List<dynamic> cats = <dynamic>[].obs;
List<String> currentCat = <String>[].obs;
List<Map<String, dynamic>> currentUser = <Map<String, dynamic>>[].obs;
List<dynamic> currentAction = <dynamic>[].obs;
List<PointLatLng> routePoints = <PointLatLng>[].obs;
var routePointLenght = 0.obs;
double currentLat = 0.0, currentLon = 0.0;
var isLoading = false.obs;
var isRogMapcontrollerLoaded = false.obs;
var isCustomAreaSelected = false.obs;
RxBool isMapControllerReady = RxBool(false); // MapControllerの初期化状態を管理するフラグ
//final mapControllerReadyStream = Stream<bool>.value(false); // MapControllerの初期化状態を通知するためのストリーム
MapController mapController = MapController();
MapController rogMapController = MapController();
LogManager logManager = LogManager();
String? userToken;
//late final ApiService _apiService;
final ApiService _apiService; // = Get.find<ApiService>();
final DatabaseHelper _dbHelper = DatabaseHelper.instance;
IndexController({
required ApiService apiService,
}) : _apiService = apiService;
// mode = 0 is map mode, mode = 1 list mode
var mode = 0.obs;
// master mode, rog or selection
var rogMode = 1.obs;
var desinationMode = 1.obs;
bool showPopup = true;
String dropdownValue = "9";
String subDropdownValue = "-1";
String areaDropdownValue = "-1";
String cateogory = "-all-";
final selectedEventName = 'add_location'.tr.obs;
RxBool isLoadingLocations = true.obs;
void setSelectedEventName(String eventName) {
selectedEventName.value = eventName;
}
final connectionStatus = Rx<ConnectivityResult>(ConnectivityResult.none);
var connectionStatusName = "".obs;
final Connectivity _connectivity = Connectivity();
late StreamSubscription<List<ConnectivityResult>> _connectivitySubscription;
final Rx<DateTime> lastUserUpdateTime = DateTime.now().obs;
RxInt teamId = RxInt(-1); // チームIDを保存するための変数
//late TeamController teamController = TeamController();
/*
void updateUserInfo(Map<String, dynamic> newUserInfo) {
currentUser.clear();
currentUser.add(newUserInfo);
lastUserUpdateTime.value = DateTime.now();
}
*/
final isReferenceMode = false.obs;
void setReferenceMode(bool value) {
isReferenceMode.value = value;
}
bool canStartRoge() {
return !isReferenceMode.value;
}
bool canCheckin() {
return !isReferenceMode.value;
}
void toggleMode() {
if (mode.value == 0) {
mode += 1;
} else {
mode -= 1;
}
}
void toggleDestinationMode() {
if (desinationMode.value == 0) {
desinationMode.value += 1;
} else {
desinationMode.value -= 1;
}
}
void switchPage(String page) {
////print("######## ${currentUser[0]["user"]["id"]}");
switch (page) {
case AppPages.INDEX:
{
rogMode.value = 0;
//print("-- rog mode is ctrl is ${rog_mode.value}");
Get.toNamed(page);
}
break;
case AppPages.TRAVEL:
{
rogMode.value = 1;
//Get.back();
//Get.off(DestnationPage(), binding: DestinationBinding());
}
break;
case AppPages.LOGIN:
{
rogMode.value = 2;
Get.toNamed(page);
}
break;
default:
{
rogMode.value = 1;
Get.toNamed(AppPages.INDEX);
}
}
}
Future<void> _checkLocationPermission() async {
if (Get.context == null) {
debugPrint('Get.context is null in _checkLocationPermission');
return;
}
LocationPermission permission = await Geolocator.checkPermission();
//permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
debugPrint('GPS : Denied');
await showLocationPermissionDeniedDialog();
} else if (permission == LocationPermission.deniedForever) {
debugPrint('GPS : Denied forever');
await showLocationPermissionDeniedDialog();
}else if (permission == LocationPermission.whileInUse){
debugPrint('GPS : While-In-Use');
await showLocationPermissionDeniedDialog();
}else{
debugPrint("Permission is no problem....");
}
}
// 追加
Future<void> showLocationPermissionDeniedDialog() async {
if (Get.context != null) {
print('Showing location permission denied dialog');
await showDialog(
context: Get.context!,
barrierDismissible: false,
builder: (BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: AlertDialog(
title: Text('location_permission_needed_title'.tr),
content: Text('location_permission_needed_main'.tr),
actions: [
TextButton(
onPressed: () {
logManager.addOperationLog("User tapped confirm button for location permission required.");
Navigator.of(context).pop();
},
child: Text('confirm'.tr),
),
],
),
);
},
);
} else {
print('Get.context is null in showLocationPermissionDeniedDialog');
// Get.contextがnullの場合の処理
print('Location permission denied, but context is null');
}
}
@override
void onInit() {
super.onInit();
try {
initConnectivity();
_connectivitySubscription = _connectivity.onConnectivityChanged.listen(_updateConnectionStatus);
//WidgetsBinding.instance.addPostFrameCallback((_) async {
// await PermissionController.checkAndRequestPermissions();
//});
WidgetsBinding.instance.addObserver(this);
_startLocationService(); // アプリ起動時にLocationServiceを開始する
initializeApiService();
print('IndexController onInit called'); // デバッグ用の出力を追加
tz.initializeTimeZones();
//teamController = Get.find<TeamController>();
loadLocations();
}catch(e,stacktrace){
print('Error in IndexController.onInit: $e');
print('Stack trace: $stacktrace');
// エラーレポートサービスにエラーを送信
//ErrorService.reportError(e, stackTrace, deviceInfo, LogManager().operationLogs);
}
}
Future<void> loadLocations() async {
isLoadingLocations.value = true;
try {
await waitForMapControllerReady();
String eventCode = currentUser.isNotEmpty ? currentUser[0]["user"]["event_code"] ?? "" : "";
await loadLocationsBound(eventCode);
} catch (e) {
print('Error loading locations: $e');
// エラーハンドリングを追加(例:スナックバーでユーザーに通知)
} finally {
isLoadingLocations.value = false;
}
}
void _updateConnectionStatus(List<ConnectivityResult> results) {
final result = results.isNotEmpty ? results.first : ConnectivityResult.none;
connectionStatus.value = result;
switch (result) {
case ConnectivityResult.wifi:
connectionStatusName.value = "WiFi";
break;
case ConnectivityResult.mobile:
connectionStatusName.value = "mobile";
break;
case ConnectivityResult.none:
connectionStatusName.value = "offline";
break;
default:
connectionStatusName.value = "不明";
break;
}
logManager.addOperationLog("Connection status changed to: ${connectionStatusName.value}");
if (result != ConnectivityResult.none) {
_handleConnectionRestored();
}
}
Future<void> _handleConnectionRestored() async {
await _syncPendingData();
}
Future<void> initConnectivity() async {
try {
final result = await _connectivity.checkConnectivity();
_updateConnectionStatus(result); // リストとして渡す
} on PlatformException catch (e) {
print('Couldn\'t check connectivity status: ${e.message}');
}
}
// 保留中のデータを同期する例のメソッド
Future<void> _syncPendingData() async {
// ここに保留中のデータを同期するロジックを実装
// 例: ローカルDBに保存された未送信のデータをサーバーに送信する
try {
// 同期処理
logManager.addOperationLog("Syncing pending data");
// 同期ロジックをここに実装
} catch (e) {
logManager.addOperationLog("Error syncing pending data: $e");
// エラーハンドリング
}
}
Future<void> initializeApiService() async {
if (currentUser.isNotEmpty) {
// 既にログインしている場合
await Get.putAsync(() => ApiService().init());
//await Get.putAsync(() => ApiService().init());
// 必要に応じて追加の初期化処理
}
}
/*
void checkPermission()
{
debugPrint("MapControllerの初期化が完了したら、位置情報の許可をチェックする");
_checkLocationPermission();
}
*/
@override
void onClose() {
_connectivitySubscription.cancel();
WidgetsBinding.instance.removeObserver(this);
_stopLocationService(); // アプリ終了時にLocationServiceを停止する
super.onClose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
if (!_isLocationServiceRunning()) {
_startLocationService();
}
} else if (state == AppLifecycleState.paused) {
_stopLocationService();
}
}
bool _isLocationServiceRunning() {
// LocationServiceが実行中かどうかを確認する処理を実装する
// 例えば、SharedPreferencesにサービスの状態を保存するなど
// ここでは簡単のために、常にfalseを返すようにしています
return false;
}
void _startLocationService() async {
if (Platform.isAndroid) {
const platform = MethodChannel('location');
try {
logManager.addOperationLog("Called start location service.");
await platform.invokeMethod('startLocationService');
} on PlatformException catch (e) {
print("Failed to start location service: '${e.message}'.");
}
}else if (Platform.isIOS) {
// iOSの位置情報サービス開始ロジック
// 例: geolocatorプラグインを使用する場合
try {
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
// 位置情報サービスが無効の場合の処理
return;
}
// 位置情報の権限確認と取得開始
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
// 権限が拒否された場合の処理
return;
}
}
// 位置情報の取得開始
Geolocator.getPositionStream().listen((Position position) {
// 位置情報を使用した処理
});
} catch (e) {
print('Error starting iOS location service: $e');
}
}
}
void _stopLocationService() async {
if (Platform.isAndroid) {
const platform = MethodChannel('location');
try {
logManager.addOperationLog("Called stop location service.");
await platform.invokeMethod('stopLocationService');
} on PlatformException catch (e) {
print("Failed to stop location service: '${e.message}'.");
}
}else{
debugPrint("stopLocation for iOS");
}
}
/*
@override
void onReady() async {
await readUserToken();
final token = userToken;
if (token != null && token.isNotEmpty) {
await loadUserDetailsForToken(token);
fixMapBound(token);
} else {
// ユーザートークンが存在しない場合はログイン画面にリダイレクト
Get.offAllNamed(AppPages.LOGIN);
}
// 地図のイベントリスナーを設定
indexController.mapController.mapEventStream.listen((MapEvent mapEvent) {
if (mapEvent is MapEventMoveEnd) {
indexController.loadLocationsBound();
}
});
super.onReady();
}
*/
/*
Future<void> initConnectivity() async {
late ConnectivityResult result;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
connectionStatus = (await _connectivity.checkConnectivity()) as ConnectivityResult;
await _updateConnectionStatus(connectionStatus);
} on PlatformException catch (_) {
//print('Couldn\'t check connectivity status --- $e');
return;
}
//return _updateConnectionStatus(result);
}
*/
LatLngBounds boundsFromLatLngList(List<LatLng> list) {
double? x0, x1, y0, y1;
for (LatLng latLng in list) {
if (x0 == null || x1 == null || y0 == null || y1 == null) {
x0 = x1 = latLng.latitude;
y0 = y1 = latLng.longitude;
} else {
if (latLng.latitude > x1) x1 = latLng.latitude;
if (latLng.latitude < x0) x0 = latLng.latitude;
if (latLng.longitude > y1) y1 = latLng.longitude;
if (latLng.longitude < y0) y0 = latLng.longitude;
}
}
logManager.addOperationLog("Called boundsFromLatLngList (${x1!},${y1!})-(${x0!},${y0!}).");
return LatLngBounds(LatLng(x1, y1), LatLng(x0, y0));
}
// 要検討:エラーハンドリングが行われていますが、エラーメッセージをローカライズすることを検討してください。
//
Future<void> login(String email, String password) async {
try {
final value = await AuthService.login(email, password);
if (value.isNotEmpty && value['token'] != null) {
await changeUser(value);
await _initializeUserData();
Get.offAllNamed(AppPages.INDEX);
} else {
Get.snackbar('Login Failed', 'Invalid credentials');
}
} catch (e) {
print('Login error: $e');
Get.snackbar('Login Failed', 'An error occurred. Please try again.');
}
}
Future<void> _initializeUserData() async {
try {
await fetchUserEventInfo();
await fetchTeamData();
// 他の必要なデータ取得処理
} catch (e) {
print('Error initializing user data: $e');
Get.snackbar('Error', 'Failed to load user data. Please try again.');
}
}
Future<void> login_old(String email, String password, BuildContext context) async{
try {
AuthService.login(email, password).then((value) async {
print("------- logged in user details ######## $value ###### --------");
if (value.isNotEmpty && value['token']!=null) {
logManager.addOperationLog("User logged in : $value.");
// Navigator.pop(context);
print("--------- user details login ----- $value");
// ログイン成功後、api_serviceを初期化
await Get.putAsync(() => ApiService().init());
// ユーザー情報の完全性をチェック
if (await checkUserInfoComplete()) {
Get.offAllNamed(AppPages.INDEX);
} else {
Get.offAllNamed(AppPages.USER_DETAILS_EDIT);
}
} else {
logManager.addOperationLog("User failed login : $email , $password.");
isLoading.value = false;
Get.snackbar(
"login_failed".tr,
"check_login_id_or_password".tr,
backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon(Icons.error, size: 40.0, color: Colors.blue),
snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 3),
//backgroundColor: Colors.yellow,
//icon:Image(image:AssetImage("assets/images/dora.png"))
);
}
});
} catch(e ){
print('Login error: $e');
Get.snackbar('Login Failed', 'An error occurred. Please try again.');
}
}
// 要検討:エラーハンドリングが行われていますが、エラーメッセージをローカライズすることを検討してください。
//
void changePassword(
String oldpassword, String newpassword, BuildContext context) {
String token = currentUser[0]['token'];
////print("------- change password ######## ${currentUser[0]['token']} ###### --------");
AuthService.changePassword(oldpassword, newpassword, token).then((value) {
////print("------- change password ######## $value ###### --------");
if (value.isNotEmpty) {
logManager.addOperationLog("User successed to change password : $oldpassword , $newpassword.");
isLoading.value = false;
Navigator.pop(context);
if (rogMode.value == 1) {
switchPage(AppPages.TRAVEL);
} else {
switchPage(AppPages.INDEX);
}
} else {
logManager.addOperationLog("User failed to change password : $oldpassword , $newpassword.");
Get.snackbar(
'failed'.tr,
'password_change_failed_please_try_again'.tr,
backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon(Icons.error, size: 40.0, color: Colors.blue),
snackPosition: SnackPosition.TOP,
duration: const Duration(milliseconds: 800),
//backgroundColor: Colors.yellow,
//icon:Image(image:AssetImage("assets/images/dora.png"))
);
}
});
isLoading.value = false;
}
/*
void logout() async {
locations.clear();
DatabaseHelper db = DatabaseHelper.instance;
db.deleteAllDestinations().then((value) {
DestinationController destinationController =
Get.find<DestinationController>();
destinationController.populateDestinations();
});
currentUser.clear();
cats.clear();
// ユーザートークンをデバイスから削除
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.remove("user_token");
}
*/
Future<void> logout() async {
logManager.addOperationLog("User logout : $currentUser .");
saveGameState();
locations.clear();
DatabaseHelper db = DatabaseHelper.instance;
db.deleteAllDestinationsExceptTodayCheckins().then((value) {
DestinationController destinationController =
Get.find<DestinationController>();
destinationController.populateDestinations();
});
currentUser.clear();
cats.clear();
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.remove("user_token");
}
// 要検討:エラーハンドリングが行われていますが、エラーメッセージをローカライズすることを検討してください。
//
void register(String email, String password, String password2, BuildContext context) {
AuthService.register(email, password,password2).then((value) {
if (value.containsKey("error")) {
String errMessage = value["error"];
debugPrint("ユーザー登録失敗:$email, $password,$errMessage");
logManager.addOperationLog("User failed to register new account : $email , $password ,$errMessage.");
isLoading.value = false;
Get.snackbar(
'user_registration_failed_please_try_again'.tr, // ユーザー登録に失敗しました。
errMessage,
backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon(Icons.error, size: 40.0, color: Colors.blue),
snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 3),
//backgroundColor: Colors.yellow,
//icon:Image(image:AssetImage("assets/images/dora.png"))
);
}else{
debugPrint("ユーザー登録成功:$email, $password");
logManager.addOperationLog("User tried to register new account : $email , $password .");
currentUser.clear();
//currentUser.add(value);
isLoading.value = false;
// ユーザー登録成功メッセージを表示
Get.snackbar(
'success'.tr,
'user_registration_successful'.tr,
backgroundColor: Colors.green,
colorText: Colors.white,
snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 3),
);
//Navigator.pop(context);
Get.toNamed(AppPages.LOGIN);
}
});
}
void saveToDevice(String val) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString("user_token", val);
print("saveToDevice: $val");
}
/*
void changeUser(Map<String, dynamic> value, {bool replace = true}) {
print("---- change user to $value -----");
currentUser.clear();
currentUser.add(value);
if (replace) {
saveToDevice(currentUser[0]["token"]);
}
isLoading.value = false;
loadLocationsBound();
if (currentUser.isNotEmpty) {
rogMode.value = 0;
} else {
rogMode.value = 1;
}
print('--- c rog mode --- ${rogMode.value}');
Get.toNamed(AppPages.INDEX);
}
*/
Future<void> changeUser(Map<String, dynamic> value, {bool replace = true}) async{
try {
if (value['user'] == null || value['token'] == null) {
throw Exception('Invalid user data');
}
currentUser.clear();
currentUser.add(value);
if (replace) {
saveToDevice(currentUser[0]["token"]);
}
isLoading.value = false;
// ユーザーのイベント情報を取得
await fetchUserEventInfo();
loadLocationsBound(currentUser[0]["user"]["event_code"]);
if (currentUser.isNotEmpty) {
rogMode.value = 0;
restoreGame();
// チームデータを取得
await fetchTeamData();
} else {
rogMode.value = 1;
}
Get.toNamed(AppPages.INDEX);
} catch( e ){
print('Error in changeUser: $e');
Get.snackbar('Error', 'Failed to update user information');
}
}
Future<bool> checkUserInfoComplete() async {
try {
final user = await ApiService.to.getCurrentUser();
return user.firstname.isNotEmpty &&
user.lastname.isNotEmpty &&
user.dateOfBirth != null;
} catch (e) {
print('Error checking user info: $e');
return false;
}
}
Future<void> fetchUserEventInfo() async {
try {
final List<Entry> entries = await _apiService.getEntries();
if (entries.isNotEmpty) {
final Entry latestEntry = entries.last;
final tokyo = tz.getLocation('Asia/Tokyo');
final eventDate = latestEntry.date!.toUtc();
//final eventDate = tz.TZDateTime.from(utcDate, tokyo);
final eventDateOnly = tz.TZDateTime(tokyo, eventDate.year, eventDate.month, eventDate.day);
currentUser[0]['user']['event_date'] = eventDateOnly.toIso8601String().split('T')[0];
currentUser[0]['user']['event_code'] = latestEntry.event.eventName;
currentUser[0]['user']['team_name'] = latestEntry.team.teamName;
currentUser[0]['user']['group'] = latestEntry.team.category.categoryName;
currentUser[0]['user']['zekken_number'] = latestEntry.zekkenNumber;
// 最後のゴール日時を取得
final lastGoalTime = await getLastGoalTime();
currentUser[0]['user']['last_goal_time'] = lastGoalTime?.toIso8601String();
print('Updated user event info: ${currentUser[0]['user']}');
} else {
print('No entries found for the user');
_clearUserEventInfo();
}
} catch (e) {
print('Error fetching user event info: $e');
_clearUserEventInfo();
}
}
Future<DateTime?> getLastGoalTime() async {
try {
final userId = currentUser[0]['user']['id'];
return await _apiService.getLastGoalTime(userId);
} catch (e) {
print('Error getting last goal time: $e');
}
return null;
}
void _clearUserEventInfo() {
currentUser[0]['user']['event_date'] = null;
currentUser[0]['user']['event_code'] = null;
currentUser[0]['user']['team_name'] = null;
currentUser[0]['user']['group'] = null;
currentUser[0]['user']['zekken_number'] = null;
}
Future<void> fetchTeamData() async {
try {
Get.put(TeamController(apiService:Get.find<ApiService>()));
// \"TeamController\" not found. You need to call \"Get.put(TeamController())\" or \"Get.lazyPut(()=>TeamController())\"
final teamController = Get.find<TeamController>();
await teamController.fetchTeams();
if (teamController.teams.isNotEmpty) {
teamId.value = teamController.teams.first.id;
}
} catch (e) {
print("Error fetching team data: $e");
}
}
loadUserDetailsForToken(String token) async {
AuthService.userForToken(token).then((value) {
print("----token val-- $value ------");
if (value![0]["user"].isEmpty) {
Get.toNamed(AppPages.LOGIN);
return;
}
changeUser(value[0], replace: false);
});
}
/* Old code
void loadLocationsBound() {
if (isCustomAreaSelected.value == true) {
return;
}
locations.clear();
String cat = currentCat.isNotEmpty ? currentCat[0] : "";
if (currentCat.isNotEmpty && currentCat[0] == "-all-") {
cat = "";
}
LatLngBounds bounds = mapController.bounds!;
currentBound.clear();
currentBound.add(bounds);
////print(currentCat);
LocationService.loadLocationsBound(
bounds.southWest.latitude,
bounds.southWest.longitude,
bounds.northWest.latitude,
bounds.northWest.longitude,
bounds.northEast.latitude,
bounds.northEast.longitude,
bounds.southEast.latitude,
bounds.southEast.longitude,
cat)
.then((value) {
////print("---value length ------ ${value!.collection.length}");
if (value == null) {
return;
}
if (value.features.isEmpty) {
if (showPopup == false) {
return;
}
Get.snackbar(
"Too many Points",
"please zoom in",
icon: const Icon(Icons.assistant_photo_outlined,
size: 40.0, color: Colors.blue),
snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 2),
backgroundColor: Colors.yellow,
//icon:Image(image:AssetImage("assets/images/dora.png"))
);
showPopup = false;
//Get.showSnackbar(GetSnackBar(message: "Too many points, please zoom in",));
}
if (value.features.isNotEmpty) {
////print("---- added---");
locations.add(value);
}
});
}
*/
// 2024-04-03 Akira .. Update the code . See ticket 2800.
//
// 2024-4-8 Akira : See 2809
// IndexControllerクラスでは、Future.delayedの呼び出しをunawaitedで囲んで、
// 非同期処理の結果を待たずに先に進むようにしました。これにより、メモリリークを防ぐことができます
//
// 要検討Future.delayedを使用して非同期処理を待たずに先に進むようにしていますが、
// これによってメモリリークが発生する可能性があります。非同期処理の結果を適切に処理することを検討してください。
//
Future<void> loadLocationsBound(String eventCode) async {
if (isCustomAreaSelected.value == true) {
return;
}
// MapControllerの初期化が完了するまで待機
await waitForMapControllerReady();
// null チェックを追加
if (mapController.bounds == null) {
print("MapController bounds are null");
return;
}
// バウンドが有効かどうかを確認する
LatLngBounds bounds = mapController.bounds!;
if (!_isValidBounds(bounds)) {
print("MapController bounds are not valid");
return;
}
locations.clear();
String cat = currentCat.isNotEmpty ? currentCat[0] : "";
if (currentCat.isNotEmpty && currentCat[0] == "-all-") {
cat = "";
}
/*
// Akira Add 2024-4-6
if( mapController.controller == null ) {
print("操作が完了する前にMapControllerまたはウィジェットが破棄されました。");
isLoading.value = true; // ローディング状態をtrueに設定
return;
}
//
*/
//LatLngBounds bounds = mapController.bounds!;
currentBound.clear();
currentBound.add(bounds);
//isLoading.value = true; // ローディング状態をtrueに設定
//print("bounds --- (${bounds.southWest.latitude},${bounds.southWest.longitude}),(${bounds.northWest.latitude},${bounds.northWest.longitude}),(${bounds.northEast.latitude},${bounds.northEast.longitude}),(${bounds.southEast.latitude},${bounds.southEast.longitude})");
// 要検討APIからのレスポンスがnullの場合のエラーハンドリングが不十分です。適切なエラーメッセージを表示するなどの処理を追加してください。
try {
final eventCode = currentUser[0]["user"]["event_code"];
final value = await LocationService.loadLocationsBound(
bounds.southWest.latitude,
bounds.southWest.longitude,
bounds.northWest.latitude,
bounds.northWest.longitude,
bounds.northEast.latitude,
bounds.northEast.longitude,
bounds.southEast.latitude,
bounds.southEast.longitude,
cat,
eventCode
);
/*
if (value == null) {
// APIからのレスポンスがnullの場合
print("LocationService.loadLocationsBound からの回答がnullのため、マップをリロード");
DestinationController destinationController = Get.find<DestinationController>(); // 追加
final tk = currentUser[0]["token"]; // 追加
if (tk != null) { // 追加
destinationController.fixMapBound(tk); // 追加
} // 追加
return;
}
*/
isLoading.value = false; // ローディング状態をfalseに設定
if (value == null) {
// APIからのレスポンスがnullの場合
print("LocationService.loadLocationsBound からの回答がnullです");
} else {
if (value.features.isEmpty) {
if (showPopup == false) {
return;
}
Get.snackbar(
"Too many Points",
"please zoom in",
backgroundColor: Colors.yellow,
colorText: Colors.white,
icon: const Icon(Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue),
snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 3),
);
showPopup = false;
}
if (value.features.isNotEmpty) {
locations.add(value);
}
}
/*
if (value != null && value.features.isEmpty) {
if (showPopup == false) {
return;
}
Get.snackbar(
"Too many Points",
"please zoom in",
backgroundColor: Colors.yellow,
colorText: Colors.white,
icon: const Icon(
Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue),
snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 3),
//backgroundColor: Colors.yellow,
);
showPopup = false;
}
if (value != null && value.features.isNotEmpty) {
locations.add(value);
}
*/
} catch ( e) {
print("Error in loadLocationsBound: $e");
// エラーが発生した場合のリトライ処理や適切なエラーメッセージの表示を行う
// 例えば、一定時間後に再度loadLocationsBound()を呼び出すなど
}
}
// バウンドが有効かどうかを確認するヘルパーメソッド
bool _isValidBounds(LatLngBounds bounds) {
// 緯度と経度が有効な範囲内にあるかチェック
return bounds.southWest.latitude.abs() <= 90 &&
bounds.southWest.longitude.abs() <= 180 &&
bounds.northEast.latitude.abs() <= 90 &&
bounds.northEast.longitude.abs() <= 180 &&
bounds.southWest.latitude < bounds.northEast.latitude;
}
//===Akira 追加:2024-4-6 #2800
// 要検討MapControllerの初期化が完了するまで待機していますが、タイムアウトを設定することを検討してください。
// 初期化に時間がかかりすぎる場合、ユーザーにわかりやすいメッセージを表示するようにしてください。
//
Future<void> waitForMapControllerReady() async {
if (!isMapControllerReady.value) {
await Future.doWhile(() async {
await Future.delayed(const Duration(milliseconds: 100));
return !isMapControllerReady.value;
});
}
}
//===Akira 追加:2024-4-6 #2800
void setBound(LatLngBounds bounds) {
currentBound.clear();
currentBound.add(bounds);
}
GeoJSONFeature? getFeatureForLatLong(double lat, double long) {
if (locations.isNotEmpty) {
GeoJSONFeature? foundFeature;
for (var i in locations[0].features) {
GeoJSONMultiPoint p = i!.geometry as GeoJSONMultiPoint;
if (p.coordinates[0][1] == lat && p.coordinates[0][0] == long) {
foundFeature = i;
}
}
return foundFeature;
}
return null;
}
void reloadMap( String eventCode ) {
// マップをリロードするロジックを実装
// 例: 現在の位置情報を再取得し、マップを更新する
loadLocationsBound( eventCode );
}
Future<void> checkEntryData() async {
// エントリーデータの有無をチェックするロジック
final teamController = TeamController(apiService:Get.find<ApiService>());
bool hasEntryData = teamController.checkIfUserHasEntryData();
if (!hasEntryData) {
await showHelperDialog(
'イベントに参加するには、チーム登録・メンバー登録及びエントリーが必要になります。',
'entry_check'
);
// ドロワーを表示するロジック
Get.toNamed('/drawer');
}
}
void updateCurrentUser(User updatedUser) {
currentUser[0]['user'] = updatedUser.toJson();
update();
}
}