diff --git a/frontend/lib/http.dart b/frontend/lib/http.dart index 951db16ba5..9dd3dda3d2 100644 --- a/frontend/lib/http.dart +++ b/frontend/lib/http.dart @@ -22,16 +22,11 @@ Future> SearchBook(String SearchString) async { }); final data = json.decode(utf8.decode(res.bodyBytes)); while (iter != 100) { - if(data['total'] <= iter){ + if (data['total'] <= iter) { break; - } - else if(data['items'][iter]['title'].toString().contains('시리즈')) { - - } - else if(data['items'][iter]['title'].toString().contains('세트')) { - - } - else{ + } else if (data['items'][iter]['title'].toString().contains('시리즈')) { + } else if (data['items'][iter]['title'].toString().contains('세트')) { + } else { bookData.add(data['items'][iter]); } iter++; @@ -200,15 +195,24 @@ Future groupCreate(dynamic token, String name, String topic, //컨텐츠 생성하기 Future contentCreate( dynamic token, - int bookId, int clubId, + String isbn, + String title2, + String author, + String publisher, + String publishDate, + String imageUrl, String contentType, String title, String body, String startDate, String endDate) async { - var address = - Uri.parse("$BASE_URL/content/create?bookId=$bookId&clubId=$clubId"); + var address; + if (clubId == 0) { + address = Uri.parse("$BASE_URL/content/create?"); + } else { + address = Uri.parse("$BASE_URL/content/create?clubId=$clubId"); + } http.Response res = await http.post( address, headers: { @@ -216,6 +220,14 @@ Future contentCreate( "Authorization": 'Bearer $token', }, body: json.encode({ + "addBookRequest": { + "isbn": "i-$isbn", + "title": title2, + "author": author, + "publisher": publisher, + "publishDate": publishDate, + "imageUrl": imageUrl, + }, "contentType": contentType, "title": title, "body": body, @@ -277,7 +289,8 @@ Future groupOut(String token, int clubId) async { //모임 추방하기 Future groupExpel(String token, String memberId, int clubId) async { - var address = Uri.parse(BASE_URL + "/club/expel?memberId=$memberId&clubId=$clubId"); + var address = + Uri.parse(BASE_URL + "/club/expel?memberId=$memberId&clubId=$clubId"); http.Response res = await http.get( address, headers: { @@ -291,7 +304,8 @@ Future groupExpel(String token, String memberId, int clubId) async { //모임장 임명하기 Future groupDelegate(String token, String memberId, int clubId) async { - var address = Uri.parse(BASE_URL + "/club/delegate?memberId=$memberId&clubId=$clubId"); + var address = + Uri.parse(BASE_URL + "/club/delegate?memberId=$memberId&clubId=$clubId"); http.Response res = await http.put( address, headers: { @@ -304,10 +318,12 @@ Future groupDelegate(String token, String memberId, int clubId) async { } //대표 책 선정 -Future groupBookSelect(dynamic token, Map bookdata, int clubId) async { +Future groupBookSelect( + dynamic token, Map bookdata, int clubId) async { String originalDate = bookdata['pubdate']; DateTime parsedDate = DateTime.parse(originalDate.replaceAllMapped( - RegExp(r'(\d{4})(\d{2})(\d{2})'), (Match m) => '${m[1]}-${m[2]}-${m[3]}')); + RegExp(r'(\d{4})(\d{2})(\d{2})'), + (Match m) => '${m[1]}-${m[2]}-${m[3]}')); String formattedDate = DateFormat('yyyy-MM-dd').format(parsedDate); var address = Uri.parse(BASE_URL + "/club/book?clubId=$clubId"); @@ -338,7 +354,8 @@ Future groupBookSelect(dynamic token, Map bookdata, int Future bookAdd(dynamic token, Map bookdata) async { String originalDate = bookdata['pubdate']; DateTime parsedDate = DateTime.parse(originalDate.replaceAllMapped( - RegExp(r'(\d{4})(\d{2})(\d{2})'), (Match m) => '${m[1]}-${m[2]}-${m[3]}')); + RegExp(r'(\d{4})(\d{2})(\d{2})'), + (Match m) => '${m[1]}-${m[2]}-${m[3]}')); String formattedDate = DateFormat('yyyy-MM-dd').format(parsedDate); // print(bookdata['image']); var address = Uri.parse(BASE_URL + "/book/add"); @@ -358,14 +375,13 @@ Future bookAdd(dynamic token, Map bookdata) async { }), ); - if(res.contentLength == 20){ + if (res.contentLength == 20) { final data = res.body; return data; - } - else{ + } else { final data = json.decode(utf8.decode(res.bodyBytes)); return data['message']; - } + } } //책 기본 정보 불러오기 @@ -402,7 +418,6 @@ Future getPost(String token, int postId, int clubId) async { //댓글 작성 Future commentCreate(dynamic token, int postId, String body) async { - var address = Uri.parse(BASE_URL + "/comment/create?postId=$postId"); http.Response res = await http.post( address, @@ -422,7 +437,7 @@ Future commentCreate(dynamic token, int postId, String body) async { //서재 불러오기 Future> getLibrary(String token) async { List libraryList = []; - var address = Uri.parse("$BASE_URL/library"); + var address = Uri.parse("$BASE_URL/member/my-book"); http.Response res = await http.get( address, headers: { @@ -446,7 +461,7 @@ Future addBookToLibrary( String publisher, String publishDate, String imageUrl) async { - var address = Uri.parse("$BASE_URL/library/add"); + var address = Uri.parse("$BASE_URL/member/my-book/add"); http.Response res = await http.post( address, headers: { @@ -465,3 +480,20 @@ Future addBookToLibrary( final data = res.body; return data; } + +//서재에 책 여러권 추가하기 +Future addBooksToLibrary( + String token, String groupName, List books) async { + var address = Uri.parse("$BASE_URL/member/my-book/adds?groupName=$groupName"); + http.Response res = await http.post( + address, + headers: { + "Content-Type": "application/json", + "Authorization": 'Bearer $token', + }, + body: json.encode(books), + ); + final data = res.body; + print(data); + return data; +} diff --git a/frontend/lib/main.dart b/frontend/lib/main.dart index 9d98653001..682509b63f 100644 --- a/frontend/lib/main.dart +++ b/frontend/lib/main.dart @@ -67,11 +67,13 @@ final GoRouter router = GoRouter( name: 'groupbook_select', path: '/groupbook_select', builder: (context, state) { - final Map data = - state.extra as Map; + final Map data = state.extra as Map; String title = data['title'] as String; int clubId = data['clubId'] as int; - return GroupBookSelectScreen(title: title, clubId: clubId,); + return GroupBookSelectScreen( + title: title, + clubId: clubId, + ); }, ), GoRoute( @@ -126,25 +128,23 @@ final GoRouter router = GoRouter( builder: (context, state) => const HomeworkListScreen(), ), GoRoute( - name: 'notice_list', - path: '/notice_list', - builder: (context, state){ - List posts = state.extra as List; - return NoticeListScreen( - posts: posts, - ); - } - ), + name: 'notice_list', + path: '/notice_list', + builder: (context, state) { + List posts = state.extra as List; + return NoticeListScreen( + posts: posts, + ); + }), GoRoute( - name: 'post_list', - path: '/post_list', - builder: (context, state){ - List posts = state.extra as List; - return PostListScreen( - posts: posts, - ); - } - ), + name: 'post_list', + path: '/post_list', + builder: (context, state) { + List posts = state.extra as List; + return PostListScreen( + posts: posts, + ); + }), GoRoute( name: 'post', path: '/post', diff --git a/frontend/lib/provider/secure_storage_provider.dart b/frontend/lib/provider/secure_storage_provider.dart index 7fe74351b2..62c8dd22d7 100644 --- a/frontend/lib/provider/secure_storage_provider.dart +++ b/frontend/lib/provider/secure_storage_provider.dart @@ -18,6 +18,11 @@ class SecureStorageService extends ChangeNotifier { await _secureStorage.delete(key: key); notifyListeners(); } + + Future deleteAllData() async { + await _secureStorage.deleteAll(); + notifyListeners(); + } } class SecureStorageProvider extends StatelessWidget { diff --git a/frontend/lib/screens/home/bookreport/bookreport_viewing_screen.dart b/frontend/lib/screens/home/bookreport/bookreport_viewing_screen.dart index e3061ecd3c..ff0961b533 100644 --- a/frontend/lib/screens/home/bookreport/bookreport_viewing_screen.dart +++ b/frontend/lib/screens/home/bookreport/bookreport_viewing_screen.dart @@ -11,23 +11,35 @@ class BookReportViewingScreen extends StatefulWidget { } class _BookReportViewingState extends State { + final TextEditingController _answerController = TextEditingController(); List _contentData = []; //DateTime _startDate = DateTime.now(); //DateTime _endDate = DateTime.now(); //bool _isPublic = false; - String _template = ''; + String _template = '퀴즈'; String _title = ''; String _body = ''; final String _author = "작가"; final String _publisher = "출판사"; + String _category = '단답형'; + String _answer = ''; + bool _oxanswer = false; + String _example1 = ''; + String _example2 = ''; + String _example3 = ''; + String _example4 = ''; + bool _answer1 = false; + bool _answer2 = false; + bool _answer3 = false; + bool _answer4 = false; // ignore: prefer_typing_uninitialized_variables var token; void initializeContentData(dynamic token) async { - _contentData = await contentLoad(token, 2); - _template = _contentData[0]['type'] as String; - _title = _contentData[0]['title'] as String; - _body = _contentData[0]['body'] as String; + // _contentData = await contentLoad(token, 2); + // _template = _contentData[0]['type'] as String; + // _title = _contentData[0]['title'] as String; + // _body = _contentData[0]['body'] as String; } @override @@ -147,8 +159,475 @@ class _BookReportViewingState extends State { ], ), ); + case "퀴즈": + return Column( + children: [ + Row( + children: [ + const Text( + '카테고리: ', + style: TextStyle( + color: Colors.black, + fontSize: 15, + fontFamily: 'Noto Sans KR', + fontWeight: FontWeight.w400, + height: 0, + letterSpacing: -0.17, + ), + ), + const SizedBox(width: 3), + SizedBox( + width: 121, + height: 22, + child: DropdownButton( + value: _category, + onChanged: null, + items: ['단답형', '객관식', 'O/X'] + .map>((String value) { + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + style: const TextStyle( + fontSize: 14, + fontFamily: 'Noto Sans KR', + fontWeight: FontWeight.w400, + color: Colors.black, + height: 0, + ), + underline: Container(), + ), + ), + ], + ), + const SizedBox(height: 15), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 0), + child: Expanded( + child: _buildQuizUI(_category), + ), + ), + ], + ); default: throw ArgumentError('Invalid template type'); } } + + Widget _buildQuizUI(String quizType) { + switch (quizType) { + case ("단답형"): + return Column( + children: [ + SizedBox( + width: 350, + height: 190, + child: Stack( + children: [ + Positioned( + left: 0, + top: 0, + child: Container( + width: 350, + height: 190, + decoration: BoxDecoration( + color: const Color(0xFFE7FFEB), + borderRadius: BorderRadius.circular(20), + border: Border.all(width: 1), + ), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Row( + children: [ + const Text('Q: '), + const SizedBox(width: 10), + Expanded( + child: SizedBox( + //width: _screenWidth * 0.7, + child: Text( + _body, + style: const TextStyle(fontSize: 14), + ), + ), + ), + ], + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Row( + children: [ + const Text('A: '), + const SizedBox(width: 10), + Expanded( + child: SizedBox( + //width: _screenWidth * 0.7, + child: TextField( + style: const TextStyle(fontSize: 14), + controller: _answerController, + decoration: const InputDecoration( + border: InputBorder.none, + hintText: '답을 입력하세요', + ), + ), + ), + ), + ], + ), + ), + ], + ); + case ("O/X"): + return Column( + children: [ + SizedBox( + width: 350, + height: 190, + child: Stack( + children: [ + Positioned( + left: 0, + top: 0, + child: Container( + width: 350, + height: 190, + decoration: BoxDecoration( + color: const Color(0xFFE7FFEB), + borderRadius: BorderRadius.circular(20), + border: Border.all(width: 1), + ), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Row( + children: [ + const Text('Q: '), + const SizedBox(width: 10), + Expanded( + child: SizedBox( + //width: _screenWidth * 0.7, + child: Text( + _answer, + style: const TextStyle(fontSize: 14), + ), + ), + ), + ], + ), + ), + ], + ), + ), + const SizedBox(height: 15), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Row( + children: [ + ElevatedButton( + onPressed: () { + setState(() { + _oxanswer = true; + }); + }, + style: ElevatedButton.styleFrom( + foregroundColor: Colors.black, + backgroundColor: _oxanswer ? Colors.green : Colors.white, + elevation: 0, + side: const BorderSide(width: 0.5), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20)), + minimumSize: const Size(140, 140), + ), + child: const Text('O'), + ), + const SizedBox(width: 30), + ElevatedButton( + onPressed: () { + setState(() { + _oxanswer = false; + }); + }, + style: ElevatedButton.styleFrom( + foregroundColor: Colors.black, + backgroundColor: !_oxanswer ? Colors.green : Colors.white, + elevation: 0, + side: const BorderSide(width: 0.5), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20)), + minimumSize: const Size(140, 140), + ), + child: const Text('X'), + ), + ], + ), + ), + ], + ); + case ("객관식"): + return Column( + children: [ + SizedBox( + width: 350, + height: 190, + child: Stack( + children: [ + Positioned( + left: 0, + top: 0, + child: Container( + width: 350, + height: 190, + decoration: BoxDecoration( + color: const Color(0xFFE7FFEB), + borderRadius: BorderRadius.circular(20), + border: Border.all(width: 1), + ), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Row( + children: [ + const Text('Q: '), + const SizedBox(width: 10), + Expanded( + child: SizedBox( + //width: _screenWidth * 0.7, + child: Text( + _body, + style: const TextStyle(fontSize: 14), + ), + ), + ), + ], + ), + ), + ], + ), + ), + const SizedBox(height: 15), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 0), + child: Column( + children: [ + Row( + children: [ + GestureDetector( + onTap: () { + setState(() { + _answer1 = true; + _answer2 = false; + _answer3 = false; + _answer4 = false; + }); + }, + child: Container( + padding: const EdgeInsets.all(0), + child: _answer1 + ? const Icon(Icons.check, color: Colors.green) + : const Icon(Icons.check, color: Colors.grey), + ), + ), + const SizedBox(width: 5), + Expanded( + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: Colors.black, + width: 0.5, + ), + color: _answer1 ? Colors.green : Colors.white, + ), + height: 24, + alignment: Alignment.center, + child: Row( + children: [ + const SizedBox(width: 10), + const Text('A: '), + Expanded( + child: SizedBox( + //width: _screenWidth * 0.7, + child: Text( + _example1, + style: const TextStyle(fontSize: 10), + ), + ), + ), + ], + ), + ), + ), + ], + ), + const SizedBox(height: 10), + Row( + children: [ + GestureDetector( + onTap: () { + setState(() { + _answer1 = false; + _answer2 = true; + _answer3 = false; + _answer4 = false; + }); + }, + child: Container( + padding: const EdgeInsets.all(0), + child: _answer2 + ? const Icon(Icons.check, color: Colors.green) + : const Icon(Icons.check, color: Colors.grey), + ), + ), + const SizedBox(width: 5), + Expanded( + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: Colors.black, + width: 0.5, + ), + color: _answer2 ? Colors.green : Colors.white, + ), + height: 24, + alignment: Alignment.center, + child: Row( + children: [ + const SizedBox(width: 10), + const Text('A: '), + Expanded( + child: SizedBox( + //width: _screenWidth * 0.7, + child: Text( + _example2, + style: const TextStyle(fontSize: 10), + ), + ), + ), + ], + ), + ), + ), + ], + ), + const SizedBox(height: 10), + Row( + children: [ + GestureDetector( + onTap: () { + setState(() { + _answer1 = false; + _answer2 = false; + _answer3 = true; + _answer4 = false; + }); + }, + child: Container( + padding: const EdgeInsets.all(0), + child: _answer3 + ? const Icon(Icons.check, color: Colors.green) + : const Icon(Icons.check, color: Colors.grey), + ), + ), + const SizedBox(width: 5), + Expanded( + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: Colors.black, + width: 0.5, + ), + color: _answer3 ? Colors.green : Colors.white, + ), + height: 24, + alignment: Alignment.center, + child: Row( + children: [ + const SizedBox(width: 10), + const Text('A: '), + Expanded( + child: SizedBox( + //width: _screenWidth * 0.7, + child: Text( + _example3, + style: const TextStyle(fontSize: 10), + ), + ), + ), + ], + ), + ), + ), + ], + ), + const SizedBox(height: 10), + Row( + children: [ + GestureDetector( + onTap: () { + setState(() { + _answer1 = false; + _answer2 = false; + _answer3 = false; + _answer4 = true; + }); + }, + child: Container( + padding: const EdgeInsets.all(0), + child: _answer4 + ? const Icon(Icons.check, color: Colors.green) + : const Icon(Icons.check, color: Colors.grey), + ), + ), + const SizedBox(width: 5), + Expanded( + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: Colors.black, + width: 0.5, + ), + color: _answer4 ? Colors.green : Colors.white, + ), + height: 24, + alignment: Alignment.center, + child: Row( + children: [ + const SizedBox(width: 10), + const Text('A: '), + Expanded( + child: SizedBox( + //width: _screenWidth * 0.7, + child: Text( + _example4, + style: const TextStyle(fontSize: 10), + ), + ), + ), + ], + ), + ), + ), + ], + ), + ], + ), + ), + ], + ); + + default: + throw ArgumentError('Invalid quiz type'); + } + } } diff --git a/frontend/lib/screens/home/bookreport/bookreport_writing_screen.dart b/frontend/lib/screens/home/bookreport/bookreport_writing_screen.dart index b35a54a1d6..d462fb48b5 100644 --- a/frontend/lib/screens/home/bookreport/bookreport_writing_screen.dart +++ b/frontend/lib/screens/home/bookreport/bookreport_writing_screen.dart @@ -3,6 +3,8 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:frontend/http.dart'; import 'package:frontend/provider/bookinfo_provider.dart'; import 'package:frontend/provider/secure_storage_provider.dart'; +import 'package:frontend/screens/home/bookreport/booksearch_screen_util.dart' + as searchutil; import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; @@ -39,10 +41,12 @@ class _BookReportWritingState extends State { final TextEditingController _answerController3 = TextEditingController(); final TextEditingController _answerController4 = TextEditingController(); var token; - - // Predefined values for author and publisher - final String _author = "작가"; - final String _publisher = "출판사"; + List BookData = []; + String _author = "작가"; + String _publisher = "출판사"; + String _isbn = ""; + String _publisherDate = ""; + String _imageUrl = ""; late final _KeyboardVisibilityObserverWrapper _keyboardVisibilityObserverWrapper; @@ -146,6 +150,48 @@ class _BookReportWritingState extends State { hintText: '도서를 입력하세요.', border: InputBorder.none, ), + textInputAction: TextInputAction.go, + onSubmitted: (value) async { + BookData = + await SearchBook(_bookTitleController.text); + showDialog( + // ignore: use_build_context_synchronously + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('도서 검색 결과'), + content: SingleChildScrollView( + child: Column( + children: [ + for (int i = 0; i < BookData.length; i++) + searchutil.BookSearchListItem( + data: BookData[i], + type: "search", + clubId: 0, + onSelected: (selectedData) { + print( + 'Selected Data: $selectedData'); + _bookTitleController.text = + selectedData['title']; + setState(() { + _author = selectedData['author']; + _publisher = + selectedData['publisher']; + }); + _isbn = selectedData['isbn']; + _publisherDate = + selectedData['pubdate']; + _imageUrl = selectedData['image']; + Navigator.of(context).pop(); + }, + ), + ], + ), + ), + ); + }, + ); + }, ), ), ), @@ -157,7 +203,7 @@ class _BookReportWritingState extends State { padding: const EdgeInsets.symmetric(horizontal: 20), child: Row( children: [ - Text('$_author | $_publisher', + Text('$_author | $_publisher', style: const TextStyle(color: Colors.black)), ], ), @@ -318,8 +364,13 @@ class _BookReportWritingState extends State { // 글 저장 기능 추가 contentCreate( token, - 1, - 3, + 0, + _isbn, + _bookTitleController.text, + _author, + _publisher, + _publisherDate, + _imageUrl, _template, _bookTitleController.text, _writingController.text, diff --git a/frontend/lib/screens/home/bookreport/booksearch_screen_util.dart b/frontend/lib/screens/home/bookreport/booksearch_screen_util.dart new file mode 100644 index 0000000000..5449ab8ec5 --- /dev/null +++ b/frontend/lib/screens/home/bookreport/booksearch_screen_util.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:go_router/go_router.dart'; +import 'package:provider/provider.dart'; +import 'package:frontend/provider/secure_storage_provider.dart'; +import 'package:frontend/http.dart'; + +class BookSearchListItem extends StatefulWidget { + final Map data; + final String type; + final int clubId; + final Function(Map) onSelected; + + const BookSearchListItem({ + super.key, + required this.data, + required this.type, + required this.clubId, + required this.onSelected, + }); + + @override + State createState() => _BookSearchListItemState(); +} + +class _BookSearchListItemState extends State { + @override + Widget build(BuildContext context) { + return Column( + children: [ + GestureDetector( + onTap: () { + widget.onSelected(widget.data); + }, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 80.w, + height: 105.h, + decoration: ShapeDecoration( + image: DecorationImage( + image: NetworkImage(widget.data['image']), + fit: BoxFit.fill, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(3)), + ), + ), + SizedBox(width: 5.w), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 177.w, + child: Text( + widget.data['title'], + style: TextStyle( + color: Colors.black, + fontSize: 12.sp, + fontFamily: 'Noto Sans KR', + fontWeight: FontWeight.w700, + ), + overflow: TextOverflow.clip, + ), + ), + SizedBox(height: 5.h), + SizedBox( + width: 177.w, + child: Text( + "${(widget.data['author'] == '') ? '저자 미상' : widget.data['author']} | ${widget.data['publisher']}", + style: TextStyle( + color: Colors.black, + fontSize: 10.sp, + fontFamily: 'Noto Sans KR', + fontWeight: FontWeight.w700, + ), + ), + ), + ], + ), + ], + ), + ), + ], + ); + } +} diff --git a/frontend/lib/screens/home/mypage/makelibrary_screen.dart b/frontend/lib/screens/home/mypage/makelibrary_screen.dart index 2c6155a10a..4141f28b32 100644 --- a/frontend/lib/screens/home/mypage/makelibrary_screen.dart +++ b/frontend/lib/screens/home/mypage/makelibrary_screen.dart @@ -1,17 +1,44 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:frontend/http.dart'; +import 'package:frontend/provider/secure_storage_provider.dart'; import 'package:go_router/go_router.dart'; +import 'package:frontend/screens/home/bookreport/booksearch_screen_util.dart' + as searchutil; +import 'package:provider/provider.dart'; class MakeLibraryScreen extends StatefulWidget { const MakeLibraryScreen({super.key}); @override - // ignore: library_private_types_in_public_api _MakeLibraryScreenState createState() => _MakeLibraryScreenState(); } class _MakeLibraryScreenState extends State { String _appBarTitle = '서재 이름'; + final TextEditingController _bookTitleController = TextEditingController(); + List BookData = []; + String _author = "작가"; + String _publisher = "출판사"; + String _isbn = ""; + String _publisherDate = ""; + String _imageUrl = ""; + List _bookList = []; + Map _tempbookList = {}; + var token; + + @override + void initState() { + super.initState(); + + _initUserState(); + } + + Future _initUserState() async { + final secureStorage = + Provider.of(context, listen: false); + token = await secureStorage.readData("token"); + } @override Widget build(BuildContext context) { @@ -86,16 +113,158 @@ class _MakeLibraryScreenState extends State { color: const Color(0xFFF2F2F2), borderRadius: BorderRadius.circular(25), ), - child: const Row( + child: Row( children: [ - Icon(Icons.search), - SizedBox(width: 10), - Text('책 제목을 검색해주세요'), + const Icon(Icons.search), + const SizedBox(width: 10), + Expanded( + child: SizedBox( + //width: _screenWidth * 0.7, + child: TextField( + style: const TextStyle(fontSize: 14), + controller: _bookTitleController, + decoration: const InputDecoration( + hintText: '도서를 입력하세요.', + border: InputBorder.none, + ), + textInputAction: TextInputAction.go, + onSubmitted: (value) async { + BookData = + await SearchBook(_bookTitleController.text); + showDialog( + // ignore: use_build_context_synchronously + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('도서 검색 결과'), + content: SingleChildScrollView( + child: Column( + children: [ + for (int i = 0; i < BookData.length; i++) + searchutil.BookSearchListItem( + data: BookData[i], + type: "search", + clubId: 0, + onSelected: (selectedData) { + print(selectedData); + _tempbookList = { + 'isbn': selectedData['isbn'], + 'title': selectedData['title'], + 'author': selectedData['author'], + 'publisher': + selectedData['publisher'], + 'publishDate': + selectedData['pubdate'], + 'imageUrl': selectedData['image'], + }; + setState(() { + _bookTitleController.text = ""; + _bookList.add(_tempbookList); + }); + _tempbookList = {}; + Navigator.of(context).pop(); + }, + ), + ], + ), + ), + ); + }, + ); + }, + ), + ), + ), ], ), ), + Expanded( + child: ListView.builder( + physics: const AlwaysScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: _bookList.length, + itemBuilder: (context, index) { + var book = _bookList[index]; + return Dismissible( + key: UniqueKey(), + onDismissed: (direction) { + setState(() { + _bookList.removeAt(index); + }); + }, + background: Container( + color: Colors.red, + alignment: Alignment.centerRight, + padding: const EdgeInsets.only(right: 20.0), + child: const Icon(Icons.delete), + ), + child: Container( + margin: const EdgeInsets.symmetric( + vertical: 8.0, horizontal: 20.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 80.w, + height: 105.h, + child: ClipRRect( + borderRadius: BorderRadius.circular(3.0), + child: Image.network( + book['imageUrl'], + fit: BoxFit.cover, + ), + ), + ), + SizedBox(width: 5.w), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + (book['title'].length > 100) + ? book['title'].substring(0, 100) + '...' + : book['title'], + style: TextStyle( + color: Colors.black, + fontSize: 15.sp, + fontFamily: 'Noto Sans KR', + fontWeight: FontWeight.w700, + ), + overflow: TextOverflow.clip, + ), + SizedBox(height: 5.h), + Text( + "${(book['author'] == '') ? '저자 미상' : book['author']} | ${book['publisher']}", + style: TextStyle( + color: Colors.black, + fontSize: 11.sp, + fontFamily: 'Noto Sans KR', + fontWeight: FontWeight.w700, + ), + ), + ], + ), + ), + ], + ), + ), + ); + }, + ), + ), ], ), + floatingActionButton: FloatingActionButton( + onPressed: () { + addBooksToLibrary(token, _appBarTitle, _bookList); + print(_appBarTitle); + print(_bookList); + //context.pop(); + }, + backgroundColor: const Color(0xFF0E9913), + child: const Icon(Icons.save), + ), + floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, ), ); } diff --git a/frontend/lib/screens/home/mypage/mypage_screen.dart b/frontend/lib/screens/home/mypage/mypage_screen.dart index 360db0bbdc..67e1e07f2e 100644 --- a/frontend/lib/screens/home/mypage/mypage_screen.dart +++ b/frontend/lib/screens/home/mypage/mypage_screen.dart @@ -3,6 +3,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:frontend/http.dart'; import 'package:frontend/provider/secure_storage_provider.dart'; +import 'package:frontend/screens/home/mypage/signup_screen.dart'; import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; import 'package:google_sign_in/google_sign_in.dart'; @@ -196,7 +197,7 @@ class LoggedWidget extends StatelessWidget { Icon( Icons.account_circle, size: 70.w, - ), + ), SizedBox(width: 16.w), Text( name, @@ -215,9 +216,16 @@ class LoggedWidget extends StatelessWidget { } } -class LoginWidget extends StatelessWidget { +class LoginWidget extends StatefulWidget { const LoginWidget({super.key}); + @override + _LoginWidgetState createState() => _LoginWidgetState(); +} + +class _LoginWidgetState extends State { + dynamic userInfo; + @override Widget build(BuildContext context) { return Container( @@ -244,7 +252,6 @@ class LoginWidget extends StatelessWidget { SizedBox(width: 76.w), ElevatedButton( onPressed: () { - //context.push('/login'); signInWithGoogle(context); }, child: const Text('로그인'), @@ -256,26 +263,27 @@ class LoginWidget extends StatelessWidget { ), ); } -} -void signInWithGoogle(BuildContext context) async { - final secureStorage = - Provider.of(context, listen: false); - final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn(); + void signInWithGoogle(BuildContext context) async { + final secureStorage = + Provider.of(context, listen: false); + final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn(); + + if (googleUser != null) { + print('name = ${googleUser.displayName}'); + print('email = ${googleUser.email}'); + print('id = ${googleUser.id}'); - if (googleUser != null) { - // print('name = ${googleUser.displayName}'); - // print('email = ${googleUser.email}'); - // print('id = ${googleUser.id}'); - dynamic userInfo = await login(googleUser.email); - await secureStorage.saveData('userID', googleUser.email); - if (userInfo['token'] == null) { - context.push('/signup'); + userInfo = await login(googleUser.email); + await secureStorage.saveData('userID', googleUser.email); + print(userInfo); + if (userInfo['exceptionCode'] != null) { + print('진입'); + await context.push('/signup'); + } + await secureStorage.saveData("token", userInfo['token']); + await secureStorage.saveData("id", userInfo['id']); } - print(userInfo['token']); - print(userInfo['id']); - await secureStorage.saveData("token", userInfo['token']); - await secureStorage.saveData("id", userInfo['id']); } } diff --git a/frontend/lib/screens/home/mypage/signup_screen.dart b/frontend/lib/screens/home/mypage/signup_screen.dart index 0fd34c5b5b..ea5e21bc5c 100644 --- a/frontend/lib/screens/home/mypage/signup_screen.dart +++ b/frontend/lib/screens/home/mypage/signup_screen.dart @@ -220,7 +220,6 @@ class _SignupState extends State { if (_validateAge(_ageController.text) == null && _validateName(_nameController.text) == null && _validateGender(_genderController.text) == null) { - print("성공"); final secureStorage = Provider.of(context, listen: false); @@ -235,6 +234,7 @@ class _SignupState extends State { 'age', _ageController.text); await secureStorage.saveData( 'gender', _genderController.text); + print("성공"); context.pop(); } else { print("실패"); diff --git a/frontend/lib/screens/home/search/search_screen.dart b/frontend/lib/screens/home/search/search_screen.dart index 06344fc2fa..1f67106839 100644 --- a/frontend/lib/screens/home/search/search_screen.dart +++ b/frontend/lib/screens/home/search/search_screen.dart @@ -52,7 +52,7 @@ class _SearchState extends State { _scrollController.dispose(); super.dispose(); } - + @override void initState() { super.initState(); @@ -62,7 +62,7 @@ class _SearchState extends State { @override Widget build(BuildContext context) { final secureStorage = - Provider.of(context, listen: false); + Provider.of(context, listen: false); return ScreenUtilInit( designSize: const Size(390, 675), builder: (context, child) => Scaffold( @@ -139,17 +139,10 @@ class _SearchState extends State { children: [ if (check && GroupData.isEmpty) Text("검색 결과가 없습니다"), ElevatedButton( - onPressed: () async { - dynamic userInfo = await login( - "test13@gmail.com"); //10-최창연, 11, 12, 13-한지민, 14, 15, 16 - // dynamic userInfo = await singup("test16@gmail.com", "랑데부", 24, "남자"); - print(userInfo['token']); - print(userInfo['id']); - await secureStorage.saveData( - "token", userInfo['token']); - await secureStorage.saveData("id", userInfo['id']); + onPressed: () { + context.push('/bookreport_viewing'); }, - child: Text('한지민'), + child: Text('테스트'), ), ElevatedButton( onPressed: () async { @@ -177,6 +170,7 @@ class _SearchState extends State { onPressed: () async { await secureStorage.deleteData("token"); await secureStorage.deleteData("id"); + await secureStorage.deleteAllData(); }, child: Text('토큰 삭제'), ), @@ -188,7 +182,8 @@ class _SearchState extends State { itemCount: GroupData.length, itemBuilder: (BuildContext context, int index) { return Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), + padding: + const EdgeInsets.symmetric(horizontal: 8.0), child: GroupUtil.GroupListItem( id: GroupData[index]['id'], groupName: GroupData[index]['name'],