From 3cca3401ac988e50a5cb4c1b4947b9356fae5e60 Mon Sep 17 00:00:00 2001 From: eld_master Date: Tue, 14 Jan 2025 19:32:42 +0900 Subject: [PATCH] =?UTF-8?q?=ED=8C=80=EC=A0=84=20=EB=8C=80=EA=B8=B0?= =?UTF-8?q?=EB=B0=A9=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- firebase.json | 2 +- lib/firebase_options.dart | 75 +++--- lib/main.dart | 4 +- lib/views/login/login_page.dart | 6 +- lib/views/room/create_room_page.dart | 7 +- lib/views/room/main_page.dart | 38 +-- lib/views/room/waiting_room_team_page.dart | 281 +++++++++++++++------ 7 files changed, 263 insertions(+), 150 deletions(-) diff --git a/firebase.json b/firebase.json index b1975b4..6153298 100644 --- a/firebase.json +++ b/firebase.json @@ -1 +1 @@ -{"flutter":{"platforms":{"android":{"default":{"projectId":"allscore-344c2","appId":"1:70449524223:android:94ffb9ec98e508313e4bca","fileOutput":"android/app/google-services.json"}},"dart":{"lib/firebase_options.dart":{"projectId":"allscore-344c2","configurations":{"android":"1:70449524223:android:94ffb9ec98e508313e4bca","ios":"1:70449524223:ios:98ebdbaa616a807f3e4bca","macos":"1:70449524223:ios:98ebdbaa616a807f3e4bca","web":"1:70449524223:web:e9c27da6646d655f3e4bca","windows":"1:70449524223:web:479dd789b837f54c3e4bca"}}}}}} \ No newline at end of file +{"flutter":{"platforms":{"android":{"default":{"projectId":"allscore-29edf","appId":"1:452355332155:android:152995468604d10d13e41e","fileOutput":"android/app/google-services.json"}},"dart":{"lib/firebase_options.dart":{"projectId":"allscore-29edf","configurations":{"android":"1:452355332155:android:152995468604d10d13e41e","ios":"1:452355332155:ios:a7b2fc75e3513e3413e41e","macos":"1:452355332155:ios:a7b2fc75e3513e3413e41e","web":"1:452355332155:web:5407f79d500818e713e41e","windows":"1:452355332155:web:5e08c9caf8d07e2c13e41e"}}}}}} \ No newline at end of file diff --git a/lib/firebase_options.dart b/lib/firebase_options.dart index 5f25ea8..4a78e68 100644 --- a/lib/firebase_options.dart +++ b/lib/firebase_options.dart @@ -41,54 +41,57 @@ class DefaultFirebaseOptions { } static const FirebaseOptions web = FirebaseOptions( - apiKey: 'AIzaSyCnZuHtj5oUe_YS9nv3nlQIKWYCCfYFysU', - appId: '1:70449524223:web:e9c27da6646d655f3e4bca', - messagingSenderId: '70449524223', - projectId: 'allscore-344c2', - authDomain: 'allscore-344c2.firebaseapp.com', - databaseURL: 'https://allscore-344c2-default-rtdb.asia-southeast1.firebasedatabase.app', - storageBucket: 'allscore-344c2.firebasestorage.app', - measurementId: 'G-50Q1W265RY', + apiKey: 'AIzaSyCZx2yNaMmMXSFcdNGpl29NerG2PweszWI', + appId: '1:452355332155:web:5407f79d500818e713e41e', + messagingSenderId: '452355332155', + projectId: 'allscore-29edf', + authDomain: 'allscore-29edf.firebaseapp.com', + databaseURL: 'https://allscore-29edf-default-rtdb.asia-southeast1.firebasedatabase.app', + storageBucket: 'allscore-29edf.firebasestorage.app', + measurementId: 'G-63H3D8MNM8', ); static const FirebaseOptions android = FirebaseOptions( - apiKey: 'AIzaSyAJEItMxO-TemHGlveSKySG-eNaTD9XJI0', - appId: '1:70449524223:android:94ffb9ec98e508313e4bca', - messagingSenderId: '70449524223', - projectId: 'allscore-344c2', - databaseURL: 'https://allscore-344c2-default-rtdb.asia-southeast1.firebasedatabase.app', - storageBucket: 'allscore-344c2.firebasestorage.app', + apiKey: 'AIzaSyB6hil7Nrk8wslHDfRNRRyw6rQktY16tTc', + appId: '1:452355332155:android:152995468604d10d13e41e', + messagingSenderId: '452355332155', + projectId: 'allscore-29edf', + databaseURL: 'https://allscore-29edf-default-rtdb.asia-southeast1.firebasedatabase.app', + storageBucket: 'allscore-29edf.firebasestorage.app', ); static const FirebaseOptions ios = FirebaseOptions( - apiKey: 'AIzaSyDq2y-BRlthl6BHs4B7FByiUnpyOfPPZQk', - appId: '1:70449524223:ios:98ebdbaa616a807f3e4bca', - messagingSenderId: '70449524223', - projectId: 'allscore-344c2', - databaseURL: 'https://allscore-344c2-default-rtdb.asia-southeast1.firebasedatabase.app', - storageBucket: 'allscore-344c2.firebasestorage.app', + apiKey: 'AIzaSyBL1ceaAYfhTpRU2C__HwKkF7cJHhxlpPg', + appId: '1:452355332155:ios:a7b2fc75e3513e3413e41e', + messagingSenderId: '452355332155', + projectId: 'allscore-29edf', + databaseURL: 'https://allscore-29edf-default-rtdb.asia-southeast1.firebasedatabase.app', + storageBucket: 'allscore-29edf.firebasestorage.app', + androidClientId: '452355332155-t29ceato8o62c9kq9drefe7b6hd1ka1d.apps.googleusercontent.com', + iosClientId: '452355332155-fo49j1u3qfup1sa3gj33bko6q269pqo4.apps.googleusercontent.com', iosBundleId: 'com.example.allscoreApp', ); static const FirebaseOptions macos = FirebaseOptions( - apiKey: 'AIzaSyDq2y-BRlthl6BHs4B7FByiUnpyOfPPZQk', - appId: '1:70449524223:ios:98ebdbaa616a807f3e4bca', - messagingSenderId: '70449524223', - projectId: 'allscore-344c2', - databaseURL: 'https://allscore-344c2-default-rtdb.asia-southeast1.firebasedatabase.app', - storageBucket: 'allscore-344c2.firebasestorage.app', + apiKey: 'AIzaSyBL1ceaAYfhTpRU2C__HwKkF7cJHhxlpPg', + appId: '1:452355332155:ios:a7b2fc75e3513e3413e41e', + messagingSenderId: '452355332155', + projectId: 'allscore-29edf', + databaseURL: 'https://allscore-29edf-default-rtdb.asia-southeast1.firebasedatabase.app', + storageBucket: 'allscore-29edf.firebasestorage.app', + androidClientId: '452355332155-t29ceato8o62c9kq9drefe7b6hd1ka1d.apps.googleusercontent.com', + iosClientId: '452355332155-fo49j1u3qfup1sa3gj33bko6q269pqo4.apps.googleusercontent.com', iosBundleId: 'com.example.allscoreApp', ); static const FirebaseOptions windows = FirebaseOptions( - apiKey: 'AIzaSyCnZuHtj5oUe_YS9nv3nlQIKWYCCfYFysU', - appId: '1:70449524223:web:479dd789b837f54c3e4bca', - messagingSenderId: '70449524223', - projectId: 'allscore-344c2', - authDomain: 'allscore-344c2.firebaseapp.com', - databaseURL: 'https://allscore-344c2-default-rtdb.asia-southeast1.firebasedatabase.app', - storageBucket: 'allscore-344c2.firebasestorage.app', - measurementId: 'G-S9J5WDYJZM', + apiKey: 'AIzaSyCZx2yNaMmMXSFcdNGpl29NerG2PweszWI', + appId: '1:452355332155:web:5e08c9caf8d07e2c13e41e', + messagingSenderId: '452355332155', + projectId: 'allscore-29edf', + authDomain: 'allscore-29edf.firebaseapp.com', + databaseURL: 'https://allscore-29edf-default-rtdb.asia-southeast1.firebasedatabase.app', + storageBucket: 'allscore-29edf.firebasestorage.app', + measurementId: 'G-MWXRMG174T', ); - -} \ No newline at end of file +} diff --git a/lib/main.dart b/lib/main.dart index c60ad03..0ea362d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -18,7 +18,9 @@ void main() async { WidgetsFlutterBinding.ensureInitialized(); // 파이어베이스 초기화 - await Firebase.initializeApp(); + await Firebase.initializeApp( + options: DefaultFirebaseOptions.currentPlatform, + ); // 모바일 광고 초기화 MobileAds.instance.initialize(); diff --git a/lib/views/login/login_page.dart b/lib/views/login/login_page.dart index e44ef13..cb02c51 100644 --- a/lib/views/login/login_page.dart +++ b/lib/views/login/login_page.dart @@ -104,10 +104,8 @@ class _LoginPageState extends State { listener: BannerAdListener( onAdLoaded: (ad) { setState(() => _isBannerReady = true); - print('로그인페이지 배너 광고 로드 완료'); }, onAdFailedToLoad: (ad, error) { - print('로그인페이지 배너 광고 로드 실패: $error'); ad.dispose(); }, ), @@ -155,7 +153,7 @@ class _LoginPageState extends State { await prefs.setString('oauth_type', 'idpw'); await prefs.setBool('auto_login', true); await prefs.setString('jwt_token', resp['auth']['token'].toString()); - await prefs.setString('user_seq', resp['auth']['user_seq'].toString()); + await prefs.setInt('my_user_seq', resp['auth']['user_seq']); // 메인 페이지 이동 if (!mounted) return; @@ -225,7 +223,7 @@ class _LoginPageState extends State { await prefs.setString('oauth_type', 'google'); await prefs.setBool('auto_login', true); await prefs.setString('jwt_token', resp['auth']['token'].toString()); - await prefs.setString('user_seq', resp['auth']['user_seq'].toString()); + await prefs.setInt('my_user_seq', resp['auth']['user_seq']); Navigator.pushReplacement( context, MaterialPageRoute(builder: (_) => const MainPage()), diff --git a/lib/views/room/create_room_page.dart b/lib/views/room/create_room_page.dart index 646ed1b..e554169 100644 --- a/lib/views/room/create_room_page.dart +++ b/lib/views/room/create_room_page.dart @@ -348,7 +348,7 @@ class _CreateRoomPageState extends State { // 방 생성 성공 → 대기 방으로 이동 if (_isTeamGame) { - Navigator.pushReplacement( + Navigator.pushAndRemoveUntil( context, MaterialPageRoute( builder: (_) => WaitingRoomTeamPage( @@ -356,9 +356,10 @@ class _CreateRoomPageState extends State { roomType: 'team', ), ), + (route) => false, ); } else { - Navigator.pushReplacement( + Navigator.pushAndRemoveUntil( context, MaterialPageRoute( builder: (_) => WaitingRoomPrivatePage( @@ -366,6 +367,7 @@ class _CreateRoomPageState extends State { roomType: 'private', ), ), + (route) => false, ); } } else { @@ -437,7 +439,6 @@ class _CreateRoomPageState extends State { return {'result': 'FAIL'}; } } catch (e) { - print('serverResponse 오류: $e'); return {'result': 'FAIL'}; } } diff --git a/lib/views/room/main_page.dart b/lib/views/room/main_page.dart index 19864c2..96885ba 100644 --- a/lib/views/room/main_page.dart +++ b/lib/views/room/main_page.dart @@ -19,6 +19,9 @@ import '../../dialogs/response_dialog.dart'; // 뒤로가기 import 'package:fluttertoast/fluttertoast.dart'; // 뒤로가기 안내 문구에 Toast 등 사용 +// 설정 +import '../../config/config.dart'; + class MainPage extends StatefulWidget { const MainPage({Key? key}) : super(key: key); @@ -30,6 +33,7 @@ class _MainPageState extends State { /// (1) 광고 배너 관련 변수 BannerAd? _bannerAd; bool _isBannerReady = false; // 광고 로드 완료 여부 + String adUnitId = Config.testAdUnitId; // 뒤로가기 처리 DateTime? _lastPressedTime; @@ -53,12 +57,18 @@ class _MainPageState extends State { _initBannerAd(); } + @override + void dispose() { + _bannerAd?.dispose(); + super.dispose(); + } + /// 배너 광고 초기화 void _initBannerAd() { _bannerAd = BannerAd( size: AdSize.banner, // 일반 배너 사이즈 // adUnitId: 'ca-app-pub-3940256099942544/6300978111' (테스트용) - adUnitId: 'ca-app-pub-3940256099942544/6300978111', // 예시: 구글 테스트 배너ID + adUnitId: adUnitId, // 실제/테스트 배너 광고 단위 ID listener: BannerAdListener( onAdLoaded: (Ad ad) { setState(() => _isBannerReady = true); @@ -76,12 +86,6 @@ class _MainPageState extends State { _bannerAd?.load(); } - @override - void dispose() { - _bannerAd?.dispose(); // ★ 광고 자원 해제 - super.dispose(); - } - Future _onWillPop() async { final now = DateTime.now(); if (_lastPressedTime == null || @@ -243,26 +247,6 @@ class _MainPageState extends State { style: TextStyle(color: Colors.black), ), ), - - // 디버그용 임시버튼 - Center( - child: OutlinedButton( - onPressed: () { - // TODO - }, - style: OutlinedButton.styleFrom( - backgroundColor: Colors.white, - side: const BorderSide(color: Colors.black54, width: 1), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(40)), - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 60), - ), - child: const Text( - '방 생성 완료 이동(임시)', - style: TextStyle(color: Colors.black, fontSize: 16), - ), - ), - ), - const SizedBox(height: 16), ], ), ), diff --git a/lib/views/room/waiting_room_team_page.dart b/lib/views/room/waiting_room_team_page.dart index 06a0ca5..dfffed8 100644 --- a/lib/views/room/waiting_room_team_page.dart +++ b/lib/views/room/waiting_room_team_page.dart @@ -3,8 +3,13 @@ import 'dart:async'; import 'package:firebase_database/firebase_database.dart'; import 'package:shared_preferences/shared_preferences.dart'; +// 메인 페이지 import 'main_page.dart'; + +// API import '../../plugins/api.dart'; + +// 대화 창 import '../../dialogs/response_dialog.dart'; import '../../dialogs/yes_no_dialog.dart'; import '../../dialogs/room_setting_dialog.dart'; @@ -12,6 +17,12 @@ import '../../dialogs/user_info_team_dialog.dart'; import '../../dialogs/team_name_edit_dialog.dart'; import 'playing_team_page.dart'; +// 광고 +import 'package:google_mobile_ads/google_mobile_ads.dart'; + +// 설정 +import '../../config/config.dart'; + class WaitingRoomTeamPage extends StatefulWidget { final int roomSeq; final String roomType; // "team" @@ -43,12 +54,17 @@ class _WaitingRoomTeamPageState extends State { bool _isLoading = true; + // 서버 요청 로딩중 + bool _isServerRequestLoading = false; + late DatabaseReference _roomRef; Stream? _roomStream; StreamSubscription? _roomStreamSubscription; bool _movedToRunningPage = false; bool _kickedOut = false; + bool _roomTimeOut = false; + String _roomTimeOutMsg = ''; String mySeq = '0'; @@ -57,14 +73,25 @@ class _WaitingRoomTeamPageState extends State { Duration _remaining = const Duration(hours: 1); DateTime? _createDt; + /// (1) 광고 배너 관련 변수 + BannerAd? _bannerAd; + bool _isBannerReady = false; // 광고 로드 완료 여부 + String adUnitId = Config.testAdUnitId; + + // 방장 SEQ 저장 + String _masterSeqString = ''; + @override void initState() { super.initState(); FirebaseDatabase.instance.goOnline(); + // (C) 배너 광고 초기화 + _initBannerAd(); + // (D) 방 정보 초기화 _initRoomRef(); } - Future _initRoomRef() async { + Future _initRoomRef() async { final prefs = await SharedPreferences.getInstance(); mySeq = prefs.getInt('my_user_seq')?.toString() ?? '0'; @@ -86,6 +113,29 @@ class _WaitingRoomTeamPageState extends State { super.dispose(); } + /// 배너 광고 초기화 + void _initBannerAd() { + _bannerAd = BannerAd( + size: AdSize.banner, // 일반 배너 사이즈 + // adUnitId: 'ca-app-pub-3940256099942544/6300978111' (테스트용) + adUnitId: adUnitId, // 실제/테스트 배너 광고 단위 ID + listener: BannerAdListener( + onAdLoaded: (Ad ad) { + setState(() => _isBannerReady = true); + debugPrint('배너 광고 로드 완료'); + }, + onAdFailedToLoad: (Ad ad, LoadAdError err) { + debugPrint('배너 광고 로드 실패: $err'); + ad.dispose(); + }, + ), + request: const AdRequest(), + ); + + // load() 호출로 광고 요청 + _bannerAd?.load(); + } + void _listenRoomData() { _roomStream = _roomRef.onValue; _roomStreamSubscription = _roomStream?.listen((event) { @@ -101,7 +151,28 @@ class _WaitingRoomTeamPageState extends State { final data = snapshot.value as Map? ?? {}; final roomInfoData = data['roomInfo'] as Map? ?? {}; - final userInfoData = data['userInfo'] as Map? ?? {}; + // final userInfoData = data['userInfo'] as Map? ?? {}; + final userInfoDynamic = data['userInfo']; // 이건 List일 수 있음 + + final tempList = >[]; + // userList + if (userInfoDynamic is Map) { + userInfoDynamic.forEach((key, val) { + if (val is Map) { + tempList.add({ + 'user_seq': val['user_seq'].toString() ?? '0', + 'participant_type': val['participant_type'] ?? '', + 'nickname': val['nickname'] ?? '유저', + 'team_name': val['team_name'] ?? '', + 'score': val['score'] ?? 0, + 'profile_img': val['profile_img'] ?? '', + 'introduce_myself': val['introduce_myself'] ?? '', + 'ready_yn': (val['ready_yn'] ?? 'N').toString().toUpperCase(), + 'connect_yn': (val['connect_yn'] ?? 'Y').toString().toUpperCase(), + }); + } + }); + } final roomStatus = (roomInfoData['room_status'] ?? 'WAIT').toString().toUpperCase(); @@ -129,22 +200,13 @@ class _WaitingRoomTeamPageState extends State { if (masterSeq != null && masterSeq.toString() == mySeq) { roomMasterYn = 'Y'; } + + if (masterSeq != null) { + _masterSeqString = masterSeq.toString(); + } else { + _masterSeqString = ''; + } - // userList - final tempList = >[]; - userInfoData.forEach((userSeq, userMap) { - tempList.add({ - 'user_seq': userSeq, - 'participant_type': userMap['participant_type'] ?? '', - 'nickname': userMap['nickname'] ?? '유저', - 'team_name': userMap['team_name'] ?? '', - 'score': userMap['score'] ?? 0, - 'profile_img': userMap['profile_img'] ?? '', - 'introduce_myself': userMap['introduce_myself'] ?? '', - 'ready_yn': (userMap['ready_yn'] ?? 'N').toString().toUpperCase(), - 'connect_yn': (userMap['connect_yn'] ?? 'Y').toString().toUpperCase(), - }); - }); _userList = tempList; _isLoading = false; }); @@ -152,7 +214,7 @@ class _WaitingRoomTeamPageState extends State { // 진행중 -> 이동 if (roomStatus == 'RUNNING' && !_movedToRunningPage) { _movedToRunningPage = true; - Navigator.pushReplacement( + Navigator.pushAndRemoveUntil( context, MaterialPageRoute( builder: (_) => PlayingTeamPage( @@ -160,16 +222,28 @@ class _WaitingRoomTeamPageState extends State { roomTitle: roomTitle, ), ), + (route) => false, ); return; } // (C) create_dt 파싱 -> 1시간 카운트다운 final createDtStr = (roomInfoData['create_dt'] ?? '') as String; - if (createDtStr.isNotEmpty && createDtStr.contains('T')) { - final dt = DateTime.tryParse(createDtStr); + if (createDtStr.isNotEmpty) { + final dotIndex = createDtStr.indexOf('.'); + final isoStr = createDtStr.substring(0, dotIndex); + print('isoStr: $isoStr'); + + final dt1 = DateTime.tryParse(createDtStr); + print('dt1: $dt1'); + final dt = DateTime.tryParse(isoStr); + print('dt: $dt'); + final dt2 = DateTime.parse(isoStr); + print('dt2: $dt2'); if (dt != null) { - _createDt = dt; + setState(() { + _createDt = dt; + }); _startCountdownTimer(); } } @@ -178,11 +252,17 @@ class _WaitingRoomTeamPageState extends State { final amIStillHere = _userList.any((u) => u['user_seq'].toString() == mySeq); if (!amIStillHere && !_kickedOut && roomMasterYn != 'Y') { _kickedOut = true; + if (_roomTimeOut) { + _roomTimeOutMsg = '방장이 나갔습니다.'; + } else if (_kickedOut) { + _roomTimeOutMsg = '강퇴되었습니다.'; + } Future.delayed(Duration.zero, () async { - await showResponseDialog(context, '안내', '강퇴되었습니다.'); - Navigator.pushReplacement( + await showResponseDialog(context, '안내', _roomTimeOutMsg); + Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (_) => const MainPage()), + (route) => false, ); }); } @@ -202,7 +282,8 @@ class _WaitingRoomTeamPageState extends State { if (_createDt == null) return; _countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) { - final endTime = _createDt!.add(const Duration(hours: 1)); + // final endTime = _createDt!.add(const Duration(hours: 1)); + final endTime = _createDt!.add(const Duration(minutes: 1)); final now = DateTime.now(); final diff = endTime.difference(now); @@ -220,6 +301,9 @@ class _WaitingRoomTeamPageState extends State { void _onAutoTimeout() { // 자동 종료 -> 방장=나가기(방삭제), 일반=나가기 + setState(() { + _roomTimeOut = true; + }); _requestLeaveRoom(); } @@ -232,7 +316,11 @@ class _WaitingRoomTeamPageState extends State { // ... } if (mounted) { - Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => const MainPage())); + Navigator.pushAndRemoveUntil( + context, + MaterialPageRoute(builder: (_) => const MainPage()), + (route) => false, + ); } } @@ -302,7 +390,7 @@ class _WaitingRoomTeamPageState extends State { Widget _buildTopButtons() { if (_isLoading) return const SizedBox(); - final me = _userList.firstWhere((u) => (u['user_seq'] ?? '0') == mySeq, orElse: () => {}); + final me = _userList.firstWhere((u) => (u['user_seq'].toString() ?? '0') == mySeq, orElse: () => {}); final myReadyYn = (me['ready_yn'] ?? 'N').toString().toUpperCase(); final bool isReady = (myReadyYn == 'Y'); final readyLabel = isReady ? '준비완료' : '준비'; @@ -341,8 +429,10 @@ class _WaitingRoomTeamPageState extends State { margin: const EdgeInsets.symmetric(horizontal: 4), child: ElevatedButton( style: btnStyle, - onPressed: _onGameStart, - child: const Text('게임 시작'), + onPressed: _isServerRequestLoading ? null : _onGameStart, + child: _isServerRequestLoading + ? const CircularProgressIndicator() + : const Text('게임 시작'), ), ), ), @@ -378,7 +468,7 @@ class _WaitingRoomTeamPageState extends State { Future _onToggleReady() async { try { - final me = _userList.firstWhere((u) => (u['user_seq'] ?? '') == mySeq, orElse: () => {}); + final me = _userList.firstWhere((u) => (u['user_seq'].toString() ?? '') == mySeq, orElse: () => {}); final myReadyYn = (me['ready_yn'] ?? 'N').toString().toUpperCase(); final isReady = (myReadyYn == 'Y'); final newYn = isReady ? 'N' : 'Y'; @@ -386,7 +476,7 @@ class _WaitingRoomTeamPageState extends State { final userRef = _roomRef.child('userInfo').child(mySeq); await userRef.update({"ready_yn": newYn}); } catch (e) { - print('READY 설정 실패: $e'); + showResponseDialog(context, '오류', 'READY 설정에 실패했습니다.'); } } @@ -417,9 +507,11 @@ class _WaitingRoomTeamPageState extends State { } Future _onGameStart() async { + setState(() => _isServerRequestLoading = true); final notReady = _userList.any((u) => (u['ready_yn'] ?? 'N').toString().toUpperCase() != 'Y'); if (notReady) { - showResponseDialog(context, '안내', 'READY되지 않은 참가자가 있습니다(방장 포함).'); + showResponseDialog(context, '안내', '아직 준비되지 않은 참가자가 있습니다(방장 포함).'); + setState(() => _isServerRequestLoading = false); return; } @@ -434,13 +526,18 @@ class _WaitingRoomTeamPageState extends State { if (resp['result'] == 'OK') { print('게임 시작 요청 성공(팀전)'); } else { - // ... + // 게임 시작 요청 실패 + showResponseDialog(context, '오류', '게임 시작 요청에 실패했습니다.'); } } else { - // ... + // 게임 시작 요청 실패 + showResponseDialog(context, '오류', '게임 시작 요청에 실패했습니다.'); } } catch (e) { - // ... + // 게임 시작 요청 실패 + showResponseDialog(context, '오류', '게임 시작 요청에 실패했습니다.'); + } finally { + setState(() => _isServerRequestLoading = false); } } @@ -461,25 +558,34 @@ class _WaitingRoomTeamPageState extends State { backgroundColor: Colors.black, elevation: 0, // 방 제목 + 남은시간 - title: Text( - (roomTitle.isNotEmpty ? roomTitle : '방 제목') + ' [$countdownStr]', - style: const TextStyle(color: Colors.white), + title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // 왼쪽: 방 제목 + Text( + roomTitle.isNotEmpty ? roomTitle : '방 제목', + style: const TextStyle(color: Colors.white), + ), + // 오른쪽: 남은시간 + Text( + countdownStr, // 예: "10:23" + style: const TextStyle(color: Colors.white), + ), + ], ), leading: IconButton( icon: const Icon(Icons.arrow_back_ios, color: Colors.white), onPressed: _onLeaveRoom, ), ), - bottomNavigationBar: Container( - height: 50, - decoration: BoxDecoration( - color: Colors.grey.shade300, - border: Border.all(color: Colors.black, width: 1), - ), - child: const Center( - child: Text('구글 광고', style: TextStyle(color: Colors.black)), - ), - ), + bottomNavigationBar: _isBannerReady && _bannerAd != null + ? Container( + color: Colors.white, + width: _bannerAd!.size.width.toDouble(), + height: _bannerAd!.size.height.toDouble(), + child: AdWidget(ad: _bannerAd!), + ) + : SizedBox.shrink(), // 로딩 전엔 빈 공간 or 원하는 위젯 body: _isLoading ? const Center(child: CircularProgressIndicator()) : SingleChildScrollView( @@ -490,10 +596,10 @@ class _WaitingRoomTeamPageState extends State { _buildTopButtons(), const SizedBox(height: 20), - const Text('사회자', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black)), - const SizedBox(height: 8), - _buildAdminSection(), - const SizedBox(height: 20), + // const Text('사회자', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black)), + // const SizedBox(height: 8), + // _buildAdminSection(), + // const SizedBox(height: 20), const Text('팀별 참가자', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black)), const SizedBox(height: 8), @@ -507,34 +613,35 @@ class _WaitingRoomTeamPageState extends State { ); } - Widget _buildAdminSection() { - final adminList = _userList.where((u) { - final pType = (u['participant_type'] ?? '').toString().toUpperCase(); - return pType == 'ADMIN'; - }).toList(); + // Widget _buildAdminSection() { + // final adminList = _userList.where((u) { + // final pType = (u['participant_type'] ?? '').toString().toUpperCase(); + // return pType == 'ADMIN'; + // }).toList(); - return Container( - padding: const EdgeInsets.all(8), - decoration: BoxDecoration( - color: Colors.white, - border: Border.all(color: Colors.black), - borderRadius: BorderRadius.circular(8), - ), - child: adminList.isEmpty - ? const Text('사회자가 없습니다.', style: TextStyle(color: Colors.black)) - : Wrap( - spacing: 16, - runSpacing: 8, - children: adminList.map(_buildSeat).toList(), - ), - ); - } + // return Container( + // padding: const EdgeInsets.all(8), + // decoration: BoxDecoration( + // color: Colors.white, + // border: Border.all(color: Colors.black), + // borderRadius: BorderRadius.circular(8), + // ), + // child: adminList.isEmpty + // ? const Text('사회자가 없습니다.', style: TextStyle(color: Colors.black)) + // : Wrap( + // spacing: 16, + // runSpacing: 8, + // children: adminList.map(_buildSeat).toList(), + // ), + // ); + // } Widget _buildTeamSection() { - final players = _userList.where((u) { - final pType = (u['participant_type'] ?? '').toString().toUpperCase(); - return (pType != 'ADMIN'); - }).toList(); + // final players = _userList.where((u) { + // final pType = (u['participant_type'] ?? '').toString().toUpperCase(); + // return (pType != 'ADMIN'); + // }).toList(); + final players = _userList.toList(); final Map>> teamMap = {}; for (final tName in _teamNameList) { @@ -619,7 +726,7 @@ class _WaitingRoomTeamPageState extends State { Widget _buildWaitSection() { final waitList = _userList.where((u) { final pType = (u['participant_type'] ?? '').toString().toUpperCase(); - if (pType == 'ADMIN') return false; + // if (pType == 'ADMIN') return false; final tName = (u['team_name'] ?? '').toString().toUpperCase(); return (tName.isEmpty || tName == 'WAIT'); }).toList(); @@ -662,6 +769,24 @@ class _WaitingRoomTeamPageState extends State { final bool isDisconnected = (connectYn == 'N'); final bool isMaster = (roomMasterYn == 'Y'); + // user가 방장인지 확인 + final isRoomMasterUser = (user['user_seq']?.toString() ?? '') == _masterSeqString; + // user가 사회자인지 확인 + final participantType = (user['participant_type'] ?? '').toString().toUpperCase(); + final isAdmin = (participantType == 'ADMIN'); + + // 아이콘 붙이기 + String roleIcon = ''; + if (isRoomMasterUser) { + // 방장 + roleIcon = '★ '; + } else if (isAdmin) { + // 사회자 + roleIcon = '☆ '; + } + + final displayName = '$roleIcon$userName'; + return GestureDetector( onTap: () async { final result = await showDialog( @@ -738,7 +863,7 @@ class _WaitingRoomTeamPageState extends State { ), ), const SizedBox(height: 2), - Text(userName, style: const TextStyle(fontSize: 12, color: Colors.black)), + Text(displayName, style: const TextStyle(fontSize: 12, color: Colors.black)), ], ), ),