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? email; UserSession({ required this.userId, this.userName, this.loginProvider = "guest", this.email, }); 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'; final _storage = const FlutterSecureStorage(); IOSOptions _getIOSOptions() => const IOSOptions( // ๐Ÿ”ฝ [์ˆ˜์ •] Xcode ์„ค์ • ์ „๊นŒ์ง€ 'groupId'๋ฅผ ์ฃผ์„ ์ฒ˜๋ฆฌํ•˜์—ฌ ํฌ๋ž˜์‹œ ๋ฐฉ์ง€ // groupId: 'group.com.lunaticbum.mygamecenter', ); AndroidOptions _getAndroidOptions() => const AndroidOptions( encryptedSharedPreferences: true, ); // 1. ํ˜„์žฌ ์„ธ์…˜ ์ •๋ณด๋ฅผ '๊ฐ์ฒด'๋กœ ๊ฐ€์ ธ์˜ค๊ธฐ Future 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, ); } // 2. ์•ฑ-๊ณ ์œ  ID ๊ฐ€์ ธ์˜ค๊ธฐ (์—†์œผ๋ฉด ์ƒ์„ฑ) Future getOrCreateUserId() async { 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(), ); } return userId; } // 3. ๋žญํ‚น์— ๋“ฑ๋กํ•œ ์‚ฌ์šฉ์ž ์ด๋ฆ„ ๊ฐ€์ ธ์˜ค๊ธฐ Future getSavedUserName() async { return await _storage.read( key: _userNameKey, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions(), ); } // 4. ๋žญํ‚น ๋“ฑ๋ก ์„ฑ๊ณต ์‹œ, ์‚ฌ์šฉ์ž ์ด๋ฆ„ ์ €์žฅํ•˜๊ธฐ Future saveUserName(String name) async { await _storage.write( key: _userNameKey, value: name, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions(), ); } // 5. ์†Œ์…œ ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์‹œ ํ˜ธ์ถœ (๊ณ„์ • ์—ฐ๊ฒฐ) Future 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, ); } // 6. ๋กœ๊ทธ์•„์›ƒ (๊ฒŒ์ŠคํŠธ ๊ณ„์ •์œผ๋กœ ๋˜๋Œ๋ฆฌ๊ธฐ) Future 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. ์ตœ๋Œ€ ๋ ˆ๋ฒจ ๊ฐ€์ ธ์˜ค๊ธฐ (gameType ๋ถ„๊ธฐ) Future getMaxUnlockedLevel({String gameType = 'SUDOKU'}) async { String key; switch (gameType) { case 'SPIDER': key = _spiderMaxLevelKey; break; case 'MATH_QUIZ': // ๐Ÿ‘ˆ [์ถ”๊ฐ€] key = _mathQuizMaxLevelKey; break; default: // 'SUDOKU' key = _sudokuMaxLevelKey; } String? levelString = await _storage.read( key: key, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions(), ); return int.parse(levelString ?? '1'); } // ๐Ÿ”ฝ [์ˆ˜์ •] 8. ์ตœ๋Œ€ ๋ ˆ๋ฒจ ์ €์žฅํ•˜๊ธฐ (gameType ๋ถ„๊ธฐ) Future saveMaxUnlockedLevel(int level, {String gameType = 'SUDOKU'}) async { String key; switch (gameType) { case 'SPIDER': key = _spiderMaxLevelKey; break; case 'MATH_QUIZ': // ๐Ÿ‘ˆ [์ถ”๊ฐ€] key = _mathQuizMaxLevelKey; break; default: // 'SUDOKU' key = _sudokuMaxLevelKey; } await _storage.write( key: key, value: level.toString(), iOptions: _getIOSOptions(), aOptions: _getAndroidOptions(), ); } // ๐Ÿ”ฝ [์ˆ˜์ •] 9. ๋งˆ์ง€๋ง‰ ๋žญํ‚น ๋งต ๊ฐ€์ ธ์˜ค๊ธฐ (gameType ๋ถ„๊ธฐ) Future> getLastSavedRankMap({String gameType = 'SUDOKU'}) async { String key; switch (gameType) { case 'SPIDER': key = _spiderRankMapKey; break; case 'MATH_QUIZ': // ๐Ÿ‘ˆ [์ถ”๊ฐ€] key = _mathQuizRankMapKey; break; default: // 'SUDOKU' key = _sudokuRankMapKey; } String? jsonString = await _storage.read( key: key, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions(), ); if (jsonString == null) return {}; try { final Map decodedMap = jsonDecode(jsonString); return decodedMap.map((key, value) => MapEntry(int.parse(key), value as int)); } catch (e) { return {}; } } // ๐Ÿ”ฝ [์ˆ˜์ •] 10. ๋งˆ์ง€๋ง‰ ๋žญํ‚น ๋งต ์ €์žฅํ•˜๊ธฐ (gameType ๋ถ„๊ธฐ) Future saveLastRankMap(Map rankMap, {String gameType = 'SUDOKU'}) async { String key; switch (gameType) { case 'SPIDER': key = _spiderRankMapKey; break; case 'MATH_QUIZ': // ๐Ÿ‘ˆ [์ถ”๊ฐ€] key = _mathQuizRankMapKey; break; default: // 'SUDOKU' key = _sudokuRankMapKey; } final Map stringKeyMap = rankMap.map((key, value) => MapEntry(key.toString(), value)); String jsonString = jsonEncode(stringKeyMap); await _storage.write( key: key, value: jsonString, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions(), ); } }