...
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
/// A Calculator.
|
||||
class Calculator {
|
||||
/// Returns [value] plus 1.
|
||||
int addOne(int value) => value + 1;
|
||||
}
|
||||
// packages/feature_game_finddiff/lib/feature_game_finddiff.dart
|
||||
|
||||
// 메인 앱이 사용할 로비 화면 export
|
||||
export 'screens/finddiff_lobby_screen.dart';
|
||||
@@ -4,11 +4,10 @@ import 'dart:convert';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
// 현재 로그인 세션을 담을 모델
|
||||
class UserSession {
|
||||
final String userId;
|
||||
final String? userName;
|
||||
final String loginProvider; // "guest", "google", "apple"
|
||||
final String loginProvider;
|
||||
final String? email;
|
||||
|
||||
UserSession({
|
||||
@@ -21,221 +20,119 @@ class UserSession {
|
||||
bool get isGuest => loginProvider == "guest";
|
||||
}
|
||||
|
||||
// 앱-고유 ID와 사용자 이름, 레벨 진행 상황을 관리하는 서비스
|
||||
class IdentityService {
|
||||
static const String _userIdKey = 'app_user_id';
|
||||
static const String _userNameKey = 'app_user_name';
|
||||
static const String _loginProviderKey = 'app_login_provider';
|
||||
static const String _userEmailKey = 'app_user_email';
|
||||
|
||||
// 게임별 저장 키 정의
|
||||
// 기존 게임 키들
|
||||
static const String _sudokuMaxLevelKey = 'max_unlocked_level';
|
||||
static const String _sudokuRankMapKey = 'last_checked_rank_map';
|
||||
|
||||
static const String _spiderMaxLevelKey = 'max_unlocked_spider_level';
|
||||
static const String _spiderRankMapKey = 'last_checked_spider_rank_map';
|
||||
|
||||
static const String _mathQuizMaxLevelKey = 'max_unlocked_mathquiz_level';
|
||||
static const String _mathQuizRankMapKey = 'last_checked_mathquiz_rank_map';
|
||||
|
||||
// [🔥 신규] 색상 일치 게임 키
|
||||
static const String _colorMatchMaxLevelKey = 'max_unlocked_colormatch_level';
|
||||
static const String _colorMatchRankMapKey = 'last_checked_colormatch_rank_map';
|
||||
|
||||
// [🔥 신규] 순서 기억 게임 키
|
||||
static const String _sequenceMaxLevelKey = 'max_unlocked_sequence_level';
|
||||
static const String _sequenceRankMapKey = 'last_checked_sequence_rank_map';
|
||||
|
||||
// [🔥 신규] 카드 뒤집기 게임 키
|
||||
static const String _cardFlipMaxLevelKey = 'max_unlocked_cardflip_level';
|
||||
static const String _cardFlipRankMapKey = 'last_checked_cardflip_rank_map';
|
||||
|
||||
// 🔽 [🔥 신규] 다른 그림 찾기 키 추가
|
||||
static const String _findDiffMaxLevelKey = 'max_unlocked_finddiff_level';
|
||||
static const String _findDiffRankMapKey = 'last_checked_finddiff_rank_map';
|
||||
|
||||
final _storage = const FlutterSecureStorage();
|
||||
|
||||
IOSOptions _getIOSOptions() => const IOSOptions();
|
||||
|
||||
AndroidOptions _getAndroidOptions() => const AndroidOptions(
|
||||
encryptedSharedPreferences: true,
|
||||
);
|
||||
AndroidOptions _getAndroidOptions() => const AndroidOptions(encryptedSharedPreferences: true);
|
||||
|
||||
// 1. 현재 세션 정보를 '객체'로 가져오기
|
||||
Future<UserSession> getUserSession() async {
|
||||
final userId = await getOrCreateUserId();
|
||||
final userName = await getSavedUserName();
|
||||
final loginProvider = await _storage.read(
|
||||
key: _loginProviderKey,
|
||||
iOptions: _getIOSOptions(),
|
||||
aOptions: _getAndroidOptions()
|
||||
) ?? "guest";
|
||||
final email = await _storage.read(
|
||||
key: _userEmailKey,
|
||||
iOptions: _getIOSOptions(),
|
||||
aOptions: _getAndroidOptions()
|
||||
);
|
||||
|
||||
return UserSession(
|
||||
userId: userId,
|
||||
userName: userName,
|
||||
loginProvider: loginProvider,
|
||||
email: email,
|
||||
);
|
||||
final loginProvider = await _storage.read(key: _loginProviderKey, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions()) ?? "guest";
|
||||
final email = await _storage.read(key: _userEmailKey, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
|
||||
return UserSession(userId: userId, userName: userName, loginProvider: loginProvider, email: email);
|
||||
}
|
||||
|
||||
// 2. 앱-고유 ID 가져오기 (없으면 생성)
|
||||
Future<String> getOrCreateUserId() async {
|
||||
String? userId = await _storage.read(
|
||||
key: _userIdKey,
|
||||
iOptions: _getIOSOptions(),
|
||||
aOptions: _getAndroidOptions(),
|
||||
);
|
||||
|
||||
String? userId = await _storage.read(key: _userIdKey, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
|
||||
if (userId == null) {
|
||||
userId = const Uuid().v4();
|
||||
await _storage.write(
|
||||
key: _userIdKey,
|
||||
value: userId,
|
||||
iOptions: _getIOSOptions(),
|
||||
aOptions: _getAndroidOptions(),
|
||||
);
|
||||
await _storage.write(key: _userIdKey, value: userId, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
// 3. 랭킹에 등록한 사용자 이름 가져오기
|
||||
Future<String?> getSavedUserName() async {
|
||||
return await _storage.read(
|
||||
key: _userNameKey,
|
||||
iOptions: _getIOSOptions(),
|
||||
aOptions: _getAndroidOptions(),
|
||||
);
|
||||
return await _storage.read(key: _userNameKey, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
|
||||
}
|
||||
|
||||
// 4. 랭킹 등록 성공 시, 사용자 이름 저장하기
|
||||
Future<void> saveUserName(String name) async {
|
||||
await _storage.write(
|
||||
key: _userNameKey,
|
||||
value: name,
|
||||
iOptions: _getIOSOptions(),
|
||||
aOptions: _getAndroidOptions(),
|
||||
);
|
||||
await _storage.write(key: _userNameKey, value: name, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
|
||||
}
|
||||
|
||||
// 5. 소셜 로그인 성공 시 호출 (계정 연결)
|
||||
Future<UserSession> saveSocialLogin({
|
||||
required String newUserId,
|
||||
required String newUserName,
|
||||
required String newEmail,
|
||||
required String provider,
|
||||
}) async {
|
||||
Future<UserSession> saveSocialLogin({required String newUserId, required String newUserName, required String newEmail, required String provider}) async {
|
||||
await _storage.write(key: _userIdKey, value: newUserId, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
|
||||
await _storage.write(key: _userNameKey, value: newUserName, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
|
||||
await _storage.write(key: _userEmailKey, value: newEmail, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
|
||||
await _storage.write(key: _loginProviderKey, value: provider, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
|
||||
|
||||
return UserSession(
|
||||
userId: newUserId,
|
||||
userName: newUserName,
|
||||
loginProvider: provider,
|
||||
email: newEmail,
|
||||
);
|
||||
return UserSession(userId: newUserId, userName: newUserName, loginProvider: provider, email: newEmail);
|
||||
}
|
||||
|
||||
// 6. 로그아웃 (게스트 계정으로 되돌리기)
|
||||
Future<UserSession> logout() async {
|
||||
await _storage.delete(key: _userNameKey, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
|
||||
await _storage.delete(key: _userEmailKey, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
|
||||
await _storage.delete(key: _loginProviderKey, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
|
||||
|
||||
return await getUserSession();
|
||||
}
|
||||
|
||||
// 7. [수정] 최대 레벨 가져오기 (모든 게임 타입 지원)
|
||||
// 7. [수정] 최대 레벨 가져오기
|
||||
Future<int> getMaxUnlockedLevel({String gameType = 'SUDOKU'}) async {
|
||||
String key;
|
||||
switch (gameType) {
|
||||
case 'SPIDER':
|
||||
key = _spiderMaxLevelKey;
|
||||
break;
|
||||
case 'MATH_QUIZ':
|
||||
key = _mathQuizMaxLevelKey;
|
||||
break;
|
||||
case 'COLOR_MATCH': // 👈 추가
|
||||
key = _colorMatchMaxLevelKey;
|
||||
break;
|
||||
case 'SEQUENCE': // 👈 추가
|
||||
key = _sequenceMaxLevelKey;
|
||||
break;
|
||||
case 'CARD_FLIP': // 👈 추가
|
||||
key = _cardFlipMaxLevelKey;
|
||||
break;
|
||||
default: // 'SUDOKU'
|
||||
key = _sudokuMaxLevelKey;
|
||||
case 'SPIDER': key = _spiderMaxLevelKey; break;
|
||||
case 'MATH_QUIZ': key = _mathQuizMaxLevelKey; break;
|
||||
case 'COLOR_MATCH': key = _colorMatchMaxLevelKey; break;
|
||||
case 'SEQUENCE': key = _sequenceMaxLevelKey; break;
|
||||
case 'CARD_FLIP': key = _cardFlipMaxLevelKey; break;
|
||||
case 'FIND_DIFF': key = _findDiffMaxLevelKey; break; // 👈 추가
|
||||
default: key = _sudokuMaxLevelKey;
|
||||
}
|
||||
String? levelString = await _storage.read(
|
||||
key: key,
|
||||
iOptions: _getIOSOptions(),
|
||||
aOptions: _getAndroidOptions(),
|
||||
);
|
||||
String? levelString = await _storage.read(key: key, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
|
||||
return int.parse(levelString ?? '1');
|
||||
}
|
||||
|
||||
// 8. [수정] 최대 레벨 저장하기 (모든 게임 타입 지원)
|
||||
// 8. [수정] 최대 레벨 저장하기
|
||||
Future<void> saveMaxUnlockedLevel(int level, {String gameType = 'SUDOKU'}) async {
|
||||
String key;
|
||||
switch (gameType) {
|
||||
case 'SPIDER':
|
||||
key = _spiderMaxLevelKey;
|
||||
break;
|
||||
case 'MATH_QUIZ':
|
||||
key = _mathQuizMaxLevelKey;
|
||||
break;
|
||||
case 'COLOR_MATCH': // 👈 추가
|
||||
key = _colorMatchMaxLevelKey;
|
||||
break;
|
||||
case 'SEQUENCE': // 👈 추가
|
||||
key = _sequenceMaxLevelKey;
|
||||
break;
|
||||
case 'CARD_FLIP': // 👈 추가
|
||||
key = _cardFlipMaxLevelKey;
|
||||
break;
|
||||
default: // 'SUDOKU'
|
||||
key = _sudokuMaxLevelKey;
|
||||
case 'SPIDER': key = _spiderMaxLevelKey; break;
|
||||
case 'MATH_QUIZ': key = _mathQuizMaxLevelKey; break;
|
||||
case 'COLOR_MATCH': key = _colorMatchMaxLevelKey; break;
|
||||
case 'SEQUENCE': key = _sequenceMaxLevelKey; break;
|
||||
case 'CARD_FLIP': key = _cardFlipMaxLevelKey; break;
|
||||
case 'FIND_DIFF': key = _findDiffMaxLevelKey; break; // 👈 추가
|
||||
default: key = _sudokuMaxLevelKey;
|
||||
}
|
||||
await _storage.write(
|
||||
key: key,
|
||||
value: level.toString(),
|
||||
iOptions: _getIOSOptions(),
|
||||
aOptions: _getAndroidOptions(),
|
||||
);
|
||||
await _storage.write(key: key, value: level.toString(), iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
|
||||
}
|
||||
|
||||
// 9. [수정] 마지막 랭킹 맵 가져오기 (모든 게임 타입 지원)
|
||||
// 9. [수정] 마지막 랭킹 맵 가져오기
|
||||
Future<Map<int, int>> getLastSavedRankMap({String gameType = 'SUDOKU'}) async {
|
||||
String key;
|
||||
switch (gameType) {
|
||||
case 'SPIDER':
|
||||
key = _spiderRankMapKey;
|
||||
break;
|
||||
case 'MATH_QUIZ':
|
||||
key = _mathQuizRankMapKey;
|
||||
break;
|
||||
case 'COLOR_MATCH': // 👈 추가
|
||||
key = _colorMatchRankMapKey;
|
||||
break;
|
||||
case 'SEQUENCE': // 👈 추가
|
||||
key = _sequenceRankMapKey;
|
||||
break;
|
||||
case 'CARD_FLIP': // 👈 추가
|
||||
key = _cardFlipRankMapKey;
|
||||
break;
|
||||
default: // 'SUDOKU'
|
||||
key = _sudokuRankMapKey;
|
||||
case 'SPIDER': key = _spiderRankMapKey; break;
|
||||
case 'MATH_QUIZ': key = _mathQuizRankMapKey; break;
|
||||
case 'COLOR_MATCH': key = _colorMatchRankMapKey; break;
|
||||
case 'SEQUENCE': key = _sequenceRankMapKey; break;
|
||||
case 'CARD_FLIP': key = _cardFlipRankMapKey; break;
|
||||
case 'FIND_DIFF': key = _findDiffRankMapKey; break; // 👈 추가
|
||||
default: key = _sudokuRankMapKey;
|
||||
}
|
||||
String? jsonString = await _storage.read(
|
||||
key: key,
|
||||
iOptions: _getIOSOptions(),
|
||||
aOptions: _getAndroidOptions(),
|
||||
);
|
||||
String? jsonString = await _storage.read(key: key, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
|
||||
|
||||
if (jsonString == null) return {};
|
||||
try {
|
||||
@@ -246,38 +143,21 @@ class IdentityService {
|
||||
}
|
||||
}
|
||||
|
||||
// 10. [수정] 마지막 랭킹 맵 저장하기 (모든 게임 타입 지원)
|
||||
// 10. [수정] 마지막 랭킹 맵 저장하기
|
||||
Future<void> saveLastRankMap(Map<int, int> rankMap, {String gameType = 'SUDOKU'}) async {
|
||||
String key;
|
||||
switch (gameType) {
|
||||
case 'SPIDER':
|
||||
key = _spiderRankMapKey;
|
||||
break;
|
||||
case 'MATH_QUIZ':
|
||||
key = _mathQuizRankMapKey;
|
||||
break;
|
||||
case 'COLOR_MATCH': // 👈 추가
|
||||
key = _colorMatchRankMapKey;
|
||||
break;
|
||||
case 'SEQUENCE': // 👈 추가
|
||||
key = _sequenceRankMapKey;
|
||||
break;
|
||||
case 'CARD_FLIP': // 👈 추가
|
||||
key = _cardFlipRankMapKey;
|
||||
break;
|
||||
default: // 'SUDOKU'
|
||||
key = _sudokuRankMapKey;
|
||||
case 'SPIDER': key = _spiderRankMapKey; break;
|
||||
case 'MATH_QUIZ': key = _mathQuizRankMapKey; break;
|
||||
case 'COLOR_MATCH': key = _colorMatchRankMapKey; break;
|
||||
case 'SEQUENCE': key = _sequenceRankMapKey; break;
|
||||
case 'CARD_FLIP': key = _cardFlipRankMapKey; break;
|
||||
case 'FIND_DIFF': key = _findDiffRankMapKey; break; // 👈 추가
|
||||
default: key = _sudokuRankMapKey;
|
||||
}
|
||||
|
||||
final Map<String, int> stringKeyMap =
|
||||
rankMap.map((key, value) => MapEntry(key.toString(), value));
|
||||
|
||||
final Map<String, int> stringKeyMap = rankMap.map((key, value) => MapEntry(key.toString(), value));
|
||||
String jsonString = jsonEncode(stringKeyMap);
|
||||
await _storage.write(
|
||||
key: key,
|
||||
value: jsonString,
|
||||
iOptions: _getIOSOptions(),
|
||||
aOptions: _getAndroidOptions(),
|
||||
);
|
||||
await _storage.write(key: key, value: jsonString, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user