Fixed Android issues

This commit is contained in:
2024-09-10 16:16:04 +09:00
parent 8ed6a4e8bf
commit 7976f4c75b
5 changed files with 255 additions and 108 deletions

View File

@ -11,3 +11,6 @@
起動時に最後の参加イベントが過去日だったら、 起動時に最後の参加イベントが過去日だったら、
チェックポイントをクリアする。 チェックポイントをクリアする。
当日なら、参加処理?をしてタイトルを変える。 当日なら、参加処理?をしてタイトルを変える。
チーム構成とエントリーの相関が難しいのでは??

View File

@ -88,6 +88,9 @@ Future<void> saveGameState() async {
pref.setBool( pref.setBool(
"rogaining_counted", destinationController.rogainingCounted.value); "rogaining_counted", destinationController.rogainingCounted.value);
pref.setBool("ready_for_goal", DestinationController.ready_for_goal); pref.setBool("ready_for_goal", DestinationController.ready_for_goal);
// 最後のゲーム日時を保存
pref.setString('lastGameDate', DateTime.now().toIso8601String());
} }
@ -108,6 +111,20 @@ Future<void> restoreGame() async {
if (indexController.currentUser.isNotEmpty && if (indexController.currentUser.isNotEmpty &&
indexController.currentUser[0]["user"]["id"] == savedUserId) { indexController.currentUser[0]["user"]["id"] == savedUserId) {
// 最後のゲーム日時を取得
final lastGameDateString = pref.getString('lastGameDate');
if (lastGameDateString != null) {
final lastGameDate = DateTime.parse(lastGameDateString);
final now = DateTime.now();
// 最後のゲームが昨日以前の場合
if (lastGameDate.isBefore(DateTime(now.year, now.month, now.day))) {
// ゲームの状態をクリア
await resetGameState();
return; // ここで関数を終了
}
}
final dateString = pref.getString('eventDate'); final dateString = pref.getString('eventDate');
if (dateString != null) { if (dateString != null) {
final parsedDate = DateTime.parse(dateString); final parsedDate = DateTime.parse(dateString);
@ -143,6 +160,24 @@ Future<void> restoreGame() async {
} }
} }
// ゲームの状態をリセットする関数
Future<void> resetGameState() async {
SharedPreferences pref = await SharedPreferences.getInstance();
await pref.remove("is_in_rog");
await pref.remove("rogaining_counted");
await pref.remove("ready_for_goal");
DestinationController destinationController = Get.find<DestinationController>();
destinationController.isInRog.value = false;
destinationController.rogainingCounted.value = false;
DestinationController.ready_for_goal = false;
// チェックポイントをクリア
destinationController.deleteDBDestinations();
debugPrint("Game state has been reset due to outdated last game date");
}
/* /*
void restoreGame_new() async { void restoreGame_new() async {
SharedPreferences pref = await SharedPreferences.getInstance(); SharedPreferences pref = await SharedPreferences.getInstance();

View File

@ -32,6 +32,8 @@ class EntryController extends GetxController {
final activeEvents = <Event>[].obs; //有効なイベントリスト final activeEvents = <Event>[].obs; //有効なイベントリスト
final teamMembers = <User>[].obs; final teamMembers = <User>[].obs;
final hasError = false.obs;
final errorMessage = "".obs;
@override @override
void onInit() async { void onInit() async {
@ -45,7 +47,7 @@ class EntryController extends GetxController {
_apiService = await Get.putAsync(() => ApiService().init()); _apiService = await Get.putAsync(() => ApiService().init());
} catch (e) { } catch (e) {
print('Error initializing ApiService: $e'); print('Error initializing ApiService: $e');
Get.snackbar('Error', 'Failed to initialize API service'); Get.snackbar('Error', 'APIサービスの初期化に失敗しました');
} }
} }
@ -64,14 +66,16 @@ class EntryController extends GetxController {
initializeEditMode(currentEntry.value!); initializeEditMode(currentEntry.value!);
} else { } else {
// 新規作成モードの場合、最初のイベントを選択 // 新規作成モードの場合、最初のイベントを選択
if (events.isNotEmpty) { if (activeEvents.isNotEmpty) {
selectedEvent.value = activeEvents.first; selectedEvent.value = activeEvents.first;
selectedDate.value = activeEvents.first.startDatetime; selectedDate.value = activeEvents.first.startDatetime;
} }
} }
} catch(e) { } catch(e) {
print('Error initializing data: $e'); print('Error initializing data: $e');
Get.snackbar('Error', 'Failed to load initial data'); // エラー状態を設定
hasError.value = true;
Get.snackbar('Error', '初期データの読み込みに失敗しました');
} finally { } finally {
isLoading.value = false; isLoading.value = false;
} }

View File

@ -7,6 +7,7 @@ import 'package:gifunavi/model/event.dart';
import 'package:gifunavi/model/category.dart'; import 'package:gifunavi/model/category.dart';
import 'package:gifunavi/model/team.dart'; import 'package:gifunavi/model/team.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:gifunavi/widgets/error_widget.dart';
import 'package:timezone/timezone.dart' as tz; import 'package:timezone/timezone.dart' as tz;
@ -31,6 +32,17 @@ class EntryDetailPage extends GetView<EntryController> {
if (controller.isLoading.value) { if (controller.isLoading.value) {
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
} }
if (controller.hasError.value) {
return CustomErrorWidget(
errorMessage: controller.errorMessage.value,
onRetry: () => controller.loadInitialData(),
);
}
try {
return Padding( return Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView( child: SingleChildScrollView(
@ -41,9 +53,22 @@ class EntryDetailPage extends GetView<EntryController> {
label: 'イベント', label: 'イベント',
items: controller.activeEvents, items: controller.activeEvents,
selectedId: controller.selectedEvent.value?.id, selectedId: controller.selectedEvent.value?.id,
onChanged: (eventId) {
final event = controller.activeEvents.firstWhereOrNull((
e) => e.id == eventId);
if (event != null) {
controller.updateEvent(event);
} else {
print('Event with id $eventId not found');
// 必要に応じてエラー処理を追加
}
},
/*
onChanged: (eventId) => controller.updateEvent( onChanged: (eventId) => controller.updateEvent(
controller.activeEvents.firstWhere((e) => e.id == eventId) controller.activeEvents.firstWhere((e) => e.id == eventId)
), ),
*/
getDisplayName: (event) => event.eventName, getDisplayName: (event) => event.eventName,
getId: (event) => event.id, getId: (event) => event.id,
), ),
@ -52,9 +77,21 @@ class EntryDetailPage extends GetView<EntryController> {
label: 'チーム', label: 'チーム',
items: controller.teams, items: controller.teams,
selectedId: controller.selectedTeam.value?.id, selectedId: controller.selectedTeam.value?.id,
onChanged: (teamId) {
final team = controller.teams.firstWhereOrNull((t) =>
t.id == teamId);
if (team != null) {
controller.updateTeam(team);
} else {
print('Team with id $teamId not found');
// 必要に応じてエラー処理を追加
}
},
/*
onChanged: (teamId) => controller.updateTeam( onChanged: (teamId) => controller.updateTeam(
controller.teams.firstWhere((t) => t.id == teamId) controller.teams.firstWhere((t) => t.id == teamId)
), ),
*/
getDisplayName: (team) => team.teamName, getDisplayName: (team) => team.teamName,
getId: (team) => team.id, getId: (team) => team.id,
), ),
@ -78,7 +115,9 @@ class EntryDetailPage extends GetView<EntryController> {
title: const Text('日付'), title: const Text('日付'),
subtitle: Text( subtitle: Text(
controller.selectedDate.value != null controller.selectedDate.value != null
? DateFormat('yyyy-MM-dd').format(tz.TZDateTime.from(controller.selectedDate.value!, tz.getLocation('Asia/Tokyo'))) ? DateFormat('yyyy-MM-dd').format(tz.TZDateTime.from(
controller.selectedDate.value!,
tz.getLocation('Asia/Tokyo')))
: '日付を選択してください', : '日付を選択してください',
), ),
onTap: () async { onTap: () async {
@ -86,25 +125,37 @@ class EntryDetailPage extends GetView<EntryController> {
Get.snackbar('Error', 'Please select an event first'); Get.snackbar('Error', 'Please select an event first');
return; return;
} }
final tz.TZDateTime now = tz.TZDateTime.now(tz.getLocation('Asia/Tokyo')); final tz.TZDateTime now = tz.TZDateTime.now(tz
final tz.TZDateTime eventStart = tz.TZDateTime.from(controller.selectedEvent.value!.startDatetime, tz.getLocation('Asia/Tokyo')); .getLocation('Asia/Tokyo'));
final tz.TZDateTime eventEnd = tz.TZDateTime.from(controller.selectedEvent.value!.endDatetime, tz.getLocation('Asia/Tokyo')); final tz.TZDateTime eventStart = tz.TZDateTime.from(
controller.selectedEvent.value!.startDatetime, tz
.getLocation('Asia/Tokyo'));
final tz.TZDateTime eventEnd = tz.TZDateTime.from(
controller.selectedEvent.value!.endDatetime, tz
.getLocation('Asia/Tokyo'));
final tz.TZDateTime initialDate = controller.selectedDate.value != null final tz.TZDateTime initialDate = controller.selectedDate
? tz.TZDateTime.from(controller.selectedDate.value!, tz.getLocation('Asia/Tokyo')) .value != null
? tz.TZDateTime.from(controller.selectedDate.value!,
tz.getLocation('Asia/Tokyo'))
: (now.isAfter(eventStart) ? now : eventStart); : (now.isAfter(eventStart) ? now : eventStart);
// 選択可能な最初の日付を設定(今日かイベント開始日のうち、より後の日付) // 選択可能な最初の日付を設定(今日かイベント開始日のうち、より後の日付)
final tz.TZDateTime firstDate = now.isAfter(eventStart) ? now : eventStart; final tz.TZDateTime firstDate = now.isAfter(eventStart)
? now
: eventStart;
final DateTime? picked = await showDatePicker( final DateTime? picked = await showDatePicker(
context: context, context: context,
initialDate: initialDate.isAfter(firstDate) ? initialDate : firstDate, initialDate: initialDate.isAfter(firstDate)
? initialDate
: firstDate,
firstDate: firstDate, firstDate: firstDate,
lastDate: eventEnd, lastDate: eventEnd,
); );
if (picked != null) { if (picked != null) {
controller.updateDate(tz.TZDateTime.from(picked, tz.getLocation('Asia/Tokyo'))); controller.updateDate(tz.TZDateTime.from(
picked, tz.getLocation('Asia/Tokyo')));
} }
}, },
), ),
@ -151,6 +202,12 @@ class EntryDetailPage extends GetView<EntryController> {
), ),
), ),
); );
} catch (e) {
print('Error in EntryDetailPage: $e');
return const Center(
child: Text('エラーが発生しました。もう一度お試しください。'),
);
}
}), }),
); );
} }
@ -166,11 +223,17 @@ class EntryDetailPage extends GetView<EntryController> {
return DropdownButtonFormField<int>( return DropdownButtonFormField<int>(
decoration: InputDecoration(labelText: label), decoration: InputDecoration(labelText: label),
value: selectedId, value: selectedId,
items: items.map((item) => DropdownMenuItem<int>( items: items.isNotEmpty ? items.map((item) => DropdownMenuItem<int>(
//items: items.map((item) => DropdownMenuItem<int>(
value: getId(item), value: getId(item),
child: Text(getDisplayName(item)), child: Text(getDisplayName(item)),
)).toList(), )).toList() : null,
onChanged: onChanged, onChanged: (value) {
if (value != null) {
onChanged(value);
}
},
//onChanged: onChanged,
); );
} }
@ -180,11 +243,18 @@ class EntryDetailPage extends GetView<EntryController> {
return DropdownButtonFormField<NewCategory>( return DropdownButtonFormField<NewCategory>(
decoration: InputDecoration(labelText: 'カテゴリ'), decoration: InputDecoration(labelText: 'カテゴリ'),
value: controller.selectedCategory.value, value: controller.selectedCategory.value,
items: eligibleCategories.map((category) => DropdownMenuItem<NewCategory>( items: eligibleCategories.isNotEmpty ? eligibleCategories.map((category) => DropdownMenuItem<NewCategory>(
//items: eligibleCategories.map((category) => DropdownMenuItem<NewCategory>(
value: category, value: category,
child: Text(category.categoryName), child: Text(category.categoryName),
)).toList(), )).toList() : null,
onChanged: (value) => controller.updateCategory(value), onChanged: (value) {
if (value != null) {
controller.updateCategory(value);
}
},
//onChanged: (value) => controller.updateCategory(value),
); );
} }

View File

@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class CustomErrorWidget extends StatelessWidget {
final String errorMessage;
final VoidCallback onRetry;
const CustomErrorWidget({
Key? key,
required this.errorMessage,
required this.onRetry,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'エラーが発生しました',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Text(errorMessage),
const SizedBox(height: 16),
ElevatedButton(
onPressed: onRetry,
child: const Text('再試行'),
),
],
),
);
}
}