diff --git a/lib/core/OssLicenseScreen.dart b/lib/core/OssLicenseScreen.dart deleted file mode 100644 index 0953e34..0000000 --- a/lib/core/OssLicenseScreen.dart +++ /dev/null @@ -1,129 +0,0 @@ -import 'package:blueberry_flutter_template/utils/oss_licenses.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:url_launcher/url_launcher.dart'; - -import '../utils/ResponsiveLayoutBuilder.dart'; - -class OssLicenseScreen extends StatelessWidget { - const OssLicenseScreen({super.key}); - - static Future> loadLicenses() async { - // merging non-dart dependency list using LicenseRegistry. - final lm = >{}; - await for (var l in LicenseRegistry.licenses) { - for (var p in l.packages) { - final lp = lm.putIfAbsent(p, () => []); - lp.addAll(l.paragraphs.map((p) => p.text)); - } - } - final licenses = allDependencies.toList(); - for (var key in lm.keys) { - licenses.add(Package( - name: key, - description: '', - authors: [], - version: '', - license: lm[key]!.join('\n\n'), - isMarkdown: false, - isSdk: false, - dependencies: [], - )); - } - return licenses..sort((a, b) => a.name.compareTo(b.name)); - } - - static final _licenses = loadLicenses(); - - @override - Widget build(BuildContext context) { - return ResponsiveLayoutBuilder( - context, - Scaffold( - appBar: AppBar( - title: const Text('Open Source Licenses'), - ), - body: FutureBuilder>( - future: _licenses, - initialData: const [], - builder: (context, snapshot) { - return ListView.separated( - padding: const EdgeInsets.all(0), - itemCount: snapshot.data?.length ?? 0, - itemBuilder: (context, index) { - final package = snapshot.data![index]; - return ListTile( - title: Text('${package.name} ${package.version}'), - subtitle: package.description.isNotEmpty - ? Text(package.description) - : null, - trailing: const Icon(Icons.chevron_right), - onTap: () => Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => - MiscOssLicenseSingle(package: package), - ), - ), - ); - }, - separatorBuilder: (context, index) => const Divider()); - })), - ); - } -} - -class MiscOssLicenseSingle extends StatelessWidget { - final Package package; - - const MiscOssLicenseSingle({super.key, required this.package}); - - String _bodyText() { - return package.license!.split('\n').map((line) { - if (line.startsWith('//')) line = line.substring(2); - line = line.trim(); - return line; - }).join('\n'); - } - - @override - Widget build(BuildContext context) { - return ResponsiveLayoutBuilder( - context, - Scaffold( - appBar: AppBar(title: Text('${package.name} ${package.version}')), - body: Container( - color: Theme.of(context).canvasColor, - child: ListView(children: [ - if (package.description.isNotEmpty) - Padding( - padding: const EdgeInsets.only( - top: 12.0, left: 12.0, right: 12.0), - child: Text(package.description, - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith(fontWeight: FontWeight.bold))), - if (package.homepage != null) - Padding( - padding: const EdgeInsets.only( - top: 12.0, left: 12.0, right: 12.0), - child: InkWell( - child: Text(package.homepage!, - style: const TextStyle( - color: Colors.blue, - decoration: TextDecoration.underline)), - onTap: () => launch(package.homepage!), - )), - if (package.description.isNotEmpty || package.homepage != null) - const Divider(), - Padding( - padding: - const EdgeInsets.only(top: 12.0, left: 12.0, right: 12.0), - child: Text(_bodyText(), - style: Theme.of(context).textTheme.bodyMedium), - ), - ])), - ), - ); - } -} diff --git a/lib/core/TopScreen.dart b/lib/core/TopScreen.dart index 8d982eb..2ec6d68 100644 --- a/lib/core/TopScreen.dart +++ b/lib/core/TopScreen.dart @@ -1,11 +1,9 @@ -import 'package:blueberry_flutter_template/feature/onboarding/OnboardingScreen.dart'; +import 'package:blueberry_flutter_template/feature/map/PoliceMapScreen.dart'; +import 'package:blueberry_flutter_template/feature/mypage/MyPageScreen.dart'; +import 'package:blueberry_flutter_template/feature/voiceOutput/VoiceOutputScreen.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import '../feature/login/LoginScreen.dart'; -import '../feature/map/PoliceMapScreen.dart'; -import '../feature/match/MatchScreen.dart'; - /// TopScreen.dart /// /// Top Page @@ -25,9 +23,9 @@ class TopScreen extends ConsumerWidget { final selectedIndex = ref.watch(selectedIndexProvider); final List pages = [ + const VoiceOutputScreen(), const PoliceMapScreen(), - const MatchScreen(), - const LoginScreen(), + const MyPageScreen(), ]; return Scaffold( @@ -42,17 +40,17 @@ class TopScreen extends ConsumerWidget { backgroundColor: Colors.blueGrey[100], items: const [ BottomNavigationBarItem( - icon: Icon(Icons.podcasts), - label: 'Post', + icon: Icon(Icons.mic), + label: 'Voice', ), BottomNavigationBarItem( - icon: Icon(Icons.pets), - label: 'match', + icon: Icon(Icons.map), + label: 'Map', ), BottomNavigationBarItem( - icon: Icon(Icons.account_circle), + icon: Icon(Icons.person), label: 'MyPage', - ) + ), ], currentIndex: selectedIndex, onTap: (index) => diff --git a/lib/core/widget/MiniAvatarWidget.dart b/lib/core/widget/MiniAvatarWidget.dart deleted file mode 100644 index 66b2aff..0000000 --- a/lib/core/widget/MiniAvatarWidget.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import '../../feature/mypage/provider/ProfileImageProvider.dart'; - -class MiniAvatar extends ConsumerWidget { - const MiniAvatar({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final profileImage = ref.watch(profileImageStreamProvider); - - return Center( - child: profileImage.when( - data: (imageUrl) => CircleAvatar( - radius: 30, - backgroundImage: NetworkImage(imageUrl), - ), - loading: () => const CircularProgressIndicator(), - error: (e, s) => CircleAvatar( - radius: 30, - backgroundColor: Colors.grey.shade300, - )), - ); - } -} diff --git a/lib/core/widget/NickNameTextWidget.dart b/lib/core/widget/NickNameTextWidget.dart deleted file mode 100644 index 39bcc4b..0000000 --- a/lib/core/widget/NickNameTextWidget.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import '../../feature/mypage/provider/MyPageNameProvider.dart'; -import '../../utils/AppStrings.dart'; - -class NickNameTextWidget extends ConsumerWidget { - const NickNameTextWidget({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final nickname = ref.watch(myPageNicknameProvider); - return nickname.when( - data: (name) => Text( - name, - style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), - ), - loading: () => const Text(''), - error: (e, s) => const Text(AppStrings.nickNameTextWidgetError), - ); - } -} diff --git a/lib/core/widget/SquareTitleWidget.dart b/lib/core/widget/SquareTitleWidget.dart deleted file mode 100644 index 3fc8fbc..0000000 --- a/lib/core/widget/SquareTitleWidget.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/material.dart'; - -class SquareTileWidget extends StatelessWidget { - final String imagePath; - final Function()? onTap; - const SquareTileWidget( - {super.key, required this.imagePath, required this.onTap}); - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: onTap, - child: Container( - padding: const EdgeInsets.all(20), - decoration: BoxDecoration( - border: Border.all(color: Colors.white), - borderRadius: BorderRadius.circular(16), - color: Colors.grey[200], - ), - child: Image.asset( - imagePath, - height: 40, - ), - ), - ); - } -} diff --git a/lib/feature/admin/AdminLoadingPage.dart b/lib/feature/admin/AdminLoadingPage.dart deleted file mode 100644 index e46d1f4..0000000 --- a/lib/feature/admin/AdminLoadingPage.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:flutter/material.dart'; - -class AdminLoadingPage extends StatelessWidget { - static const String name = 'AdminLoadingPage'; - const AdminLoadingPage({super.key}); - - @override - Widget build(BuildContext context) { - return const Scaffold( - body: Center( - child: Text( - "Admin Page", - style: TextStyle( - fontSize: 50, - ), - ), - ), - ); - } -} diff --git a/lib/feature/admin/AdminScreen.dart b/lib/feature/admin/AdminScreen.dart deleted file mode 100644 index 7735e4a..0000000 --- a/lib/feature/admin/AdminScreen.dart +++ /dev/null @@ -1,98 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -import 'AdminUserListPage.dart'; - -class AdminScreen extends StatelessWidget { - static const String name = 'AdminScreen'; - - const AdminScreen({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text( - 'Admin', - style: TextStyle(fontSize: 30), - ), - ), - body: Center( - child: Column(mainAxisAlignment: MainAxisAlignment.start, children: [ - const SizedBox( - height: 30, - ), - GestureDetector( - onTap: () { - context.pushNamed(AdminUserListPage.name); - }, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), color: Colors.black), - width: 280, - height: 50, - child: const Center( - child: Text( - 'User List', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.w700, - fontSize: 20), - ), - ), - ), - ), - const SizedBox( - height: 30, - ), - GestureDetector( - onTap: () { - //TODO: ItemListPage 구현 필요 - }, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), color: Colors.black), - width: 280, - height: 50, - child: const Center( - child: Text( - 'Item List', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.w700, - fontSize: 20), - ), - ), - ), - ), - const SizedBox( - height: 30, - ), - GestureDetector( - onTap: () { - //TODO: EventListPage 구현 필요 - }, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), color: Colors.black), - width: 280, - height: 50, - child: const Center( - child: Text( - 'Event List', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.w700, - fontSize: 20), - ), - ), - ), - ), - const SizedBox( - height: 30, - ), - ]), - ), - ); - } -} diff --git a/lib/feature/admin/AdminUserDetailPage.dart b/lib/feature/admin/AdminUserDetailPage.dart deleted file mode 100644 index 5820b85..0000000 --- a/lib/feature/admin/AdminUserDetailPage.dart +++ /dev/null @@ -1,105 +0,0 @@ -import 'package:flutter/material.dart'; - -class AdminUserDetailPage extends StatelessWidget { - static const String name = 'UserDetailPageInAdmin'; - - const AdminUserDetailPage({super.key}); - - @override - Widget build(BuildContext context) { - final userDetails = [ - 'Name', - 'Nickname', - 'Phone', - 'Email', - 'Description', - 'Profile Photo' - ]; - - return Scaffold( - appBar: AppBar( - title: const Text( - 'Users', - style: TextStyle(fontSize: 25, fontWeight: FontWeight.w600), - textAlign: TextAlign.center, - ), - ), - body: Center( - child: Column( - children: [ - Expanded( - child: ListView.separated( - padding: const EdgeInsets.symmetric(vertical: 20), - itemCount: userDetails.length, - itemBuilder: (context, index) { - return _buildUserInformationContainer(userDetails[index]); - }, - separatorBuilder: (context, index) => - const SizedBox(height: 20), - ), - ), - Padding( - padding: const EdgeInsets.all(45.0), - child: InkWell( - onTap: () { - print( - 'Test: move to the UserListInAdminPage & Add the User in UserListInAdminPage'); - }, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - color: Colors.blue, - ), - width: 350, - height: 50, - child: const Center( - child: Text( - 'Create', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.w700, - fontSize: 18, - ), - ), - ), - ), - ), - ), - ], - ), - ), - ); - } - - Widget _buildUserInformationContainer(String username) { - return InkWell( - onTap: () { - print('Test: Enter the User Information'); - }, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - color: Colors.grey, - ), - width: 350, - height: 50, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Padding( - padding: const EdgeInsets.only(left: 16.0), - child: Text( - username, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.w500, - fontSize: 20, - ), - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/feature/admin/AdminUserListPage.dart b/lib/feature/admin/AdminUserListPage.dart deleted file mode 100644 index 26e21bc..0000000 --- a/lib/feature/admin/AdminUserListPage.dart +++ /dev/null @@ -1,139 +0,0 @@ -import 'package:blueberry_flutter_template/feature/admin/AdminUserDetailPage.dart'; -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -class AdminUserListPage extends StatelessWidget { - static const String name = 'UserListInAdminPage'; - - const AdminUserListPage({super.key}); - - @override - Widget build(BuildContext context) { - final List usernames = [ - 'Username_1', - 'Username_2', - 'Username_3', - 'Username_4', - 'Username_5', - ]; - - return Scaffold( - appBar: AppBar( - title: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const SizedBox(), - const Text( - 'Users', - style: TextStyle(fontSize: 25, fontWeight: FontWeight.w600), - textAlign: TextAlign.center, - ), - IconButton( - icon: const Icon( - Icons.search, - size: 40, - ), - onPressed: () { - //TODO: UserSearchScreen 구현 필요 - }, - ), - ], - ), - ), - body: Padding( - padding: const EdgeInsets.all(8.0), - child: Center( - child: Column( - children: [ - Expanded( - child: ListView.separated( - padding: const EdgeInsets.symmetric(vertical: 20), - itemCount: usernames.length, - itemBuilder: (context, index) { - return _buildUserContainer(context, usernames[index]); - }, - separatorBuilder: (context, index) => - const SizedBox(height: 20), - ), - ), - Padding( - padding: const EdgeInsets.all(45.0), - child: InkWell( - onTap: () { - context.pushNamed(AdminUserDetailPage.name); - }, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - color: Colors.blue, - ), - width: 350, - height: 50, - child: const Center( - child: Text( - 'Add User', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.w700, - fontSize: 18, - ), - ), - ), - ), - ), - ), - ], - ), - ), - ), - ); - } - - Widget _buildUserContainer(BuildContext context, String username) { - return InkWell( - onTap: () { - context.pushNamed(AdminUserDetailPage.name); - }, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - color: Colors.black, - ), - width: 350, - height: 70, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Padding( - padding: const EdgeInsets.only(left: 16.0), - child: Text( - username, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.w700, - fontSize: 20, - ), - ), - ), - Row( - children: [ - IconButton( - icon: const Icon(Icons.edit, color: Colors.white), - onPressed: () { - context.pushNamed(AdminUserDetailPage.name); - }, - ), - IconButton( - icon: const Icon(Icons.delete, color: Colors.white), - onPressed: () { - print('Test: Delete the User'); - }, - ), - ], - ), - ], - ), - ), - ); - } -} diff --git a/lib/feature/calendar/CalendarBodyWidget.dart b/lib/feature/calendar/CalendarBodyWidget.dart deleted file mode 100644 index 0b54e56..0000000 --- a/lib/feature/calendar/CalendarBodyWidget.dart +++ /dev/null @@ -1,69 +0,0 @@ -part of 'CalendarScreen.dart'; - -class CalendarBodyWidget extends StatefulWidget { - CalendarBodyWidget({super.key}); - - @override - State createState() => _CalendarBodyWidgetState(); -} - -class _CalendarBodyWidgetState extends State { - List dateTime = []; - - //TODO: 해당 로직 provider로 분리 예정 - void setDate(int month, int year) { - final lastDay = DateTime(year, month + 1, 0); - final firstDay = DateTime(year, month, 1); - final int totalDate = lastDay.difference(firstDay).inDays; - dateTime = List.generate( - totalDate, (index) => firstDay.add(Duration(days: index))); - } - - @override - void initState() { - super.initState(); - setDate(DateTime.now().month, DateTime.now().year); - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Row( - children: List.generate( - DateTime.daysPerWeek, - (idx) => _dateItem(AppStrings.date[idx]), - ), - ), - Wrap( - children: [ - ...List.generate( - //TODO: 1일의 요일에 따른 배치 변경 - dateTime.length, - (idx) => _dateItem(dateTime[idx].day.toString()), - ), - ], - ) - ], - ); - } - - Widget _dateItem(String item) { - final color = Color(0xFF5E5E5E); - final textStyle = TextStyle( - fontSize: 14.sp, - height: 16 / 14, - fontWeight: FontWeight.w500, - color: color, - //fontFamily: 'Urbanist', - ); - return Container( - padding: EdgeInsets.symmetric(vertical: 14.w, horizontal: 5.4.w), - width: 50.29.w, - child: Text( - item, - style: textStyle, - ), - ); - } -} diff --git a/lib/feature/calendar/CalendarHeaderWidget.dart b/lib/feature/calendar/CalendarHeaderWidget.dart deleted file mode 100644 index 40ec88e..0000000 --- a/lib/feature/calendar/CalendarHeaderWidget.dart +++ /dev/null @@ -1,74 +0,0 @@ -part of 'CalendarScreen.dart'; - -class CalendarHeaderWidget extends StatelessWidget { - final int selectYear; - final int selectMonth; - final VoidCallback setYear; - final VoidCallback setMonth; - - const CalendarHeaderWidget( - {super.key, - required this.selectYear, - required this.selectMonth, - required this.setYear, - required this.setMonth}); - - @override - Widget build(BuildContext context) { - return Padding( - padding: EdgeInsets.symmetric(horizontal: 10.w), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SvgPicture.asset( - Assets.icon.icLeftArrow, - ), - //TODO: 하드코딩 변경 - Expanded( - child: Row(children: [ - _changeDate( - date: selectMonth, - changeDate: setMonth, - ), - _changeDate( - date: selectYear, - changeDate: setYear, - ), - ]), - ), - SvgPicture.asset(Assets.icon.icRightArrow), - ], - ), - ); - } - - Widget _changeDate({required int date, required VoidCallback changeDate}) { - const color = Color(0xFF000000); - final textStyle = TextStyle( - fontSize: 16.sp, - height: 20 / 16, - fontWeight: FontWeight.w700, - color: color, - //fontFamily: 'Urbanist', - ); - return GestureDetector( - onTap: changeDate, - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 16.w), - child: Row(children: [ - Text( - '$date', - style: textStyle, - ), - SizedBox( - width: 8.w, - ), - SvgPicture.asset( - Assets.icon.icDownArrow, - height: 20.w, - ), - ]), - ), - ); - } -} diff --git a/lib/feature/calendar/CalendarScreen.dart b/lib/feature/calendar/CalendarScreen.dart deleted file mode 100644 index 9dcd1fe..0000000 --- a/lib/feature/calendar/CalendarScreen.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:flutter_svg/svg.dart'; - -import '../../gen/assets.gen.dart'; -import '../../utils/AppStrings.dart'; - -part 'CalendarHeaderWidget.dart'; - -part 'CalendarBodyWidget.dart'; - -class CalendarScreen extends StatelessWidget { - const CalendarScreen({super.key}); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - //TODO: 하드 코딩 변경 - CalendarHeaderWidget( - selectYear: DateTime.now().year, - selectMonth: DateTime.now().month, - setYear: () {}, - setMonth: () {}, - ), - CalendarBodyWidget(), - ], - ); - } -} diff --git a/lib/feature/camera/CameraShadow.dart b/lib/feature/camera/CameraShadow.dart deleted file mode 100644 index f338dc2..0000000 --- a/lib/feature/camera/CameraShadow.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:flutter/material.dart'; - -class CameraShadow extends CustomPainter { - final double radius; - - CameraShadow({required this.radius}); - - @override - void paint(Canvas canvas, Size size) { - final paint = Paint() - ..color = Colors.black.withOpacity(0.5) - ..style = PaintingStyle.fill; - - final rect = Rect.fromLTWH(0, 0, size.width, size.height); - final circlePath = Path() - ..addOval(Rect.fromCircle( - center: Offset(size.width / 2, size.height / 2), radius: radius)) - ..addRect(rect) - ..fillType = PathFillType.evenOdd; - - canvas.drawPath(circlePath, paint); - } - - @override - bool shouldRepaint(CustomPainter oldDelegate) => false; -} diff --git a/lib/feature/camera/MyPageProfileImagePreview.dart b/lib/feature/camera/MyPageProfileImagePreview.dart deleted file mode 100644 index 94a6e0b..0000000 --- a/lib/feature/camera/MyPageProfileImagePreview.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'dart:io'; - -import 'package:blueberry_flutter_template/feature/camera/provider/fireStorageServiceProvider.dart'; -import 'package:blueberry_flutter_template/feature/mypage/MyPageScreen.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; - -import '../../services/FirebaseStoreServiceProvider.dart'; - -class SharePostScreen extends ConsumerWidget { - final File imageFile; - - const SharePostScreen(this.imageFile, {super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final storage = ref.read(fireStorageServiceProvider); - final fireStorage = ref.read(firebaseStoreServiceProvider); - final userId = FirebaseAuth.instance.currentUser!.uid; - - return Scaffold( - appBar: AppBar( - title: const Text('프로필 이미지 편집'), - ), - body: Center( - child: Column( - children: [ - ClipOval( - child: Image.file( - imageFile, - width: 100, - height: 100, - fit: BoxFit.cover, - ), - ), - TextButton( - onPressed: () async { - try { - final imageUrl = await storage.uploadImageFromApp( - imageFile, ImageType.profileimage, - fixedFileName: userId); - - // 프로필 이미지 생성 - fireStorage.createProfileIamge(userId, imageUrl); - - // 페이지 이동 - context.goNamed(MyPageScreen.name); - } catch (e) { - print(e); - } - }, - child: const Text('이미지 저장 하기'), - ), - ], - ), - ), - ); - } -} diff --git a/lib/feature/camera/ProfileCameraPage.dart b/lib/feature/camera/ProfileCameraPage.dart deleted file mode 100644 index a55efc8..0000000 --- a/lib/feature/camera/ProfileCameraPage.dart +++ /dev/null @@ -1,170 +0,0 @@ -import 'package:blueberry_flutter_template/feature/camera/CameraShadow.dart'; -import 'package:blueberry_flutter_template/feature/camera/MyPageProfileImagePreview.dart'; -import 'package:blueberry_flutter_template/feature/camera/provider/PageProvider.dart'; -import 'package:blueberry_flutter_template/services/camera/CameraService.dart'; -import 'package:blueberry_flutter_template/utils/AppStrings.dart'; -import 'package:camera/camera.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'dart:io'; -import 'package:path/path.dart'; -import 'package:path_provider/path_provider.dart'; - -class ProfileCameraPage extends ConsumerStatefulWidget { - const ProfileCameraPage({super.key}); - - @override - ConsumerState createState() => _ProfileCameraPageState(); -} - -class _ProfileCameraPageState extends ConsumerState { - final CameraService cameraService = CameraService(); - bool _isPressed = false; - Size size = Size.zero; - - @override - void initState() { - super.initState(); - initializeCamera(); - } - - @override - void dispose() { - cameraService.dispose(); - super.dispose(); - } - - Future initializeCamera() async { - await cameraService.initializeCameras(); - setState(() {}); - } - - @override - Widget build(BuildContext context) { - final pageNotifier = ref.watch(pageProvider.notifier); - size = MediaQuery.of(context).size; - - return Scaffold( - appBar: AppBar( - title: const Text(AppStrings.takeProfilePhoto), - leading: IconButton( - onPressed: () { - pageNotifier.moveToPage(0); - }, - icon: const Icon(Icons.arrow_back), - ), - ), - body: Column( - children: [ - Stack( - children: [ - Container( - width: size.width, - height: size.width * 1.3, - color: Colors.black, - child: cameraService.controller != null - ? _getPreview(cameraService.controller!, context) - : _buildErrorMessage(cameraService.changeCamera), - ), - Positioned( - top: 0, - right: 0, - child: IconButton( - onPressed: () async { - try { - await cameraService.toggleCamera(); - setState(() {}); - } catch (error) { - print("Error toggling camera: $error"); - } - }, - icon: const Icon(Icons.change_circle), - ), - ) - ], - ), - Expanded( - child: Center( - child: GestureDetector( - onTap: () { - _attemptTakePhoto(context); - }, - onTapDown: (_) => setState(() => _isPressed = true), - onTapUp: (_) => setState(() => _isPressed = false), - onTapCancel: () => setState(() => _isPressed = false), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Container( - decoration: ShapeDecoration( - shape: CircleBorder( - side: BorderSide( - color: _isPressed ? Colors.red : Colors.black12, - width: 20, - ), - ), - ), - ), - ), - ), - ), - ), - ], - ), - ); - } - - Widget _getPreview(CameraController controller, BuildContext context) { - return ClipRect( - child: OverflowBox( - alignment: Alignment.center, - child: FittedBox( - fit: BoxFit.fitWidth, - child: Stack(children: [ - SizedBox( - width: size.width, - height: size.width * 1.2, - child: CameraPreview(controller), - ), - Positioned.fill( - child: CustomPaint( - painter: CameraShadow(radius: size.width * 0.48), - ), - ), - ]), - ), - ), - ); - } - - Widget _buildErrorMessage(bool changeCamera) { - return Center( - child: Text( - changeCamera ? AppStrings.setFrontCamera : AppStrings.setBackCamera, - style: const TextStyle(color: Colors.white), - ), - ); - } - - void _attemptTakePhoto(BuildContext context) async { - // file에 접근할꺼임 사진을 촬영하고 저장하기 위해서 - final String timeInMilli = DateTime.now().millisecondsSinceEpoch.toString(); - // 이미지를 저장할때 시간을 기준으로 이미지명을 만들기 위해서 사용함 - try { - final path = - join((await getTemporaryDirectory()).path, '$timeInMilli.png'); - - final XFile file = await cameraService.controller!.takePicture(); - - await file.saveTo(path); - - final File imageFile = File(path); - - if (context.mounted) { - Navigator.of(context).push( - MaterialPageRoute(builder: (_) => SharePostScreen(imageFile))); - } - } catch (e) { - print('Error: $e'); - } - } -} diff --git a/lib/feature/camera/ProfileGalleryPage.dart b/lib/feature/camera/ProfileGalleryPage.dart deleted file mode 100644 index 1791343..0000000 --- a/lib/feature/camera/ProfileGalleryPage.dart +++ /dev/null @@ -1,104 +0,0 @@ -import 'dart:io'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:photo_manager/photo_manager.dart'; - -import 'MyPageProfileImagePreview.dart'; - -class ProfileGalleryPage extends ConsumerStatefulWidget { - const ProfileGalleryPage({super.key}); - - @override - _ImageGalleryState createState() => _ImageGalleryState(); -} - -class _ImageGalleryState extends ConsumerState { - final List _imageList = []; - - int _currentPage = 0; - - final int _pageSize = 50; - - @override - void initState() { - super.initState(); - _fetchImages(); - } - - Future _fetchImages() async { - // 권한 요청 - final PermissionState ps = await PhotoManager.requestPermissionExtend(); - if (ps.isAuth || ps.hasAccess) { - // 안드로이드 일때 ps.hasAccess가 없으면 이미지를 못가져옴 - - // 모든 앨범 가져오기 - final List albums = await PhotoManager.getAssetPathList( - onlyAll: true, - ); - if (albums.isEmpty) return; - - final List images = await albums[0].getAssetListPaged( - page: _currentPage, - size: _pageSize, // 가져올 이미지 수 - ); - - setState(() { - _imageList.addAll(images); - _currentPage++; - }); - } else { - SnackBar snackBar = SnackBar( - content: const Text("앨범 접근 권한을 허용 해주세요"), - action: SnackBarAction( - label: 'OK', - onPressed: () { - ScaffoldMessenger.of(context).hideCurrentSnackBar(); - PhotoManager.openSetting(); - }, - ), - ); - } - } - - void _navigateToSharePostScreen(File imageFile) { - Navigator.of(context).push(MaterialPageRoute( - builder: (context) => SharePostScreen(imageFile), - )); - } - - @override - Widget build(BuildContext context) { - return GridView.builder( - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, - mainAxisSpacing: 5, - crossAxisSpacing: 5, - ), - itemCount: _imageList.length, - itemBuilder: (context, index) { - return GestureDetector( - onTap: () async { - final File? imageFile = await _imageList[index].file; - if (imageFile != null) { - _navigateToSharePostScreen(imageFile); - } - }, - child: FutureBuilder( - future: _imageList[index].thumbnailData, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done && - snapshot.data != null) { - return Image.memory( - snapshot.data!, - fit: BoxFit.cover, - ); - } - return Container(color: Colors.grey); - }, - ), - ); - }, - ); - } -} diff --git a/lib/feature/camera/SettingInsideAccountManagerWidget.dart b/lib/feature/camera/SettingInsideAccountManagerWidget.dart deleted file mode 100644 index c923c6b..0000000 --- a/lib/feature/camera/SettingInsideAccountManagerWidget.dart +++ /dev/null @@ -1,126 +0,0 @@ -import 'package:blueberry_flutter_template/services/InAppPurchaseService.dart'; -import 'package:blueberry_flutter_template/utils/AppStrings.dart'; -import 'package:blueberry_flutter_template/core/widget/MiniAvatarWidget.dart'; -import 'package:blueberry_flutter_template/feature/payment/widget/WebPaymentWidget.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; - -import '../payment/provider/UserMemberShipProvider.dart'; - -class SettingAccountManagerWidget extends ConsumerStatefulWidget { - static const String name = 'SettingAccountManagerWidget'; - const SettingAccountManagerWidget({super.key}); - - @override - ConsumerState createState() => - _SettingAccountManagerWidget(); -} - -class _SettingAccountManagerWidget - extends ConsumerState { - bool showNumber = false; - final InAppPurchaseService _purchaseService = InAppPurchaseService(); - - @override - void initState() { - // TODO: implement initState - super.initState(); - _purchaseService.initStoreInfo(); - } - - @override - void dispose() { - // TODO: implement dispose - super.dispose(); - _purchaseService.dispose(); - } - - @override - Widget build(BuildContext context) { - final userMemberShipState = ref.watch(userMemberShipProvider); - - return Scaffold( - appBar: AppBar( - title: const Text("나의 계정 관리"), - ), - body: Column( - children: [ - const Padding( - padding: EdgeInsets.all(10.0), - child: Row( - children: [ - MiniAvatar(), - SizedBox( - width: 20, - ), - Text("홍길동"), - ], - ), - ), - const Divider(), - ListTile( - leading: const Icon(Icons.call), - title: showNumber - ? const Text("010-1234-5678") - : const Text("010-**34-56**"), - trailing: ElevatedButton( - onPressed: () { - setState(() { - showNumber = !showNumber; - }); - }, - child: Container( - child: const Text("보이기"), - ), - ), - ), - const Divider(), - ListTile( - leading: const Icon(Icons.email), - title: userMemberShipState.isMembership - ? const Text(AppStrings.isUserMembership) - : const Text(AppStrings.notUserMembership), - trailing: _inAppPurchaseBtn(userMemberShipState, context), - ), - ], - ), - ); - } - - ElevatedButton _inAppPurchaseBtn( - UserMemberShipState userMemberShipState, BuildContext context) { - return ElevatedButton( - onPressed: userMemberShipState.isMembership - ? null // 이미 멤버십이 있으면 버튼 비활성화 - : () async { - if (kIsWeb) { - context.goNamed(WebPaymentWidget.name); - } else { - // 앱 일때 인앱 결제 처리 - try { - await _purchaseService.buyMembership(); - // 구매 성공 시 상태 업데이트 또는 사용자에게 알림 - await ref - .watch(userMemberShipProvider.notifier) - .loadMembershipStatus(); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text(AppStrings.successMessage_membership)), - ); - } catch (e) { - // 구매 실패 시 에러 처리 - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text(AppStrings.errorMessage_purchase)), - ); - } - } - }, - child: Container( - child: userMemberShipState.isMembership ? null : const Text("가입 하기"), - ), - ); - } -} diff --git a/lib/feature/camera/SettingsBottomSheet.dart b/lib/feature/camera/SettingsBottomSheet.dart deleted file mode 100644 index 195264e..0000000 --- a/lib/feature/camera/SettingsBottomSheet.dart +++ /dev/null @@ -1,77 +0,0 @@ -import 'package:blueberry_flutter_template/feature/camera/provider/PageProvider.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:permission_handler/permission_handler.dart'; - -class SettingsBottomSheet extends ConsumerStatefulWidget { - const SettingsBottomSheet({super.key}); - - @override - ConsumerState createState() => - _SettingsBottomSheetState(); -} - -class _SettingsBottomSheetState extends ConsumerState { - Future _requestAlbumPermission() async { - // 앨범 권한 요청 - PermissionStatus photoStatus = await Permission.photos.request(); - PermissionStatus storageStatus = await Permission.storage.request(); - - if (photoStatus.isGranted || storageStatus.isGranted) { - print("앨범 접근 권한이 허용되었습니다."); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('앨범 접근 권한이 허용되었습니다.')), - ); - return true; - } else if (photoStatus.isDenied || storageStatus.isDenied) { - print("앨범 접근 권한이 거부되었습니다."); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('앨범 접근 권한이 거부되었습니다.')), - ); - return false; - } else if (photoStatus.isPermanentlyDenied || - storageStatus.isPermanentlyDenied) { - print("앨범 접근 권한이 영구적으로 거부되었습니다. 설정에서 권한을 허용해주세요."); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('앨범 접근 권한이 영구적으로 거부되었습니다. 설정에서 권한을 허용해주세요.')), - ); - // 사용자가 권한을 영구적으로 거부한 경우 설정 페이지로 이동 - await openAppSettings(); - return false; - } - return false; - } - - @override - Widget build(BuildContext context) { - final pageNotifier = ref.watch(pageProvider.notifier); - return Column( - children: [ - const SizedBox( - width: double.infinity, - ), - TextButton( - onPressed: () async { - pageNotifier.moveToPage(1); - Navigator.pop(context); - }, - child: const Text("직접 촬영 하기"), - ), - TextButton( - onPressed: () async { - bool hasPermission = await _requestAlbumPermission(); - if (hasPermission) { - pageNotifier.moveToPage(2); - Navigator.pop(context); - } else { - pageNotifier.moveToPage(0); - Navigator.pop(context); - } - }, - child: const Text("앨범에서 선택 하기"), - ) - ], - ); - } -} diff --git a/lib/feature/camera/provider/PageProvider.dart b/lib/feature/camera/provider/PageProvider.dart deleted file mode 100644 index 48eb921..0000000 --- a/lib/feature/camera/provider/PageProvider.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -class PageState { - final PageController pageController; - final int pageNumber; - - PageState({required this.pageController, required this.pageNumber}); - PageState copyWith({PageController? pageController, int? pageNumber}) { - return PageState( - pageController: pageController ?? this.pageController, - pageNumber: pageNumber ?? this.pageNumber); - } -} - -class PageProviderNotifier extends StateNotifier { - PageProviderNotifier() - : super(PageState(pageController: PageController(), pageNumber: 0)); - - void moveToPage(int index, {PageController? pageController}) { - state = state.copyWith(pageNumber: index); - state.pageController.animateToPage(index, - duration: const Duration(microseconds: 300), curve: Curves.easeInOut); - } -} - -final pageProvider = - StateNotifierProvider.autoDispose((ref) { - return PageProviderNotifier(); -}); diff --git a/lib/feature/camera/provider/fireStorageServiceProvider.dart b/lib/feature/camera/provider/fireStorageServiceProvider.dart deleted file mode 100644 index 1de83ea..0000000 --- a/lib/feature/camera/provider/fireStorageServiceProvider.dart +++ /dev/null @@ -1,120 +0,0 @@ -import 'dart:io'; - -import 'package:firebase_storage/firebase_storage.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:uuid/uuid.dart'; - -/// FirebaseStorageService.dart -/// -/// Firebase Storage Service -/// - Firebase Storage에 이미지 업로드 서비스 -/// - uploadImageFromWeb(): 웹 이미지 업로드 -/// - uploadImageFromApp(): 앱 이미지 업로드 -/// -/// @jwson-automation - -enum ImageType { - banner, - profileimage, - event, - item, - productImage, - descriptionImage -} - -final fireStorageServiceProvider = Provider((ref) { - return FirebaseStorageService(); -}); - -class FirebaseStorageService { - final FirebaseStorage _storage = FirebaseStorage.instance; - - Future uploadImageFromWeb(Uint8List webImageFile, ImageType type, - {String? fixedFileName}) async { - try { - // UUID를 사용하여 유니크한 파일 이름 생성 - var uuid = const Uuid(); - String fullPath = ''; - - switch (type) { - case ImageType.banner: - fullPath = '${type.name}/$fixedFileName'; - break; - case ImageType.profileimage: - fullPath = '${type.name}/$fixedFileName'; - break; - case ImageType.event: - String fileName = '${uuid.v4()}.jpg'; - fullPath = '${type.name}/$fileName'; - break; - case ImageType.item: - String fileName = '${uuid.v4()}.jpg'; - fullPath = '${type.name}/$fileName'; - break; - case ImageType.productImage: - String fileName = '${uuid.v4()}.jpg'; - fullPath = '${type.name}/$fileName'; - break; - case ImageType.descriptionImage: - String fileName = '${uuid.v4()}.jpg'; - fullPath = '${type.name}/$fileName'; - break; - } - - final ref = _storage.ref().child(fullPath); - await ref.putData(webImageFile); - final url = await ref.getDownloadURL(); - print('Image uploaded successfully : $url'); - return url; - } catch (e) { - // Handle error - print('Failed to upload image: $e'); - throw Exception('Failed to upload image'); - } - } - - Future uploadImageFromApp(File appImageFile, ImageType type, - {String? fixedFileName}) async { - try { - // UUID를 사용하여 유니크한 파일 이름 생성 - var uuid = const Uuid(); - - String fullPath = ''; - switch (type) { - case ImageType.banner: - fullPath = '${type.name}/$fixedFileName'; - break; - case ImageType.profileimage: - fullPath = '${type.name}/$fixedFileName'; - break; - case ImageType.event: - String fileName = '${uuid.v4()}.jpg'; - fullPath = '${type.name}/$fileName'; - break; - case ImageType.item: - String fileName = '${uuid.v4()}.jpg'; - fullPath = '${type.name}/$fileName'; - break; - case ImageType.productImage: - String fileName = '${uuid.v4()}.jpg'; - fullPath = '${type.name}/$fileName'; - break; - case ImageType.descriptionImage: - String fileName = '${uuid.v4()}.jpg'; - fullPath = '${type.name}/$fileName'; - break; - } - - final ref = _storage.ref().child(fullPath); - await ref.putFile(appImageFile); - final url = await ref.getDownloadURL(); - print('Image uploaded successfully_firebase_storage_service : $url'); - return url; - } catch (e) { - // Handle error - print('Failed to upload image: $e'); - throw Exception('Failed to upload image'); - } - } -} diff --git a/lib/feature/camera/setting_inside_notice.dart b/lib/feature/camera/setting_inside_notice.dart deleted file mode 100644 index 5c01b9b..0000000 --- a/lib/feature/camera/setting_inside_notice.dart +++ /dev/null @@ -1,99 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:webview_flutter/webview_flutter.dart'; - -class FixSettingNotice extends ConsumerStatefulWidget { - const FixSettingNotice({super.key}); - - @override - ConsumerState createState() => _FixSettingNoticeState(); -} - -class _FixSettingNoticeState extends ConsumerState { - bool _isWebViewVisible = false; - bool _isWebViewVisible2 = false; - late WebViewController _webViewController; - late WebViewController _webViewController2; - - @override - void initState() { - // TODO: implement initState - super.initState(); - _webViewController = WebViewController(); - _webViewController2 = WebViewController(); - } - - void _toggleWebView() { - setState(() { - _isWebViewVisible = !_isWebViewVisible; - }); - } - - void _toggleWebView2() { - setState(() { - _isWebViewVisible = !_isWebViewVisible; - _isWebViewVisible2 = !_isWebViewVisible2; - }); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text("공지 사항"), - ), - body: SingleChildScrollView( - child: Column( - children: [ - GestureDetector( - onTap: () { - _toggleWebView(); - if (_isWebViewVisible) { - _webViewController.loadRequest( - Uri.parse('https://flutter.dev/'), - ); - } - }, - child: const ListTile( - leading: Icon(Icons.speaker_notes), - title: Text("블루베리 템플릿 버젼 1.0.0v"), - ), - ), - AnimatedContainer( - duration: const Duration(microseconds: 3000), - curve: Curves.easeInOut, - height: _isWebViewVisible ? 500.0 : 0, - child: _isWebViewVisible - ? WebViewWidget( - controller: _webViewController, - ) - : Container()), - const Divider(), - GestureDetector( - onTap: () { - _toggleWebView2(); - if (_isWebViewVisible2) { - _webViewController.loadRequest( - Uri.parse('https://www.google.co.kr/'), - ); - } - }, - child: const ListTile( - leading: Icon(Icons.speaker_notes), - title: Text("블루베리 템플릿 버젼 0.1.0v"), - ), - ), - AnimatedContainer( - duration: const Duration(microseconds: 3000), - curve: Curves.easeInOut, - height: _isWebViewVisible2 ? 500.0 : 0, - child: _isWebViewVisible2 - ? WebViewWidget( - controller: _webViewController, - ) - : Container()), - ], - ), - )); - } -} diff --git a/lib/feature/chat/ChatRoomScreen.dart b/lib/feature/chat/ChatRoomScreen.dart deleted file mode 100644 index a674fc0..0000000 --- a/lib/feature/chat/ChatRoomScreen.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:blueberry_flutter_template/feature/chat/widget/ChatRoomWidget.dart'; -import 'package:blueberry_flutter_template/utils/AppStrings.dart'; -import 'package:flutter/material.dart'; - -import '../../services/FirebaseService.dart'; - -class ChatRoomScreen extends StatelessWidget { - static const String name = 'ChatRoomScreen'; - const ChatRoomScreen({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: const Text(AppStrings.chatRoomScreenTitle)), - body: const Center( - child: ChatRoomWidget(), - ), - floatingActionButton: _buildFloatingActionButton(context), - ); - } -} - -FloatingActionButton _buildFloatingActionButton(BuildContext context) { - return FloatingActionButton( - onPressed: () { - FirebaseService().createChatRoom("test room"); - }, - backgroundColor: Colors.blue, - child: const Icon(Icons.add), - ); -} diff --git a/lib/feature/chat/ChatScreen.dart b/lib/feature/chat/ChatScreen.dart deleted file mode 100644 index b1d1d41..0000000 --- a/lib/feature/chat/ChatScreen.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../utils/AppStrings.dart'; -import 'widget/ChatListWidget.dart'; -import 'widget/ChatSendWidget.dart'; - -class ChatScreen extends StatelessWidget { - static const String name = 'ChatScreen'; - const ChatScreen({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: const Text(AppStrings.lessonChatScreenTitle)), - body: Center( - child: Column( - children: [ - const Expanded(child: ChatListWidget()), - ChatSendWidget(), - ], - ), - ), - ); - } -} diff --git a/lib/feature/chat/provider/ChatListProvider.dart b/lib/feature/chat/provider/ChatListProvider.dart deleted file mode 100644 index c1bd8a0..0000000 --- a/lib/feature/chat/provider/ChatListProvider.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:firebase_database/firebase_database.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import '../../../model/ChatListItemModel.dart'; -import '../../../model/ChatMessageModel.dart'; - -final roomIdProvider = StateProvider((ref) { - return ''; -}); - -final loadRoomListProvider = - FutureProvider>((ref) async { - final firestore = FirebaseFirestore.instance; - var user = FirebaseAuth.instance.currentUser; - - if (user == null) { - throw Exception('No current user found'); - } - - final docSnapshot = - await firestore.collection('chatRooms').doc(user.uid).get(); - - if (docSnapshot.exists) { - final data = docSnapshot.data(); - final List rooms = data?['rooms'] ?? []; - final List roomList = []; - - for (var roomId in rooms) { - final roomSnapshot = await FirebaseDatabase.instance - .ref("chats/$roomId") - .orderByKey() - .get(); - - if (roomSnapshot.value != null) { - final roomData = roomSnapshot.value as Map; - final value = ChatListItemModel( - roomId: roomId, - otherUserId: roomData['senderId'] == user.uid - ? roomData['receiverId'] - : roomData['senderId'], - lastMessage: roomData['lastMessage'], - lastMessageTime: - DateTime.fromMillisecondsSinceEpoch(roomData['lastMessageTime']), - ); - roomList.add(value); - } - } - return roomList; - } else { - return []; - } -}); - -final chatMessageListProvider = - StreamProvider.family, String>((ref, roomId) async* { - final roomSnapshot = - await FirebaseDatabase.instance.ref("chats/$roomId").get(); - - if (roomSnapshot.exists) { - final roomData = roomSnapshot.value as Map; - final messageData = roomData['messages'] as List; - List messages = []; - - for (var message in messageData) { - messages.add(ChatMessageModel( - senderId: message['senderId'], - message: message['message'], - timestamp: DateTime.fromMillisecondsSinceEpoch(message['timestamp']), - isRead: message['isRead'], - )); - } - messages.sort((a, b) => a.timestamp.compareTo(b.timestamp)); - yield messages; - } else { - yield []; - } -}); diff --git a/lib/feature/chat/widget/ChatListWidget.dart b/lib/feature/chat/widget/ChatListWidget.dart deleted file mode 100644 index c8f8253..0000000 --- a/lib/feature/chat/widget/ChatListWidget.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import '../../../model/ChatMessageModel.dart'; -import '../../../services/FirebaseAuthServiceProvider.dart'; -import '../../../utils/AppColors.dart'; -import '../../../utils/AppTextStyle.dart'; -import '../provider/ChatListProvider.dart'; - -class ChatListWidget extends ConsumerWidget { - const ChatListWidget({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final roomId = ref.watch(roomIdProvider); - final list = ref.watch(chatMessageListProvider(roomId)); - User? loggedInUser = ref.read(firebaseAuthServiceProvider).getCurrentUser(); - - return list.when( - data: (messages) => _buildListView(messages, loggedInUser), - loading: () => const Center(child: CircularProgressIndicator()), - error: (error, stackTrace) => Center(child: Text('Error: $error'))); - } -} - -Widget _buildListView(List messages, User? loggedInUser) { - return ListView.builder( - shrinkWrap: true, - itemCount: messages.length, - itemBuilder: (context, index) { - bool isMe = messages[index].senderId == loggedInUser!.uid; - return Row( - mainAxisAlignment: - isMe ? MainAxisAlignment.end : MainAxisAlignment.start, - children: [ - Container( - padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16), - margin: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), - decoration: BoxDecoration( - color: isMe ? grey : limeGreen, - borderRadius: isMe - ? const BorderRadius.only( - topLeft: Radius.circular(14), - topRight: Radius.circular(14), - bottomLeft: Radius.circular(14), - ) - : const BorderRadius.only( - topLeft: Radius.circular(14), - topRight: Radius.circular(14), - bottomRight: Radius.circular(14), - ), - ), - child: Text( - messages[index].message, - style: black16TextStyle, - ), - ), - ], - ); - }, - ); -} diff --git a/lib/feature/chat/widget/ChatRoomWidget.dart b/lib/feature/chat/widget/ChatRoomWidget.dart deleted file mode 100644 index ddbd227..0000000 --- a/lib/feature/chat/widget/ChatRoomWidget.dart +++ /dev/null @@ -1,110 +0,0 @@ -import 'package:blueberry_flutter_template/feature/chat/provider/ChatListProvider.dart'; -import 'package:blueberry_flutter_template/feature/chat/ChatScreen.dart'; -import 'package:blueberry_flutter_template/model/ChatListItemModel.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:intl/intl.dart'; - -class ChatRoomWidget extends ConsumerWidget { - const ChatRoomWidget({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final list = ref.watch(loadRoomListProvider); - return list.when( - data: (data) => _buildRoomListView(data, ref), - loading: () => const Center(child: CircularProgressIndicator()), - error: (error, stackTrace) => Center(child: Text('Error: $error'))); - } -} - -Widget _buildRoomListView(List data, WidgetRef ref) { - return ListView.builder( - itemCount: data.length, - itemBuilder: (context, index) { - return Dismissible( - key: Key(index.toString()), - direction: DismissDirection.endToStart, - background: Container( - alignment: Alignment.centerRight, - color: const Color(0xff910c1b), - child: const Padding( - padding: EdgeInsets.only(right: 16.0), - child: Icon( - CupertinoIcons.delete_simple, - color: Colors.white, - size: 16, - ), - ), - ), - onDismissed: (direction) { - data.removeAt(index); - }, - child: GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - ref.read(roomIdProvider.notifier).state = data[index].roomId; - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => const ChatScreen(), - ), - ); - }, - child: Padding( - padding: - const EdgeInsets.symmetric(vertical: 8.0, horizontal: 8.0), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, // 이 부분을 추가 - children: [ - _buildProfileImage(null), - const SizedBox(width: 8), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(data[index].otherUserId, - style: const TextStyle( - color: Colors.black, fontSize: 15)), - Text( - DateFormat('a h:mm', 'ko_KR') - .format(data[index].lastMessageTime), - style: const TextStyle( - color: Colors.grey, fontSize: 10)), - ], - ), - Text(data[index].lastMessage, - style: const TextStyle( - color: Colors.grey, fontSize: 12), - overflow: TextOverflow.ellipsis), - ], - ), - ), - ], - ), - ), - ), - ); - }); -} - -Widget _buildProfileImage(String? path) { - return ClipRRect( - borderRadius: BorderRadius.circular(10), - child: path != null && path.isNotEmpty - ? Image.asset( - path, - fit: BoxFit.cover, - width: 45, - height: 45, - ) - : Container( - width: 45, - height: 45, - color: Colors.grey, - ), - ); -} diff --git a/lib/feature/chat/widget/ChatSendWidget.dart b/lib/feature/chat/widget/ChatSendWidget.dart deleted file mode 100644 index 7d09ace..0000000 --- a/lib/feature/chat/widget/ChatSendWidget.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:blueberry_flutter_template/feature/chat/provider/ChatListProvider.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import '../../../services/FirebaseService.dart'; - -class ChatSendWidget extends ConsumerWidget { - final TextEditingController _controller = TextEditingController(); - - ChatSendWidget({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final firebaseService = FirebaseService(); - final roomId = ref.watch(roomIdProvider); - - void sendChatMessage(String value) async { - await firebaseService.addChatMessage(roomId, value); - _controller.clear(); - } - - return SafeArea( - child: Container( - padding: const EdgeInsets.all(8.0), - child: Row( - children: [ - Expanded( - child: TextField( - controller: _controller, - decoration: const InputDecoration( - hintText: 'Enter message', border: OutlineInputBorder()), - onSubmitted: (value) async { - // 엔터키가 눌러졌을때 하는 행동 - value.isEmpty ? null : sendChatMessage(value); - }, - ), - ), - IconButton( - icon: const Icon(Icons.send), - onPressed: () async { - // 눌러졌을때 행동 - _controller.text.isEmpty - ? null - : sendChatMessage(_controller.text); - }, - ), - ], - ), - ), - ); - } -} diff --git a/lib/feature/friend/FriendsListScreen.dart b/lib/feature/friend/FriendsListScreen.dart deleted file mode 100644 index 1fdcb83..0000000 --- a/lib/feature/friend/FriendsListScreen.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'widget/FriendsListWidget.dart'; - -class FriendsListScreen extends StatelessWidget { - static const String name = 'FriendsListScreen'; - - const FriendsListScreen({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(), - body: const Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded(child: FriendsListWidget()), - ], - ), - ), - ); - } -} diff --git a/lib/feature/friend/provider/FriendsListProvider.dart b/lib/feature/friend/provider/FriendsListProvider.dart deleted file mode 100644 index ecefaa4..0000000 --- a/lib/feature/friend/provider/FriendsListProvider.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_storage/firebase_storage.dart'; - -import '../../../model/FriendModel.dart'; - -// 친구 목록을 제공하는 Provider -final friendsListProvider = StreamProvider>((ref) { - final firestore = FirebaseFirestore.instance; - - const userId = '4D22soWrX1aoGcuQ0GpAtNYDiYN2'; // 임시 유저 UUID - - return firestore - .collection('users') - .doc(userId) - .collection('friends') - .snapshots() - .map((snapshot) { - return snapshot.docs.map((doc) { - final data = doc.data(); - return FriendModel.fromJson({ - ...data, - 'lastConnect': - (data['lastConnect'] as Timestamp).toDate().toIso8601String(), - }); - }).toList(); - }); -}); - -// // 친구목록 이미지 URL을 제공하는 Provider -// final friendsListImageProvider = FutureProvider.family((ref, imageName) async { -// final ref = FirebaseStorage.instance.ref('friends-profile/$imageName'); -// return await ref.getDownloadURL(); -// }); - -/// 이미지 URL을 제공하는 공용 Provider -final imageProvider = - FutureProvider.family((ref, imagePath) async { - final ref = FirebaseStorage.instance.ref(imagePath); - return await ref.getDownloadURL(); -}); diff --git a/lib/feature/friend/widget/BottomSheetButtonWidget.dart b/lib/feature/friend/widget/BottomSheetButtonWidget.dart deleted file mode 100644 index 749e186..0000000 --- a/lib/feature/friend/widget/BottomSheetButtonWidget.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/material.dart'; - -class BottomSheetButtonWidget extends StatelessWidget { - final VoidCallback onPressed; - final String text; - - const BottomSheetButtonWidget({ - super.key, - required this.onPressed, - required this.text, - }); - - @override - Widget build(BuildContext context) { - return ElevatedButton( - style: ElevatedButton.styleFrom( - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - minimumSize: const Size(120, 40), // 버튼 크기 - ), - onPressed: onPressed, - child: Text(text), - ); - } -} diff --git a/lib/feature/friend/widget/FriendBottomSheet.dart b/lib/feature/friend/widget/FriendBottomSheet.dart deleted file mode 100644 index ecf7e0b..0000000 --- a/lib/feature/friend/widget/FriendBottomSheet.dart +++ /dev/null @@ -1,103 +0,0 @@ -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -import '../../../model/FriendModel.dart'; -import '../../../utils/AppStrings.dart'; -import '../../user_report/provider/ReportModalSheet.dart'; -import 'BottomSheetButtonWidget.dart'; - -class FriendBottomSheet extends StatelessWidget { - final FriendModel friend; - final String imageUrl; - - const FriendBottomSheet( - {super.key, required this.friend, required this.imageUrl}); - - @override - Widget build(BuildContext context) { - return Container( - height: 260, // 바텀 시트 높이 수정 부분 - decoration: BoxDecoration( - color: Colors.grey[200], - borderRadius: const BorderRadius.vertical( - top: Radius.circular(25.0), - ), - ), - padding: const EdgeInsets.all(16), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.only(top: 10.0), - child: Row( - children: [ - Container( - width: 120, - height: 120, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all(color: Colors.white, width: 4), - image: DecorationImage( - image: CachedNetworkImageProvider(imageUrl), - fit: BoxFit.cover, - ), - ), - ), - const SizedBox(width: 16), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(friend.name, - style: const TextStyle( - fontWeight: FontWeight.bold, fontSize: 20)), - const SizedBox(height: 8), - Text(friend.status, style: const TextStyle(fontSize: 16)), - ], - ), - ), - ], - ), - ), - const SizedBox(height: 24), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - BottomSheetButtonWidget( - onPressed: () { - Navigator.of(context).pop(); - GoRouter.of(context).push('/chat'); - }, - text: AppStrings.chatButton, - ), - BottomSheetButtonWidget( - onPressed: () { - Navigator.of(context).pop(); - GoRouter.of(context) - .push('/userdetail'); // 현재 임의 경로 사용 중 수정 필요 - }, - text: AppStrings.profileButton, - ), - BottomSheetButtonWidget( - onPressed: () { - Navigator.of(context).pop(); - showModalBottomSheet( - context: context, - isScrollControlled: true, - shape: const RoundedRectangleBorder( - borderRadius: - BorderRadius.vertical(top: Radius.circular(25.0)), - ), - builder: (context) => ReportModalSheet(friend: friend), - ); - }, - text: AppStrings.reportButton, - ), - ], - ), - ], - ), - ); - } -} diff --git a/lib/feature/friend/widget/FriendListItemWidget.dart b/lib/feature/friend/widget/FriendListItemWidget.dart deleted file mode 100644 index 84c89c0..0000000 --- a/lib/feature/friend/widget/FriendListItemWidget.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:flutter/material.dart'; - -import '../../../model/FriendModel.dart'; -import '../../../utils/Formatter.dart'; - -class FriendListItemWidget extends StatelessWidget { - final FriendModel friend; - final String imageUrl; - final VoidCallback onTap; - - const FriendListItemWidget( - {super.key, - required this.friend, - required this.imageUrl, - required this.onTap}); - - @override - Widget build(BuildContext context) { - final lastConnect = timeAgo(friend.lastConnect); - - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0), - child: GestureDetector( - onTap: onTap, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - border: Border.all( - color: Colors.grey.shade300, - width: 1.0, - ), - ), - child: Column( - children: [ - ListTile( - leading: CircleAvatar( - radius: 25, - backgroundImage: CachedNetworkImageProvider(imageUrl), - ), - title: Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text(lastConnect), - const SizedBox(height: 5), - Text(friend.name, - style: const TextStyle(fontWeight: FontWeight.bold)), - Text(friend.status), - ], - ), - ), - Padding( - padding: const EdgeInsets.only(bottom: 8.0, left: 20.0), - child: Row( - children: [ - const Icon(Icons.favorite, color: Colors.red), - const SizedBox(width: 5), - Text(friend.likes.toString()), - ], - ), - ), - ], - ), - ), - ), - ); - } -} diff --git a/lib/feature/friend/widget/FriendsListWidget.dart b/lib/feature/friend/widget/FriendsListWidget.dart deleted file mode 100644 index 7fe111b..0000000 --- a/lib/feature/friend/widget/FriendsListWidget.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import '../provider/FriendsListProvider.dart'; -import 'FriendBottomSheet.dart'; -import 'FriendListItemWidget.dart'; - -class FriendsListWidget extends ConsumerWidget { - const FriendsListWidget({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final friendList = ref.watch(friendsListProvider); - - return friendList.when( - data: (friends) { - return RefreshIndicator( - onRefresh: () async { - ref.invalidate(friendsListProvider); - }, - child: ListView.builder( - itemCount: friends.length, - itemBuilder: (context, index) { - final friend = friends[index]; - final friendListImageUrl = ref - .watch(imageProvider('friends-profile/${friend.imageName}')); - - return friendListImageUrl.when( - data: (imageUrl) { - return FriendListItemWidget( - friend: friend, - imageUrl: imageUrl, - onTap: () { - showModalBottomSheet( - context: context, - isScrollControlled: true, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical( - top: Radius.circular(25.0), - ), - ), - builder: (context) => FriendBottomSheet( - friend: friend, - imageUrl: imageUrl, - ), - ); - }, - ); - }, - loading: () => const Center(child: CircularProgressIndicator()), - error: (error, stack) { - return const Center(child: Text('Error loading image')); - }, - ); - }, - ), - ); - }, - loading: () => const Center(child: CircularProgressIndicator()), - error: (error, stack) => Center(child: Text('Error: $error')), - ); - } -} diff --git a/lib/feature/login/LoginScreen.dart b/lib/feature/login/LoginScreen.dart deleted file mode 100644 index 0b4b115..0000000 --- a/lib/feature/login/LoginScreen.dart +++ /dev/null @@ -1,204 +0,0 @@ -import 'package:blueberry_flutter_template/feature/camera/ProfileCameraPage.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; - -import '../../core/widget/SquareTitleWidget.dart'; -import '../../services/FirebaseAuthServiceProvider.dart'; -import '../../services/SocialAuthService.dart'; -import '../../utils/AppStrings.dart'; -import '../camera/ProfileGalleryPage.dart'; -import '../camera/provider/PageProvider.dart'; -import '../mypage/MyPageScreen.dart'; -import '../signup/SignUpScreen.dart'; -import 'provider/UserInfoProvider.dart'; - -/// DeleteMyPage.dart -/// -/// My Page -/// - 사용자 정보를 보여주는 화면 -/// - 사용자 정보 수정 및 로그아웃 기능을 제공 -/// -/// @jwson-automation - -final wantEditAgeProvider = StateProvider((ref) => false); -final wantEditNameProvider = StateProvider((ref) => false); - -///# 로그인 화면 -///* 이메일, 소셜(구글,애플,깃허브) 로그인 기능 제공 -class LoginScreen extends ConsumerWidget { - const LoginScreen({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - // 페이지 정보를 가져오는 상태 관리 객체 - final pageState = ref.watch(pageProvider); - // 로그인 상태를 가져오는 상태 관리 객체 - final loginState = ref.watch(loginStateProvider); - - return Scaffold( - body: loginState.maybeWhen( - data: (user) => user != null - ? PageView( - controller: pageState.pageController, - physics: const NeverScrollableScrollPhysics(), - children: const [ - MyPageScreen(), - ProfileCameraPage(), - ProfileGalleryPage(), - ]) - : _buildLogin(context, ref), - orElse: () => const Center(child: CircularProgressIndicator()), - ), - ); - } -} - -Widget _buildLogin(BuildContext context, WidgetRef ref) { - TextEditingController emailController = TextEditingController(); - TextEditingController passwordController = TextEditingController(); - final formKey = GlobalKey(); - - return Padding( - padding: const EdgeInsets.all(16.0), - child: Form( - key: formKey, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TextFormField( - controller: emailController, - decoration: const InputDecoration( - labelText: AppStrings.emailInputLabel, - border: OutlineInputBorder(), - ), - validator: (value) { - if (value == null || value.isEmpty) { - return AppStrings.errorMessage_emptyEmail; - } - if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$') - .hasMatch(value)) { - return AppStrings.errorMessage_invalidEmail; - } - return null; - }, - ), - const SizedBox(height: 16), - TextFormField( - controller: passwordController, - obscureText: true, - decoration: const InputDecoration( - labelText: AppStrings.passwordInputLabel, - border: OutlineInputBorder(), - ), - validator: (value) { - if (value == null || value.isEmpty) { - return AppStrings.errorMessage_emptyPassword; - } - if (!RegExp(r'^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$') - .hasMatch(value)) { - return AppStrings.errorMessage_invalidPassword; - } - return null; - }, - ), - - const SizedBox(height: 5), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - TextButton( - onPressed: () {}, - child: const Text( - AppStrings.passwordForgot, - style: TextStyle(color: Colors.black87), - ), - ), - ], - ), - - //로그인 버튼 & 회원가입 버튼 - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - ElevatedButton( - onPressed: () async { - if (formKey.currentState!.validate()) { - String email = emailController.text.trim(); - String password = passwordController.text.trim(); - try { - await ref - .read(firebaseAuthServiceProvider) - .signInWithEmailPassword(email, password); - } catch (e) { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: const Text(AppStrings.errorTitle), - content: Text('에러: $e'), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context), - child: const Text(AppStrings.okButtonText), - ), - ], - ); - }, - ); - } - } - }, - style: ElevatedButton.styleFrom( - foregroundColor: Colors.white, - backgroundColor: Colors.blue, - ), - child: const Text(AppStrings.loginButtonText), - ), - TextButton( - onPressed: () => context.goNamed(SignUpScreen.name), - child: const Text( - AppStrings.signUpButtonText, - style: TextStyle(color: Colors.blue), - ), - ), - ], - ), - - const SizedBox(height: 16), - Container( - width: MediaQuery.of(context).size.width * 1, - height: 1, - color: Colors.black, - ), - const SizedBox( - height: 15, - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - //Google button - SquareTileWidget( - onTap: () => SocialAuthService().signInWithGoogle(), - imagePath: 'assets/login_page_images/google.png'), - const SizedBox( - width: 10, - ), - //Apple button - SquareTileWidget( - onTap: () => SocialAuthService().signInWithApple(), - imagePath: 'assets/login_page_images/apple.png'), - const SizedBox( - width: 10, - ), - //github button - SquareTileWidget( - onTap: () => SocialAuthService().signInWithGithub(), - imagePath: 'assets/login_page_images/github.png'), - ], - ), - ], - ), - ), - ); -} diff --git a/lib/feature/login/provider/UserInfoProvider.dart b/lib/feature/login/provider/UserInfoProvider.dart deleted file mode 100644 index fcee211..0000000 --- a/lib/feature/login/provider/UserInfoProvider.dart +++ /dev/null @@ -1,94 +0,0 @@ -import 'package:blueberry_flutter_template/model/UserDataModel.dart'; -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import '../../../model/UserModel.dart'; - -/// UserInfoProvider.dart -/// -/// User Info Provider -/// - 사용자 정보 데이터를 제공하는 Provider -/// - fetchUserInfoStream(): 사용자 정보 데이터 불러오기 -/// -/// @jwson-automation - -// 사용자의 인증 상태를 실시간으로 추적 -final loginStateProvider = StreamProvider((ref) { - return FirebaseAuth.instance.authStateChanges(); -}); - -// 로그인한 사용자의 UID를 추적 -final userIdProvider = FutureProvider((ref) { - final user = ref.watch(loginStateProvider).asData?.value; - return user?.uid; -}); - -// 사용자 정보를 가져오는 공급자 -final userDataLoadProvider = FutureProvider((ref) async { - final userId = await ref.watch(userIdProvider.future); - if (userId == null) throw Exception('User not logged in'); - final userDoc = - await FirebaseFirestore.instance.collection('users').doc(userId).get(); - if (userDoc.exists) { - return UserModel.fromJson(userDoc.data()!); - } - throw Exception('User not found'); -}); - -final getUserDataProvider = FutureProvider((ref) async { - final userId = await ref.watch(userIdProvider.future); - if (userId == null) throw Exception('User not logged in'); - - final userDoc = - await FirebaseFirestore.instance.collection('users').doc(userId).get(); - - if (userDoc.exists) { - final userData = userDoc.data()!; - return UserDataModel.fromJson(userData); - } - - throw Exception('User not found'); -}); - -final userInfoNotifierProvider = - StateNotifierProvider((ref) { - final user = ref.watch(userDataLoadProvider); - return UserNotifier(user.maybeWhen( - data: (user) => user, - orElse: () => UserModel( - userId: '익명', - name: '익명', - email: '', - age: 123, - mbti: 'NULL', - profileImageUrl: '', - isMemberShip: false, - createdAt: DateTime.now(), - userClass: 'user', - likeGivens: [""]))); -}); - -// 유저 정보 업데이트를 위한 노티파이어 -class UserNotifier extends StateNotifier { - UserNotifier(super.state); - - Future updateUser( - {String? name, int? age, String? profilePicture, String? mbti}) async { - try { - final userId = state.userId; - final updateData = {}; - if (name != null) updateData['name'] = name; - if (age != null) updateData['age'] = age; - if (profilePicture != null) updateData['profilePicture'] = profilePicture; - if (mbti != null) updateData['mbti'] = mbti; - - await FirebaseFirestore.instance - .collection('users') - .doc(userId) - .update(updateData); - } catch (e) { - print('Failed to update user data: $e'); - } - } -} diff --git a/lib/feature/map/provider/PoliceStationProvider.dart b/lib/feature/map/provider/PoliceStationProvider.dart index dc4ab0f..2e67bc9 100644 --- a/lib/feature/map/provider/PoliceStationProvider.dart +++ b/lib/feature/map/provider/PoliceStationProvider.dart @@ -1,13 +1,14 @@ import 'dart:convert'; -import 'package:blueberry_flutter_template/model/GoogleMapPlace.dart'; +import 'package:blueberry_flutter_template/model/GoogleMapPlaceModel.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:http/http.dart' as http; //Google Map API key 등록 필요 -String apiKey = dotenv.env['GOOGLE_MAP_API_KEY'] ?? 'AIzaSyDEGMHYfH7pQ1ks39pXrhfmXZJRosp755w'; +String apiKey = dotenv.env['GOOGLE_MAP_API_KEY'] ?? + 'AIzaSyDEGMHYfH7pQ1ks39pXrhfmXZJRosp755w'; // Android : AndroidManifest.xml 에 API Key와 권한 추가 필요 // iOS : AppDelegate.swift, Info.plist 에 API Key와 권한 추가 필요 @@ -28,7 +29,8 @@ Future _getPlacePhoneNumber(String placeId, String apiKey) async { } final policeStationsProvider = - FutureProvider.family, LatLng>((ref, location) async { + FutureProvider.family, LatLng>( + (ref, location) async { final url = 'https://maps.googleapis.com/maps/api/place/nearbysearch/json' '?location=${location.latitude},${location.longitude}' '&radius=1000' @@ -42,7 +44,7 @@ final policeStationsProvider = final places = await Future.wait(results.map((placeJson) async { final placeId = placeJson['place_id']; final phoneNumber = await _getPlacePhoneNumber(placeId, apiKey); - return Place.fromJson(placeJson, phoneNumber); + return GoogleMapPlaceModel.fromJson(placeJson, phoneNumber); }).toList()); return places; } else { diff --git a/lib/feature/map/provider/SeletedPlaceProvider.dart b/lib/feature/map/provider/SeletedPlaceProvider.dart index ff01c5a..654dd71 100644 --- a/lib/feature/map/provider/SeletedPlaceProvider.dart +++ b/lib/feature/map/provider/SeletedPlaceProvider.dart @@ -1,4 +1,5 @@ -import 'package:blueberry_flutter_template/model/GoogleMapPlace.dart'; +import 'package:blueberry_flutter_template/model/GoogleMapPlaceModel.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -final selectedPlaceProvider = StateProvider.autoDispose((ref) => null); +final selectedPlaceProvider = + StateProvider.autoDispose((ref) => null); diff --git a/lib/feature/map/widget/GoogleMapWidget.dart b/lib/feature/map/widget/GoogleMapWidget.dart index 33e8878..b7c80bf 100644 --- a/lib/feature/map/widget/GoogleMapWidget.dart +++ b/lib/feature/map/widget/GoogleMapWidget.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:blueberry_flutter_template/feature/map/provider/LocationProvider.dart'; import 'package:blueberry_flutter_template/feature/map/provider/PoliceStationProvider.dart'; import 'package:blueberry_flutter_template/feature/map/provider/SeletedPlaceProvider.dart'; -import 'package:blueberry_flutter_template/model/GoogleMapPlace.dart'; +import 'package:blueberry_flutter_template/model/GoogleMapPlaceModel.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; @@ -11,7 +11,7 @@ import 'package:url_launcher/url_launcher.dart'; class GoogleMapWidget extends ConsumerWidget { final Completer googleMapControllerCompleter; final LatLng locationState; - final AsyncValue> policeStationsAsyncValue; + final AsyncValue> policeStationsAsyncValue; const GoogleMapWidget({ required this.googleMapControllerCompleter, @@ -22,7 +22,8 @@ class GoogleMapWidget extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - ref.listen(selectedPlaceProvider, (previous, next) async { + ref.listen(selectedPlaceProvider, + (previous, next) async { if (next != null) { try { final controller = await googleMapControllerCompleter.future; diff --git a/lib/feature/map/widget/PoliceStationListWidget.dart b/lib/feature/map/widget/PoliceStationListWidget.dart index 35b5661..cdf4048 100644 --- a/lib/feature/map/widget/PoliceStationListWidget.dart +++ b/lib/feature/map/widget/PoliceStationListWidget.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'package:blueberry_flutter_template/feature/map/provider/PoliceStationProvider.dart'; import 'package:blueberry_flutter_template/feature/map/provider/SeletedPlaceProvider.dart'; -import 'package:blueberry_flutter_template/model/GoogleMapPlace.dart'; +import 'package:blueberry_flutter_template/model/GoogleMapPlaceModel.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; @@ -10,7 +10,7 @@ import 'package:url_launcher/url_launcher.dart'; class PoliceStationListWidget extends ConsumerWidget { final LatLng locationState; final Completer googleMapControllerCompleter; - final AsyncValue> policeStationsAsyncValue; + final AsyncValue> policeStationsAsyncValue; const PoliceStationListWidget({ required this.locationState, diff --git a/lib/feature/match/MatchScreen.dart b/lib/feature/match/MatchScreen.dart deleted file mode 100644 index 2cc9edf..0000000 --- a/lib/feature/match/MatchScreen.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:flutter/material.dart'; -import '../../utils/AppStrings.dart'; -import 'widget/MatchProfileListWidget.dart'; - -/// MatchScreen - 프로필 스와이프 매칭 화면 -/// -/// 주요 구성 요소: -/// - SwipeCardWidget: 프로필 카드를 스와이프할 수 있는 위젯 -/// - SwipeButtonWidget: 수동으로 좌/우 스와이프를 할 수 있는 버튼 - -class MatchScreen extends StatelessWidget { - const MatchScreen({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: const Text(AppStrings.matchScreenTitle)), - body: const Padding( - padding: EdgeInsets.only(bottom: 36.0), - child: Center( - child: MatchProfileListWidget(), - ), - ), - ); - } -} diff --git a/lib/feature/match/ProfileScreen.dart b/lib/feature/match/ProfileScreen.dart deleted file mode 100644 index 20887da..0000000 --- a/lib/feature/match/ProfileScreen.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:blueberry_flutter_template/utils/AppTextStyle.dart'; -import 'package:flutter/material.dart'; - -import '../../model/DogProfileModel.dart'; - -class ProfileScreen extends StatelessWidget { - final DogProfileModel dogProfile; - - const ProfileScreen({super.key, required this.dogProfile}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(dogProfile.name), - ), - body: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Image.network( - dogProfile.imageUrl, - width: double.infinity, - height: 300, - fit: BoxFit.cover, - ), - Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - dogProfile.name, - style: black16BoldTextStyle, - ), - const SizedBox(height: 8), - Text('성별: ${dogProfile.gender}'), - Text('종족: ${dogProfile.breed}'), - Text('사는 곳: ${dogProfile.location}'), - const SizedBox(height: 16), - const Text( - '소개', - style: black16BoldTextStyle, - ), - Text(dogProfile.bio), - ], - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/feature/match/provider/DogProfileProvider.dart b/lib/feature/match/provider/DogProfileProvider.dart deleted file mode 100644 index 7c2ce81..0000000 --- a/lib/feature/match/provider/DogProfileProvider.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import '../../../model/DogProfileModel.dart'; - -final dogProfilesProvider = FutureProvider((ref) async { - final firestore = FirebaseFirestore.instance; - final snapshot = await firestore.collection('pet').get(); - return snapshot.docs - .map((doc) => DogProfileModel.fromJson(doc.data())) - .toList(); -}); diff --git a/lib/feature/match/widget/MatchProfileListWidget.dart b/lib/feature/match/widget/MatchProfileListWidget.dart deleted file mode 100644 index f5613b2..0000000 --- a/lib/feature/match/widget/MatchProfileListWidget.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:blueberry_flutter_template/model/DogProfileModel.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_card_swiper/flutter_card_swiper.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import '../provider/DogProfileProvider.dart'; -import '../ProfileScreen.dart'; -import 'SwipeButtonWidget.dart'; -import 'SwipeCardWidget.dart'; - -/// MatchProfileListWidget - 메인 위젯으로, SwipeCard, SwipeButton와 CardSwiperController을 통해 매칭 기능을 구현함 - -class MatchProfileListWidget extends ConsumerWidget { - const MatchProfileListWidget({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final list = ref.watch(dogProfilesProvider); - - return list.when( - data: (data) => _buildCardView(context, ref, data), - loading: () => const Center(child: CircularProgressIndicator()), - error: (error, stackTrace) => Center(child: Text('Error: $error')), - ); - } -} - -Widget _buildCardView( - BuildContext context, WidgetRef ref, List data) { - final cardSwiperController = CardSwiperController(); - final cards = data.map(SwipeCardWidget.new).toList(); - - return Column( - children: [ - Flexible( - child: CardSwiper( - controller: cardSwiperController, - cardsCount: cards.length, - onSwipe: (previousIndex, currentIndex, direction) { - if (direction == CardSwiperDirection.right) { - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => - ProfileScreen(dogProfile: data[previousIndex]), - ), - ); - } - return true; - }, - cardBuilder: ( - context, - index, - horizontalThresholdPercentage, - verticalThresholdPercentage, - ) => - cards[index], - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - SwipeButtonWidget( - onPressed: () => - cardSwiperController.swipe(CardSwiperDirection.left), - icon: Icons.close), - SwipeButtonWidget( - onPressed: () => - cardSwiperController.swipe(CardSwiperDirection.right), - icon: Icons.check), - ], - ), - ], - ); -} diff --git a/lib/feature/match/widget/SwipeButtonWidget.dart b/lib/feature/match/widget/SwipeButtonWidget.dart deleted file mode 100644 index c823aa4..0000000 --- a/lib/feature/match/widget/SwipeButtonWidget.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:flutter/material.dart'; - -/// MatchScreen - 수동으로 좌/우 스와이프를 할 수 있는 버튼 -/// -/// 추후 디자인이 들어갈 수 있기에 따로 빼놨음 - -class SwipeButtonWidget extends StatelessWidget { - final VoidCallback onPressed; - final IconData icon; - - const SwipeButtonWidget({ - super.key, - required this.onPressed, - required this.icon, - }); - - @override - Widget build(BuildContext context) { - return FloatingActionButton( - heroTag: UniqueKey(), - onPressed: onPressed, - child: Icon(icon), - // 여기에 커스텀 스타일 추가 - ); - } -} diff --git a/lib/feature/match/widget/SwipeCardWidget.dart b/lib/feature/match/widget/SwipeCardWidget.dart deleted file mode 100644 index d27a852..0000000 --- a/lib/feature/match/widget/SwipeCardWidget.dart +++ /dev/null @@ -1,76 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../../model/DogProfileModel.dart'; -import '../../../utils/AppColors.dart'; -import '../../../utils/AppTextStyle.dart'; - -/// SwipeCardWidget - 프로필 카드를 스와이프할 수 있는 위젯 - -class SwipeCardWidget extends StatelessWidget { - final DogProfileModel dogProfiles; - - const SwipeCardWidget( - this.dogProfiles, { - super.key, - }); - - @override - Widget build(BuildContext context) { - double height = MediaQuery.of(context).size.height; - double width = MediaQuery.of(context).size.width; - - return Card( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - ), - elevation: 5, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - image: DecorationImage( - image: NetworkImage(dogProfiles.imageUrl), - fit: BoxFit.cover, - ), - ), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - gradient: LinearGradient( - begin: Alignment.bottomCenter, - end: Alignment.topCenter, - colors: [richBlack.withOpacity(0.8), richBlack.withOpacity(0)], - ), - ), - child: Padding( - padding: EdgeInsets.symmetric( - vertical: height * 0.03, horizontal: width * 0.05), - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Text( - dogProfiles.name, - style: white24BoldTextStyle, - ), - SizedBox(width: width * 0.02), - Text( - '${dogProfiles.gender} • ${dogProfiles.breed} • ${dogProfiles.location}', - style: white16TextStyle, - ), - ], - ), - SizedBox(height: height * 0.005), - Text( - dogProfiles.bio, - style: white16TextStyle, - ), - ], - ), - ), - ), - ), - ); - } -} diff --git a/lib/feature/mbti/MBTIScreen.dart b/lib/feature/mbti/MBTIScreen.dart deleted file mode 100644 index 3d7032e..0000000 --- a/lib/feature/mbti/MBTIScreen.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:blueberry_flutter_template/feature/mbti/widget/MBTIHomeWidget.dart'; -import 'package:blueberry_flutter_template/utils/AppStrings.dart'; -import 'package:flutter/material.dart'; - -class MBTIScreen extends StatelessWidget { - static const String name = 'mbtiScreen'; - - const MBTIScreen({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: const Text(AppStrings.titleMBTI)), - body: const Center( - child: Expanded(child: MBTIHomeWidget()), - ), - ); - } -} diff --git a/lib/feature/mbti/MBTITestScreen.dart b/lib/feature/mbti/MBTITestScreen.dart deleted file mode 100644 index a66827c..0000000 --- a/lib/feature/mbti/MBTITestScreen.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:blueberry_flutter_template/utils/AppStrings.dart'; -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -import 'widget/MBTITestWidget.dart'; - -class MBTITestScreen extends StatelessWidget { - static const String name = 'MBTITestScreen'; - - const MBTITestScreen({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text(AppStrings.titleMBTITest), - leading: IconButton( - onPressed: () => context.pop(), - icon: const Icon(Icons.arrow_back))), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded(child: MBTITestWidget()), - ], - ), - ), - ); - } -} diff --git a/lib/feature/mbti/provider/MBTIProvider.dart b/lib/feature/mbti/provider/MBTIProvider.dart deleted file mode 100644 index 07e6a63..0000000 --- a/lib/feature/mbti/provider/MBTIProvider.dart +++ /dev/null @@ -1,129 +0,0 @@ -import 'package:blueberry_flutter_template/model/MBTIQuestionModel.dart'; -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_storage/firebase_storage.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import '../../../model/UserDataModel.dart'; -import '../../login/provider/UserInfoProvider.dart'; -import '../widget/MBTIHomeWidget.dart'; - -// 스트림으로 firebase에서 데이터를 가져오는 공급자 -final mbtiProvider = StreamProvider((ref) async* { - final userId = await ref.watch(userIdProvider.future); - if (userId == null) throw Exception('User not logged in'); - - yield* FirebaseFirestore.instance - .collection('users') - .doc(userId) - .snapshots() - .map((snapshot) { - if (snapshot.exists) { - return UserDataModel.fromJson(snapshot.data()!); - } else { - throw Exception('User not found'); - } - }); -}); - -final mbtiTestProvider = StateNotifierProvider((ref) { - return MBTINotifier(MBTIType.NULL); -}); - -final mbtiQuestionProvider = - StreamProvider.autoDispose>((ref) { - final firestore = FirebaseFirestore.instance; - return firestore.collection('mbtiQuestions').snapshots().map((snapshot) { - return snapshot.docs - .map((doc) => MBTIQuestionModel( - question: doc['Question'] as String, - type: doc['Type'] as String, - imageUrl: doc['ImageUrl'] as String, - )) - .toList(); - }); -}); - -Future fetchMBTIImageUrl(String imageName) async { - final ref = FirebaseStorage.instance.ref('mbti-image/$imageName.webp'); - return await ref.getDownloadURL(); -} - -final mbtiImageProvider = - FutureProvider.autoDispose.family((ref, imageName) async { - return await fetchMBTIImageUrl(imageName); -}); - -Future fetchMBTITestImageUrl(String imageName) async { - final ref = FirebaseStorage.instance.ref('mbti-question/$imageName.webp'); - return await ref.getDownloadURL(); -} - -final mbtiTestImageProvider = - FutureProvider.autoDispose.family((ref, imageName) async { - return await fetchMBTITestImageUrl(imageName); -}); - -class MBTINotifier extends StateNotifier { - MBTINotifier(super.state); - - List _mbtiScore = [0, 0, 0, 0]; - - void initScore() { - _mbtiScore = [0, 0, 0, 0]; - } - - MBTIType setMBTI() { - int type = 0; - if (_mbtiScore[0] >= 0) type += 8; - if (_mbtiScore[1] >= 0) type += 4; - if (_mbtiScore[2] >= 0) type += 2; - if (_mbtiScore[3] >= 0) type += 1; - - state = MBTIType.values[type]; - return state; - } - - void updateScore(String type, int addition) { - switch (type) { - case "E": - _mbtiScore[0] += addition; - break; - case "I": - _mbtiScore[0] -= addition; - break; - case "S": - _mbtiScore[1] += addition; - break; - case "N": - _mbtiScore[1] -= addition; - break; - case "T": - _mbtiScore[2] += addition; - break; - case "F": - _mbtiScore[2] -= addition; - break; - case "J": - _mbtiScore[3] += addition; - break; - case "P": - _mbtiScore[3] -= addition; - break; - } - } - - Future updateMBTI( - {required String userId, required MBTIType mbtiResult}) async { - try { - final updateData = {}; - updateData['mbti'] = mbtiResult.name; - - await FirebaseFirestore.instance - .collection('users') - .doc(userId) - .update(updateData); - } catch (e) { - print('Failed to update user data: $e'); - } - } -} diff --git a/lib/feature/mbti/widget/MBTIHomeWidget.dart b/lib/feature/mbti/widget/MBTIHomeWidget.dart deleted file mode 100644 index 0aee5b8..0000000 --- a/lib/feature/mbti/widget/MBTIHomeWidget.dart +++ /dev/null @@ -1,159 +0,0 @@ -import 'package:blueberry_flutter_template/feature/mbti/MBTIScreen.dart'; -import 'package:blueberry_flutter_template/feature/mbti/provider/MBTIProvider.dart'; -import 'package:blueberry_flutter_template/utils/AppStrings.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; - -import '../../../utils/AppColors.dart'; -import '../../../utils/AppTextStyle.dart'; -import 'MBTIShareDialogWidget.dart'; - -class MBTIHomeWidget extends ConsumerWidget { - const MBTIHomeWidget({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final userData = ref.watch(mbtiProvider); - - return userData.when( - data: (userData) { - final userName = userData.name; - final mbti = MBTIType.fromString(userData.mbti); - final imageState = ref.watch(mbtiImageProvider(mbti.name)); - - return imageState.when( - data: (imageUrl) { - return _buildMBTIHomeWidgetView( - context, ref, userName, mbti, imageUrl); - }, - loading: () => const Center(child: CircularProgressIndicator()), - error: (error, stackTrace) => - Center(child: Text(style: black16TextStyle, 'Error: $error')), - ); - }, - loading: () => const Center(child: CircularProgressIndicator()), - error: (error, stackTrace) => - _buildMBTIHomeWidgetView(context, ref, '', MBTIType.NULL, ''), - ); - } -} - -Widget _buildMBTIHomeWidgetView(BuildContext context, WidgetRef ref, - String userName, MBTIType mbti, String imageUrl) { - return Container( - padding: const EdgeInsets.all(8.0), - // MBTI 가 있으면 카드 모양 Decoration 제공 / 디자인 확정시 분리 - decoration: mbti != MBTIType.NULL - ? (BoxDecoration( - boxShadow: const [ - BoxShadow( - color: coolGray, - spreadRadius: 5, - blurRadius: 7, - offset: Offset(0, 3)) - ], - color: (MBTIType.toColor(mbti)), - borderRadius: BorderRadius.circular(20))) - // MBTI 없으면 null - : null, - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - textAlign: TextAlign.center, - style: black24TextStyle, - // mbti가 있으면 mbti 보여주기, 없으면 '검사해주세요' - mbti != MBTIType.NULL - ? '$userName${AppStrings.yourMBTI} ${mbti.name}' - : AppStrings.pleaseCheckMBTI), - imageUrl.isNotEmpty - // imageUrl이 있으면 이미지를 표시 - ? Image.network(imageUrl, height: 300) - // 네트워크 에러를 위한 로컬 이미지 사용 (mbti 디자인 완료시 변경) - : Image.asset('assets/logo/mbti_logo.webp', height: 300), - TextButton( - onPressed: () => { - ref.read(mbtiTestProvider.notifier).initScore(), - context.goNamed(MBTIScreen.name), - }, - child: Text( - style: black24TextStyle, - // mbti가 없으면 검사하기, 있으면 재검사하기 - mbti != MBTIType.NULL - ? AppStrings.reCheckMBTI - : AppStrings.checkMBTI)), - if (mbti != MBTIType.NULL) - TextButton( - onPressed: () => { - // 공유 Dialog 호출 - showDialog( - context: context, - builder: (BuildContext context) { - return const MBTIShareDialogWidget(); - }) - }, - child: const Text(style: black24TextStyle, AppStrings.shareMBTI)) - ], - ), - ); -} - -enum MBTIType { - INFP, - INFJ, - INTP, - INTJ, - ISFP, - ISFJ, - ISTP, - ISTJ, - ENFP, - ENFJ, - ENTP, - ENTJ, - ESFP, - ESFJ, - ESTP, - ESTJ, - NULL; - - static MBTIType fromString(String mbti) { - return MBTIType.values.firstWhere( - (type) => type.name.toUpperCase() == mbti.toUpperCase(), - orElse: () => MBTIType.NULL, - ); - } - - static Color toColor(MBTIType mbti) { - final mbtiColorList = [ - midnightGreen, - softBlue, - strongCyan, - deepPurple, - brightGreen, - amber, - midnightBlue, - coolGray, - pastelPurple, - neonGreen, - fireRed, - blue, - warmOrange, - palePink, - pastelPink, - pastelGreen, - richBlack - ]; - return mbtiColorList[MBTIType.values.indexOf(mbti)]; - } -} - -enum Extroversion { E, I } - -enum Sensing { S, N } - -enum Thinking { T, F } - -enum Judging { J, P } diff --git a/lib/feature/mbti/widget/MBTIResultWidget.dart b/lib/feature/mbti/widget/MBTIResultWidget.dart deleted file mode 100644 index 926eeb6..0000000 --- a/lib/feature/mbti/widget/MBTIResultWidget.dart +++ /dev/null @@ -1,81 +0,0 @@ -import 'package:blueberry_flutter_template/feature/mbti/provider/MBTIProvider.dart'; -import 'package:blueberry_flutter_template/feature/mbti/widget/MBTIHomeWidget.dart'; -import 'package:blueberry_flutter_template/utils/AppColors.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; - -import '../../../utils/AppStrings.dart'; -import '../../../utils/AppTextStyle.dart'; -import '../../../utils/DialogHelpers.dart'; - -class MBTIResultWidget extends ConsumerWidget { - final MBTIType mbtiResult; - - const MBTIResultWidget({super.key, required this.mbtiResult}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final mbtiImage = ref.watch(mbtiImageProvider(mbtiResult.name)); - - return mbtiImage.when( - data: (imageUrl) { - return _buildMBTIResultWidgetView(context, ref, mbtiResult, imageUrl); - }, - loading: () => const Center(child: CircularProgressIndicator()), - error: (error, stackTrace) => - Center(child: Text(style: black12BoldTextStyle, 'Error: $error'))); - } -} - -Widget _buildMBTIResultWidgetView( - BuildContext context, WidgetRef ref, MBTIType mbtiResult, String imageUrl) { - final userId = FirebaseAuth.instance.currentUser?.uid; - final userState = ref.read(mbtiTestProvider.notifier); - - return Center( - child: Container( - padding: const EdgeInsets.all(8.0), - decoration: BoxDecoration( - boxShadow: const [ - BoxShadow( - color: coolGray, - spreadRadius: 5, - blurRadius: 7, - offset: Offset(0, 3)) - ], - color: (MBTIType.toColor(mbtiResult)), - borderRadius: BorderRadius.circular(20)), - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - style: black16BoldTextStyle, - '${AppStrings.petMBTI} ${mbtiResult.name}'), - imageUrl.isNotEmpty - ? Image.network(imageUrl, height: 300) - : const Text(style: black12TextStyle, AppStrings.errorTitle), - if (userId != null) - TextButton( - onPressed: () => { - userState - .updateMBTI( - userId: userId, mbtiResult: mbtiResult) - .whenComplete(() => showSuccessDialog( - context, AppStrings.setCompleteMBTI)) - .catchError((error) => showErrorDialog( - context, AppStrings.setErrorMBTI)), - }, - child: - const Text(style: black16TextStyle, AppStrings.setMBTI)) - else - const Text(style: black16TextStyle, AppStrings.pleaseLogin), - TextButton( - onPressed: () => {context.pop()}, - child: const Text( - style: black16TextStyle, AppStrings.okButtonText)), - ], - ))); -} diff --git a/lib/feature/mbti/widget/MBTIShareDialogWidget.dart b/lib/feature/mbti/widget/MBTIShareDialogWidget.dart deleted file mode 100644 index f6a099e..0000000 --- a/lib/feature/mbti/widget/MBTIShareDialogWidget.dart +++ /dev/null @@ -1,124 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:kakao_flutter_sdk_share/kakao_flutter_sdk_share.dart'; - -import '../../../utils/AppTextStyle.dart'; - -class MBTIShareDialogWidget extends ConsumerWidget { - const MBTIShareDialogWidget({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - return Dialog( - child: Container( - width: MediaQuery.of(context).size.width * 0.3, - height: 200, - alignment: Alignment.center, - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - ElevatedButton( - onPressed: () { - shareOnKakao(); - }, - child: const Text(style: black16BoldTextStyle, 'KakaoTalk'), - ), - ElevatedButton( - onPressed: () { - Navigator.pop(context); - }, - child: const Text(style: black16BoldTextStyle, 'Link'), - ) - ], - ), - ElevatedButton( - onPressed: () { - Navigator.pop(context); - }, - child: const Text(style: black16BoldTextStyle, 'close'), - ), - ], - )), - ); - } - - Future shareOnKakao() async { - // bool isKakaoTalkSharingAvailable = await ShareClient.instance - // .isKakaoTalkSharingAvailable(); - // final kakaoShareTemplateId = kakaoShareTemplate; - - // if (isKakaoTalkSharingAvailable) { - // // 카카오톡 공유 - // try { - // Uri uri = - // await ShareClient.instance.shareCustom( - // templateId: kakaoShareTemplateId); - // await ShareClient.instance.launchKakaoTalk(uri); - // print('카카오톡 공유 완료'); - // } catch (error) { - // print('카카오톡 공유 실패 $error'); - // } - // } else { - // try { - // Uri shareUrl = await WebSharerClient.instance.makeCustomUrl( - // templateId: kakaoShareTemplateId, templateArgs: {'key1': 'value1'}); - // await launchBrowserTab(shareUrl, popupOpen: true); - // } catch (error) { - // print('카카오톡 공유 실패 $error'); - // } - // } - // else { - // 카카오톡 미설치 웹으로 공유 - // - // } - } -} - -// kakao Share Template -final FeedTemplate kakaoShareTemplate = FeedTemplate( - content: Content( - title: '펫팅 MBTI 공유하기!', - description: '#펫 #반려동물 #MBTI #동물 #강아지 #고양이 #소개팅', - imageUrl: Uri.parse('https://DefaultImage.png'), - link: Link( - webUrl: Uri.parse('https://DEFAULT_URL'), - mobileWebUrl: Uri.parse('https://DEFAULT_MOBILE_URL')), - ), - itemContent: ItemContent( - profileText: 'PETTING', - profileImageUrl: Uri.parse('https://PETTING_IMAGE2.png'), - titleImageUrl: Uri.parse('https://PETTING_IMAGE3.png'), - titleImageText: 'Pet cake', - titleImageCategory: 'petCake', - items: [ - ItemInfo(item: 'pet_dog', itemOp: '1등'), - ItemInfo(item: 'pet_cat', itemOp: '2등'), - ItemInfo(item: 'pet_lizard', itemOp: '3등'), - ItemInfo(item: 'pet_fish', itemOp: '4등'), - ItemInfo(item: 'pet_hamster', itemOp: '5등') - ], - sum: 'total', - sumOp: '15000원', - ), - social: Social(likeCount: 286, commentCount: 45, sharedCount: 845), - buttons: [ - Button( - title: '웹으로 보기', - link: Link( - webUrl: Uri.parse('https: //developers.kakao.com'), - mobileWebUrl: Uri.parse('https: //developers.kakao.com'), - ), - ), - Button( - title: '앱으로보기', - link: Link( - androidExecutionParams: {'key1': 'value1', 'key2': 'value2'}, - iosExecutionParams: {'key1': 'value1', 'key2': 'value2'}, - ), - ), - ], -); diff --git a/lib/feature/mbti/widget/MBTITestWidget.dart b/lib/feature/mbti/widget/MBTITestWidget.dart deleted file mode 100644 index aed3078..0000000 --- a/lib/feature/mbti/widget/MBTITestWidget.dart +++ /dev/null @@ -1,109 +0,0 @@ -import 'package:blueberry_flutter_template/feature/mbti/provider/MBTIProvider.dart'; -import 'package:blueberry_flutter_template/feature/mbti/widget/MBTIHomeWidget.dart'; -import 'package:blueberry_flutter_template/feature/mbti/widget/MBTIResultWidget.dart'; -import 'package:blueberry_flutter_template/model/MBTIQuestionModel.dart'; -import 'package:blueberry_flutter_template/utils/AppStringEnglish.dart'; -import 'package:blueberry_flutter_template/utils/AppStrings.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import '../../../utils/AppTextStyle.dart'; - -class MBTITestWidget extends ConsumerWidget { - final pageController = PageController(); - - final _buttonTexts = [ - AppStrings.stronglyAgree, - AppStrings.agree, - AppStrings.neutral, - AppStrings.disagree, - AppStrings.stronglyDisagree, - ]; - - MBTITestWidget({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final mbtiQuestionList = ref.watch(mbtiQuestionProvider); - return mbtiQuestionList.when( - data: (data) => Column( - children: [ - Expanded( - child: _buildMBTITestPageWidgetView(pageController, data)), - _buildMBTITestWidgetView( - pageController, ref, _buttonTexts, data), - ], - ), - loading: () => const Center(child: CircularProgressIndicator()), - error: (error, stackTrace) => Center( - child: Text( - style: black12BoldTextStyle, - '${AppStringEnglish.errorTitle}: $error'))); - } -} - -Widget _buildMBTITestPageWidgetView( - PageController pageController, List questions) { - return PageView.builder( - controller: pageController, - itemCount: questions.length, - itemBuilder: (BuildContext context, int index) { - return Center( - child: Column( - children: [ - Text( - questions[index].question, - style: black24BoldTextStyle, - textAlign: TextAlign.center, - ), - Image.network( - questions[index].imageUrl, - width: double.infinity, - height: 300, - fit: BoxFit.fitHeight, - ) - ], - ), - ); - }, - ); -} - -Widget _buildMBTITestWidgetView(PageController pageController, WidgetRef ref, - List buttonText, List data) { - return ListView.builder( - shrinkWrap: true, - itemCount: buttonText.length, - itemBuilder: (BuildContext context, int index) { - return ListTile( - title: TextButton( - onPressed: () { - // 페이지 전환 중 버튼 입력 차단 - if (pageController.page != pageController.page?.ceilToDouble()) { - return; - } - ref.watch(mbtiTestProvider.notifier).updateScore( - data[pageController.page!.toInt()].type, 2 - index); - // 마지막 페이지 일시 결과 호출 - if (pageController.page == data.length - 1) { - MBTIType result = ref.watch(mbtiTestProvider.notifier).setMBTI(); - Navigator.pop(context); - showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext context) { - return MBTIResultWidget(mbtiResult: result); - }); - } else { - pageController.nextPage( - duration: const Duration(seconds: 1), - curve: Curves.decelerate, - ); - } - }, - child: Text(style: black24TextStyle, buttonText[index]), - ), - ); - }, - ); -} diff --git a/lib/feature/mypage/MyPageScreen.dart b/lib/feature/mypage/MyPageScreen.dart index 22967dd..fdc0b69 100644 --- a/lib/feature/mypage/MyPageScreen.dart +++ b/lib/feature/mypage/MyPageScreen.dart @@ -1,315 +1,54 @@ -import 'dart:io'; -import 'package:blueberry_flutter_template/feature/mypage/provider/ProfileImageProvider.dart'; -import 'package:cloud_functions/cloud_functions.dart'; -import 'package:easy_engine/easy_engine.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; -import 'package:image_picker/image_picker.dart'; -import '../../core/widget/CustomDividerWidget.dart'; -import '../../core/widget/NickNameTextWidget.dart'; -import '../../services/FirebaseAuthServiceProvider.dart'; -import '../../services/FirebaseStoreServiceProvider.dart'; -import '../../utils/AppStrings.dart'; -import '../camera/SettingInsideAccountManagerWidget.dart'; -import '../camera/SettingsBottomSheet.dart'; -import '../camera/provider/fireStorageServiceProvider.dart'; -import '../setting/SettingScreen.dart'; +class MyPageScreen extends StatelessWidget { + static const name = 'MyPageScreen'; -class MyPageScreen extends ConsumerWidget { - static const String name = 'MyPageScreen'; const MyPageScreen({super.key}); @override - Widget build(BuildContext context, WidgetRef ref) { - final profileImage = ref.watch(profileImageStreamProvider); - + Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text(AppStrings.myPageTitle), + title: const Text('마이페이지'), + centerTitle: true, ), body: Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.all(16.0), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - Row( - children: [ - profileImageStack(profileImage, ref, context), - const Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - NickNameTextWidget(), - Text("Google 로그인을 사용 중 입니다.") - ], - ), - ) - ], - ), - const SizedBox( - height: 40, - ), - const CustomDividerWidget(), - GestureDetector( - onTap: () { - context.goNamed(SettingAccountManagerWidget.name); - }, - child: const ListTile( - leading: Icon(Icons.person), - title: Text( - "관리", - style: TextStyle(fontSize: 20), - ), - ), - ), - const CustomDividerWidget(), - GestureDetector( - onTap: () {}, - child: const ListTile( - leading: Icon(Icons.card_membership), - title: Text( - "결제 정보", - style: TextStyle(fontSize: 20), - ), - ), - ), - const CustomDividerWidget(), - GestureDetector( - onTap: () {}, - child: const ListTile( - leading: Icon(Icons.alarm_add_outlined), - title: Text( - "알림", - style: TextStyle(fontSize: 20), - ), - ), - ), - const CustomDividerWidget(), - GestureDetector( - onTap: () {}, - child: const ListTile( - leading: Icon(Icons.lock), - title: Text( - "개인 / 보안", - style: TextStyle(fontSize: 20), - ), - ), - ), - const CustomDividerWidget(), - GestureDetector( - onTap: () {}, - child: const ListTile( - leading: Icon(Icons.monitor), - title: Text( - "테마", - style: TextStyle(fontSize: 20), - ), - ), - ), - const CustomDividerWidget(), - GestureDetector( - onTap: () {}, - child: const ListTile( - leading: Icon(Icons.chat_bubble_outline), - title: Text( - "채팅 / 미디어", - style: TextStyle(fontSize: 20), - ), - ), - ), - const SizedBox( - height: 30, - ), - const CustomDividerWidget(), - GestureDetector( - onTap: () { - context.goNamed(SettingScreen.name); + const Text( + "nickname", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 24.0, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 40.0), + ElevatedButton( + onPressed: () { + // 발송 문자 지정 버튼의 기능 구현 }, - child: const ListTile( - leading: Icon(Icons.notifications), - title: Text( - "설정", - style: TextStyle(fontSize: 20), - ), - ), + child: const Text('발송 문자 지정'), ), - const CustomDividerWidget(), - - //Logout button - GestureDetector( - onTap: () { - ref.read(firebaseAuthServiceProvider).signOut(); + const SizedBox(height: 16.0), + ElevatedButton( + onPressed: () { + // 주변 범죄 위험도 확인 버튼의 기능 구현 }, - child: const ListTile( - leading: Icon(Icons.logout), - title: Text( - "로그아웃", - style: TextStyle(fontSize: 20), - ), - ), + child: const Text('주변 범죄 위험도 확인'), ), - - //Logout button - GestureDetector( - onTap: () async { - try { - final re = await engine.deleteAccount(); - debugPrint(re.toString()); - if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('회원탈퇴가 완료되었습니다.'), - ), - ); - } - ref.read(firebaseAuthServiceProvider).signOut(); - } on FirebaseFunctionsException catch (e) { - if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Error: ${e.code}/${e.message}'), - ), - ); - } - } catch (e) { - if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - 'Error: $e'), // e.code: internal, e.message: INTERNAL - ), - ); - } - } + const SizedBox(height: 16.0), + ElevatedButton( + onPressed: () { + // 자동 대화 시간 설정 버튼의 기능 구현 }, - child: const ListTile( - leading: Icon(Icons.person_off), - title: Text( - "회원탈퇴", - style: TextStyle(fontSize: 20), - ), - ), + child: const Text('자동 대화 시간 설정'), ), ], ), ), ); } - - Stack profileImageStack( - AsyncValue profileImage, WidgetRef ref, BuildContext context) { - return Stack( - alignment: Alignment.center, - children: [ - profileImage.when( - data: (imageUrl) => Container( - width: 100, - height: 100, - clipBehavior: Clip.hardEdge, - decoration: const BoxDecoration(shape: BoxShape.circle), - child: Image.network( - imageUrl, - fit: BoxFit.cover, - errorBuilder: (context, _, __) { - return CircleAvatar( - radius: 50, - backgroundColor: Colors.grey.shade300, - ); - }, - ), - ), - loading: () => const CircularProgressIndicator(), - error: (e, s) => CircleAvatar( - radius: 50, - backgroundColor: Colors.grey.shade300, - ), - ), - Positioned( - right: 0, - bottom: 0, - child: kIsWeb - ? Container( - decoration: BoxDecoration( - color: Colors.white, // 아이콘 배경색으로 흰색을 사용합니다. - shape: BoxShape.circle, // 원형으로 배경을 설정합니다. - border: Border.all( - color: Colors.grey.shade300, // 테두리 색상 설정 - width: 2, // 테두리 두께 설정 - ), - ), - child: _uploadProfileImageButtons( - ref.read(firebaseStoreServiceProvider), - ref.read(fireStorageServiceProvider), - context, - ), - ) - : IconButton( - onPressed: () { - showModalBottomSheet( - context: context, - builder: (context) { - return Padding( - padding: MediaQuery.of(context).viewInsets, - child: const SizedBox( - height: 150, - child: SettingsBottomSheet(), - ), - ); - }); - }, - icon: const Icon(Icons.upload_file), - ), - ), - ], - ); - } -} - -Widget _uploadProfileImageButtons(FirestoreService firestoreService, - FirebaseStorageService firebaseStorageService, BuildContext context) { - final userId = FirebaseAuth.instance.currentUser!.uid; - - return IconButton( - onPressed: () async { - try { - var imageUrl = ''; - - if (kIsWeb) { - final ImagePicker picker = ImagePicker(); - final XFile? image = - await picker.pickImage(source: ImageSource.gallery); - - image?.readAsBytes().then((value) async { - imageUrl = await firebaseStorageService.uploadImageFromWeb( - value, ImageType.profileimage, - fixedFileName: userId); - - firestoreService.createProfileIamge(userId, imageUrl); - }); - } - if (!kIsWeb) { - final pickedFile = - await ImagePicker().pickImage(source: ImageSource.gallery); - if (pickedFile != null) { - // 선택된 이미지를 Firebase Storage에 업로드 - imageUrl = await firebaseStorageService.uploadImageFromApp( - File(pickedFile.path), ImageType.profileimage, - fixedFileName: userId); - - firestoreService.createProfileIamge(userId, imageUrl); - } - } - if (imageUrl != '') { - print('Banner created successfully'); - } else { - throw Exception('Cancel to upload image'); - } - } catch (e) {} - }, - icon: const Icon(Icons.settings), - ); } diff --git a/lib/feature/mypage/PasswordResetPage.dart b/lib/feature/mypage/PasswordResetPage.dart deleted file mode 100644 index 4c71f90..0000000 --- a/lib/feature/mypage/PasswordResetPage.dart +++ /dev/null @@ -1,84 +0,0 @@ -import 'package:app_links/app_links.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -final GlobalKey navigatorKey = GlobalKey(); - -class PasswordResetPage extends StatefulWidget { - const PasswordResetPage({ - super.key, - required this.params, - }); - - final Map params; - - @override - State createState() => _PasswordResetPageState(); -} - -class _PasswordResetPageState extends State { - final newPasswordController = TextEditingController(); - - @override - void initState() { - if (kIsWeb == false) { - final appLinks = AppLinks(); // AppLinks is singleton - - /// Subscribe to all events (initial link and further) - appLinks.uriLinkStream.listen((uri) { - if (uri.queryParameters['mode'] == 'resetPassword') { - Navigator.of(navigatorKey.currentState!.context).push( - MaterialPageRoute( - builder: (context) => PasswordResetPage( - params: uri.queryParameters, - ), - ), - ); - } - }); - } - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('PasswordReset'), - ), - body: Column( - children: [ - const Text("PasswordReset"), - Text('oobCode: ${widget.params['oobCode']}'), - Text('apikey: ${widget.params['apiKey']}'), - Text('mode: ${widget.params['mode']}'), - Text('continueUrl: ${widget.params['continueUrl']}'), - TextField( - controller: newPasswordController, - decoration: const InputDecoration(labelText: 'New password'), - ), - ElevatedButton( - onPressed: () async { - await FirebaseAuth.instance.confirmPasswordReset( - code: widget.params['oobCode']!, - newPassword: newPasswordController.text, - ); - if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text( - 'Password has been reset. Please sing-in with new password!', - ), - ), - ); - Navigator.of(context).pop(); - } - }, - child: const Text('Reset password'), - ), - ], - ), - ); - } -} diff --git a/lib/feature/mypage/provider/MyPageNameProvider.dart b/lib/feature/mypage/provider/MyPageNameProvider.dart deleted file mode 100644 index b05e8c4..0000000 --- a/lib/feature/mypage/provider/MyPageNameProvider.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -final myPageNicknameProvider = StreamProvider((ref) { - final firestore = FirebaseFirestore.instance; - final userId = FirebaseAuth.instance.currentUser!.uid; - - return firestore.collection('users').doc(userId).snapshots().map((snapshot) { - return snapshot['name'] as String; - }); -}); diff --git a/lib/feature/mypage/provider/ProfileImageProvider.dart b/lib/feature/mypage/provider/ProfileImageProvider.dart deleted file mode 100644 index 84e3fc0..0000000 --- a/lib/feature/mypage/provider/ProfileImageProvider.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -/// ProfileImageProvider.dart -/// -/// Profile Image Provider -/// - 프로필 이미지 데이터를 제공하는 Provider -/// - profileImageUrlStream(): 프로필 이미지 데이터 불러오기 -/// -/// @jwson-automation -Stream profileImageUrlStream() { - final userId = FirebaseAuth - .instance.currentUser!.uid; // 안전하게 처리를 위해 non-null assertion 사용 - - return FirebaseFirestore.instance - .collection('profileImages') - .doc(userId) - .snapshots() - .map((snapshot) { - if (snapshot.exists && snapshot.data()!.containsKey('imageUrl')) { - return snapshot.data()!['imageUrl'] as String; // 이미지 URL 반환 - } else { - return 'default_image_url'; // 문서 또는 필드가 존재하지 않는 경우 기본 URL 반환 - } - }); -} - -// 스트림 프로바이더 선언 -final profileImageStreamProvider = StreamProvider((ref) { - return profileImageUrlStream(); -}); diff --git a/lib/feature/onboarding/OnboardingScreen.dart b/lib/feature/onboarding/OnboardingScreen.dart index b09a38d..60a76a5 100644 --- a/lib/feature/onboarding/OnboardingScreen.dart +++ b/lib/feature/onboarding/OnboardingScreen.dart @@ -8,6 +8,8 @@ import 'widgets/OnboardingDotWidget.dart'; import 'widgets/OnboardingPageButton.dart'; class OnboardingScreen extends StatefulWidget { + static const String name = '/onboarding'; + const OnboardingScreen({super.key}); @override diff --git a/lib/feature/payment/provider/UserMemberShipProvider.dart b/lib/feature/payment/provider/UserMemberShipProvider.dart deleted file mode 100644 index 6f8bf9d..0000000 --- a/lib/feature/payment/provider/UserMemberShipProvider.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -final userMemberShipProvider = - StateNotifierProvider( - (ref) => UserMembershipNotifier(), -); - -class UserMembershipNotifier extends StateNotifier { - UserMembershipNotifier() : super(UserMemberShipState()); - - Future loadMembershipStatus() async { - if (state.isLoaded) return; // 이미 로드되었다면 다시 로드하지 않습니다. - - try { - var user = FirebaseAuth.instance.currentUser; - if (user != null) { - var doc = await FirebaseFirestore.instance - .collection('users') - .doc(user.uid) - .get(); - if (doc.exists) { - final isMembership = doc.data()!['isMembership'] as bool; - state = state.copyWith(isMembership: isMembership, isLoaded: true); - } - } - } catch (e) { - print('Error checking user membership: $e'); - // 에러 처리를 여기에 추가할 수 있습니다. - } - } - - void updateMembership(bool isMembership) { - state = state.copyWith(isMembership: isMembership); - } -} - -class UserMemberShipState { - final bool isMembership; - final bool isLoaded; - - UserMemberShipState({this.isMembership = false, this.isLoaded = false}); - - UserMemberShipState copyWith({bool? isMembership, bool? isLoaded}) { - return UserMemberShipState( - isMembership: isMembership ?? this.isMembership, - isLoaded: isLoaded ?? this.isLoaded, - ); - } -} diff --git a/lib/feature/payment/widget/WebPaymentWidget.dart b/lib/feature/payment/widget/WebPaymentWidget.dart deleted file mode 100644 index 103a86f..0000000 --- a/lib/feature/payment/widget/WebPaymentWidget.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:blueberry_flutter_template/feature/login/provider/UserInfoProvider.dart'; -import 'package:blueberry_flutter_template/services/PaymentService.dart'; -import 'package:blueberry_flutter_template/services/PredefinedItems.dart'; -import 'package:bootpay/model/payload.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -class WebPaymentWidget extends ConsumerStatefulWidget { - static const String name = 'WebPaymentWidget'; - const WebPaymentWidget({super.key}); - - @override - ConsumerState createState() => _WebPaymentWidgetState(); -} - -class _WebPaymentWidgetState extends ConsumerState { - Payload payload = Payload(); - - @override - Widget build(BuildContext context) { - final userDataState = ref.watch(getUserDataProvider); - - return Column( - children: [ - Center( - child: userDataState.when( - data: (userData) => TextButton( - onPressed: () => PaymentService().requestPayment(userData.name, - userData.email, PredefinedItems.premiumMembership), - child: const Text('통합결제 테스트', style: TextStyle(fontSize: 16.0))), - loading: () => const CircularProgressIndicator(), - error: (error, stack) => Text('Error: $error'), - )) - ], - ); - } -} diff --git a/lib/feature/phoneauth/ConfirmationPage.dart b/lib/feature/phoneauth/ConfirmationPage.dart deleted file mode 100644 index e9a9739..0000000 --- a/lib/feature/phoneauth/ConfirmationPage.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'dart:async'; - -import 'package:blueberry_flutter_template/feature/signup/provider/SignUpDataProviders.dart'; -import 'package:blueberry_flutter_template/services/FirebaseAuthServiceProvider.dart'; -import 'package:blueberry_flutter_template/services/FirebaseService.dart'; -import 'package:blueberry_flutter_template/utils/AppStrings.dart'; -import 'package:firebase_analytics/firebase_analytics.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; - -import '../../core/TopScreen.dart'; - -final signUpProvider = FutureProvider((ref) async { - // Simulate a network request with a 2-second delay - await Future.delayed(const Duration(seconds: 2)); - return true; -}); - -class ConfirmationPage extends ConsumerWidget { - final VoidCallback onNext; - - const ConfirmationPage({super.key, required this.onNext}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final email = ref.watch(emailProvider); - final name = ref.watch(nameProvider); - final passwordConfirm = ref.watch(passwordConfirmProvider); - final nickname = ref.watch(nicknameProvider); - final isLoading = ref.watch(signUpProvider); - final firebaseService = FirebaseService(); - - return Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text('이메일: $email'), - const SizedBox(height: 20), - Text('이름: $name'), - const SizedBox(height: 20), - Text('닉네임: $nickname'), - const SizedBox(height: 20), - Row( - children: [ - Checkbox(value: true, onChanged: (value) {}), - const Text('개인정보 처리방침에 동의합니다.'), - ], - ), - Row( - children: [ - Checkbox(value: true, onChanged: (value) {}), - const Text('이용약관에 동의합니다.'), - ], - ), - const SizedBox(height: 20), - isLoading.when( - data: (value) => ElevatedButton( - onPressed: () async { - await ref - .watch(firebaseAuthServiceProvider) - .signUpWithEmailPassword(email, passwordConfirm); - // 오류 뱉어내는거 하나 만들어야함 ex ) ID or Password 형식에 문제가 있다라고 쏴야할듯 ? - await firebaseService.upDateUserDB(email, name); - FirebaseAnalytics.instance.logSignUp(signUpMethod: 'email'); - if (context.mounted) { - context.goNamed(TopScreen.name); - } - }, - child: const Text(AppStrings.signUpButtonText), - ), - loading: () => const CircularProgressIndicator(), - error: (error, stackTrace) => Text('Error: $error'), - ), - ], - ), - ); - } -} diff --git a/lib/feature/phoneauth/PhoneVerificationPage.dart b/lib/feature/phoneauth/PhoneVerificationPage.dart deleted file mode 100644 index ba8c315..0000000 --- a/lib/feature/phoneauth/PhoneVerificationPage.dart +++ /dev/null @@ -1,96 +0,0 @@ -import 'package:blueberry_flutter_template/feature/phoneauth/provider/PhoneNumberInputWidget.dart'; -import 'package:blueberry_flutter_template/feature/phoneauth/provider/VerificationCodeInputWidget.dart'; -import 'package:flutter/material.dart'; - -class PhoneNumberInputPage extends StatelessWidget { - final VoidCallback onNext; - final VoidCallback onDone; - - const PhoneNumberInputPage({ - super.key, - required this.onNext, - required this.onDone, - }); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(16), - child: PhoneNumberInputWidget(onNext: onNext, onDone: onDone), - ); - } -} - -class VerificationCodeInputPage extends StatefulWidget { - final VoidCallback onNext; - final VoidCallback onPrev; - - const VerificationCodeInputPage({ - super.key, - required this.onNext, - required this.onPrev, - }); - - @override - _VerificationCodeInputPageState createState() => - _VerificationCodeInputPageState(); -} - -class _VerificationCodeInputPageState extends State { - final FocusNode _focusNode = FocusNode(); - - @override - void initState() { - super.initState(); - WidgetsBinding.instance.addPostFrameCallback((_) { - FocusScope.of(context).requestFocus(_focusNode); - }); - } - - @override - void dispose() { - _focusNode.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(16), - child: VerificationCodeInputWidget( - focusNode: _focusNode, - onNext: widget.onNext, - onPrev: widget.onPrev, - ), - ); - } -} - -class PhoneVerificationDonePage extends StatelessWidget { - final VoidCallback onNext; - - const PhoneVerificationDonePage({ - super.key, - required this.onNext, - }); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(16), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('인증 완료'), - const SizedBox( - height: 20, - ), - ElevatedButton( - onPressed: () => onNext(), - child: const Text('확인'), - ), - ], - ), - ); - } -} diff --git a/lib/feature/phoneauth/PhoneVerificationScreen.dart b/lib/feature/phoneauth/PhoneVerificationScreen.dart deleted file mode 100644 index a372593..0000000 --- a/lib/feature/phoneauth/PhoneVerificationScreen.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'PhoneVerificationPage.dart'; - -final PageController _pageController = PageController(); - -class PhoneVerificationScreen extends StatelessWidget { - const PhoneVerificationScreen({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - body: PageView( - controller: _pageController, - physics: const NeverScrollableScrollPhysics(), - children: [ - PhoneNumberInputPage( - onNext: () => _pageController.nextPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ), - onDone: () => _pageController.animateToPage( - 2, - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ), - ), - VerificationCodeInputPage( - onNext: () => _pageController.nextPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ), - onPrev: () => _pageController.previousPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ), - ), - PhoneVerificationDonePage( - onNext: () {}, - ), - ], - ), - ); - } -} diff --git a/lib/feature/phoneauth/provider/PhoneNumberInputWidget.dart b/lib/feature/phoneauth/provider/PhoneNumberInputWidget.dart deleted file mode 100644 index 3507cdd..0000000 --- a/lib/feature/phoneauth/provider/PhoneNumberInputWidget.dart +++ /dev/null @@ -1,107 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import '../../../utils/AppStrings.dart'; -import '../../phoneauth/provider/PhoneNumberProvider.dart'; -import '../../phoneauth/provider/PhoneVerificationProvider.dart'; - -class PhoneNumberInputWidget extends ConsumerWidget { - final VoidCallback onNext; - final VoidCallback onDone; - - const PhoneNumberInputWidget({ - super.key, - required this.onNext, - required this.onDone, - }); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final TextEditingController controller = TextEditingController(); - final phoneNumber = ref.watch(phoneNumberProvider.notifier); - - WidgetsBinding.instance.addPostFrameCallback((_) { - controller.text = ref.read(phoneNumberProvider); - }); - - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text(AppStrings.inputPhoneNumber), - const SizedBox(height: 20), - TextField( - controller: controller, - autofocus: true, - decoration: const InputDecoration( - labelText: '전화번호', - border: OutlineInputBorder( - borderSide: BorderSide(), - ), - ), - maxLength: 11, - buildCounter: (context, - {required currentLength, - required isFocused, - required maxLength}) => - null, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - keyboardType: TextInputType.number, - onChanged: (value) => phoneNumber.state = value, - ), - const SizedBox(height: 20), - ElevatedButton( - onPressed: () async { - if (phoneNumber.state.isEmpty) { - _showMessageDialog( - context, AppStrings.errorMessage_emptyPhoneNumber); - return; - } - - final completer = Completer(); - - ref - .read(phoneVerificationProvider.notifier) - .sendPhoneNumber(phoneNumber.state, completer); - - try { - await completer.future; - - final state = ref.read(phoneVerificationProvider); - if (state is CodeSent) { - onNext(); - } else if (state is Verified) { - onDone(); - } - } catch (e) { - _showMessageDialog( - context, AppStrings.errorMessage_invalidPhoneNumber); - } - }, - child: const Text('다음'), - ), - ], - ); - } -} - -void _showMessageDialog(BuildContext context, String message) { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: Text(message), - actions: [ - TextButton( - child: const Text('확인'), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ], - ); - }, - ); -} diff --git a/lib/feature/phoneauth/provider/PhoneNumberProvider.dart b/lib/feature/phoneauth/provider/PhoneNumberProvider.dart deleted file mode 100644 index ffa5279..0000000 --- a/lib/feature/phoneauth/provider/PhoneNumberProvider.dart +++ /dev/null @@ -1,3 +0,0 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -final phoneNumberProvider = StateProvider((ref) => ''); diff --git a/lib/feature/phoneauth/provider/PhoneVerificationProvider.dart b/lib/feature/phoneauth/provider/PhoneVerificationProvider.dart deleted file mode 100644 index 1423297..0000000 --- a/lib/feature/phoneauth/provider/PhoneVerificationProvider.dart +++ /dev/null @@ -1,124 +0,0 @@ -import 'dart:async'; - -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -final phoneVerificationProvider = - NotifierProvider(() { - return PhoneVerificationNotifier(); -}); - -class PhoneVerificationNotifier extends Notifier { - @override - PhoneVerificationState build() { - return Initial(); - } - - void reset() { - state = Initial(); - } - - Future sendPhoneNumber( - String phoneNumber, Completer completer) async { - try { - await FirebaseAuth.instance.verifyPhoneNumber( - timeout: const Duration(seconds: 120), - phoneNumber: '+82 $phoneNumber', - verificationCompleted: (PhoneAuthCredential credential) async { - final auth = FirebaseAuth.instance; - final authCredential = await auth.signInWithCredential(credential); - - if (authCredential.user != null) { - auth.currentUser!.delete(); - auth.signOut(); - state = Verified(); - completer.complete(); - } - }, - verificationFailed: (FirebaseAuthException e) { - state = Failed(message: e.toString()); - completer.completeError(e.toString()); - }, - codeSent: (String verificationId, int? resendToken) { - state = CodeSent(verificationId, resendToken: resendToken); - completer.complete(); - startCodeInputTimeout(); - }, - codeAutoRetrievalTimeout: (String verificationId) { - if (!completer.isCompleted) { - state = TimeOut(verificationId: verificationId); - completer.completeError('Timeout'); - } - }, - ); - } catch (e) { - state = Failed(message: e.toString()); - completer.completeError(e.toString()); - } - } - - void startCodeInputTimeout() { - Future.delayed(const Duration(seconds: 120), () { - if (state is CodeSent) { - state = TimeOut(verificationId: ''); - } - }); - } - - Future verifyCode( - String verificationCode, Completer completer) async { - final verificationId = state.verificationId; - - if (state is CodeSent || state is Failed) { - final credential = PhoneAuthProvider.credential( - verificationId: verificationId!, - smsCode: verificationCode, - ); - - try { - final auth = FirebaseAuth.instance; - final authResult = await auth.signInWithCredential(credential); - if (authResult.user != null) { - auth.currentUser!.delete(); - auth.signOut(); - state = Verified(); - completer.complete(); - } else { - state = Failed( - message: 'invalid-credential', verificationId: verificationId); - completer.completeError('invalid-credential'); - } - } catch (e) { - state = Failed(message: e.toString(), verificationId: verificationId); - completer.completeError(e.toString()); - } - } - } -} - -abstract class PhoneVerificationState { - final String? verificationId; - - PhoneVerificationState({this.verificationId}); -} - -class Initial extends PhoneVerificationState {} - -class CodeSent extends PhoneVerificationState { - final int? resendToken; - - CodeSent(String verificationId, {this.resendToken}) - : super(verificationId: verificationId); -} - -class TimeOut extends PhoneVerificationState { - TimeOut({super.verificationId}); -} - -class Verified extends PhoneVerificationState {} - -class Failed extends PhoneVerificationState { - final String? message; - - Failed({this.message, super.verificationId}); -} diff --git a/lib/feature/phoneauth/provider/VerificationCodeInputWidget.dart b/lib/feature/phoneauth/provider/VerificationCodeInputWidget.dart deleted file mode 100644 index 3e764ef..0000000 --- a/lib/feature/phoneauth/provider/VerificationCodeInputWidget.dart +++ /dev/null @@ -1,120 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:pinput/pinput.dart'; - -import '../../../utils/AppStrings.dart'; -import '../../phoneauth/provider/PhoneVerificationProvider.dart'; -import '../../phoneauth/provider/VerificationCodeProvider.dart'; - -class VerificationCodeInputWidget extends ConsumerWidget { - final FocusNode focusNode; - final VoidCallback onNext; - final VoidCallback onPrev; - final int length; - - const VerificationCodeInputWidget({ - super.key, - required this.focusNode, - required this.onNext, - required this.onPrev, - this.length = 6, - }); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final verificationCode = ref.watch(verificationCodeProvider.notifier); - final phoneVerification = ref.watch(phoneVerificationProvider); - - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text(AppStrings.inputVerificationCode), - const SizedBox( - height: 20, - ), - Pinput( - enabled: phoneVerification is TimeOut ? false : true, - focusNode: focusNode, - autofocus: true, - showCursor: true, - length: length, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - onChanged: (value) => verificationCode.state = value, - ), - phoneVerification is TimeOut - ? const Text( - AppStrings.errorMessage_timeOut, - style: TextStyle( - color: Colors.red, - ), - ) - : Container(), - const SizedBox( - height: 20, - ), - phoneVerification is TimeOut - ? ElevatedButton( - onPressed: () { - ref.read(phoneVerificationProvider.notifier).reset(); - onPrev(); - }, - child: const Text('재시도'), - ) - : ElevatedButton( - onPressed: () async { - if (verificationCode.state.isEmpty) { - _showMessageDialog( - context, AppStrings.errorMessage_emptyVerificationCode); - return; - } - - final completer = Completer(); - - ref - .read(phoneVerificationProvider.notifier) - .verifyCode(verificationCode.state, completer); - - try { - await completer.future; - - final state = ref.read(phoneVerificationProvider); - if (state is Verified) { - onNext(); - } else { - _showMessageDialog( - context, AppStrings.errorMessage_commonError); - } - } catch (e) { - _showMessageDialog(context, - AppStrings.errorMessage_invalidVerificationCode); - FocusScope.of(context).requestFocus(focusNode); - } - }, - child: const Text('확인'), - ), - ], - ); - } -} - -void _showMessageDialog(BuildContext context, String message) { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: Text(message), - actions: [ - TextButton( - child: const Text('확인'), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ], - ); - }, - ); -} diff --git a/lib/feature/phoneauth/provider/VerificationCodeProvider.dart b/lib/feature/phoneauth/provider/VerificationCodeProvider.dart deleted file mode 100644 index 307a04b..0000000 --- a/lib/feature/phoneauth/provider/VerificationCodeProvider.dart +++ /dev/null @@ -1,3 +0,0 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -final verificationCodeProvider = StateProvider((ref) => ''); diff --git a/lib/feature/post/PostScreen.dart b/lib/feature/post/PostScreen.dart deleted file mode 100644 index 3cc5fe4..0000000 --- a/lib/feature/post/PostScreen.dart +++ /dev/null @@ -1,87 +0,0 @@ -// 게시물 화면! -// -import 'package:blueberry_flutter_template/feature/friend/FriendsListScreen.dart'; -import 'package:blueberry_flutter_template/feature/mbti/MBTIScreen.dart'; -import 'package:blueberry_flutter_template/feature/post/widget/PostListViewWidget.dart'; -import 'package:blueberry_flutter_template/feature/profile/ProfileDetailScreen.dart'; -import 'package:blueberry_flutter_template/feature/rank/RankScreen.dart'; -import 'package:blueberry_flutter_template/feature/setting/SettingScreen.dart'; -import 'package:blueberry_flutter_template/utils/AppStrings.dart'; -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -import '../../core/widget/CustomFab.dart'; -import '../chat/ChatRoomScreen.dart'; -import 'PostingScreen.dart'; - -class PostScreen extends StatelessWidget { - const PostScreen({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Colors.white70, - appBar: _buildAppBar(context), - body: const SingleChildScrollView( - child: Column( - children: [ - PostListViewWidget(), - SizedBox(height: 100), - ], - ), - ), - floatingActionButton: _buildCustomFAB(context), - ); - } -} - -AppBar _buildAppBar(BuildContext context) { - return AppBar( - centerTitle: false, - title: const Text(AppStrings.appbar_Text_Logo), - actions: [ - IconButton( - icon: const Icon(Icons.message), - onPressed: () => context.goNamed(ChatRoomScreen.name), - ), - IconButton( - icon: const Icon(Icons.emoji_events), - onPressed: () => context.goNamed(RankingScreen.name)), - IconButton( - icon: const Icon(Icons.supervised_user_circle_rounded), - onPressed: () => context.goNamed(ProfileDetailScreen.name), - ) - ], - ); -} - -CustomFab _buildCustomFAB(BuildContext context) { - return CustomFab( - mainColor: Colors.blue, - mainIcon: Icons.add, - fabButtons: [ - FabButton( - icon: Icons.accessibility_sharp, - label: 'MBTI', - onPressed: () => context.goNamed(MBTIScreen.name), - color: Colors.orange, - ), - FabButton( - icon: Icons.person, - label: 'friends', - onPressed: () => context.goNamed(FriendsListScreen.name), - color: Colors.green, - ), - FabButton( - icon: Icons.add_a_photo, - label: 'post', - onPressed: () => context.goNamed(PostingScreen.name)), - FabButton( - icon: Icons.settings, - label: 'Settings', - onPressed: () => context.goNamed(SettingScreen.name), - color: Colors.red, - ), - ], - ); -} diff --git a/lib/feature/post/PostingScreen.dart b/lib/feature/post/PostingScreen.dart deleted file mode 100644 index ee32195..0000000 --- a/lib/feature/post/PostingScreen.dart +++ /dev/null @@ -1,93 +0,0 @@ -import 'package:blueberry_flutter_template/utils/Talker.dart'; -import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; - -import '../../model/PostModel.dart'; - -class PostingScreen extends StatefulWidget { - static const name = 'CreatePostPage'; - - const PostingScreen({super.key}); - - @override - _PostingScreenState createState() => _PostingScreenState(); -} - -class _PostingScreenState extends State { - final _formKey = GlobalKey(); - String? title; - String? content; - String? imageUrl; - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('포스트 생성'), - ), - body: Padding( - padding: const EdgeInsets.all(16.0), - child: Form( - key: _formKey, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TextFormField( - decoration: const InputDecoration(labelText: '제목'), - onSaved: (value) => title = value, - validator: (value) { - if (value == null || value.isEmpty) { - return '제목을 입력해주세요'; - } - return null; - }, - ), - TextFormField( - decoration: const InputDecoration(labelText: '내용'), - onSaved: (value) => content = value, - validator: (value) { - if (value == null) { - return '내용을 입력해주세요'; - } - return null; - }, - ), - TextFormField( - decoration: const InputDecoration(labelText: '이미지 URL'), - onSaved: (value) => imageUrl = value, - validator: (value) { - if (value == null || value.isEmpty) { - return '이미지 URL을 입력해주세요'; - } - return null; - }, - ), - const SizedBox(height: 20), - Center( - child: ElevatedButton( - onPressed: () { - if (_formKey.currentState?.validate() ?? false) { - _formKey.currentState?.save(); - final String uploadTime = - DateFormat('yyyy-MM-dd HH:mm:ss') - .format(DateTime.now()); - final post = PostModel( - title: title!, - content: content!, - imageUrl: imageUrl!, - uploadTime: uploadTime, - ); - // 포스트 생성 후 처리 - talker.info('포스트 생성: ${post.toJson()}'); - } - }, - child: const Text('포스트 생성'), - ), - ), - ], - ), - ), - ), - ); - } -} diff --git a/lib/feature/post/provider/PostProvider.dart b/lib/feature/post/provider/PostProvider.dart deleted file mode 100644 index bc5f3db..0000000 --- a/lib/feature/post/provider/PostProvider.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:blueberry_flutter_template/model/PostModel.dart'; - -final postListInfoProvider = StreamProvider>((ref) { - final firestore = FirebaseFirestore.instance; - return firestore.collection('posts').snapshots().map((snapshot) { - return snapshot.docs.map((doc) => PostModel.fromJson(doc.data())).toList(); - }); -}); diff --git a/lib/feature/post/widget/PostListViewItemWidget.dart b/lib/feature/post/widget/PostListViewItemWidget.dart deleted file mode 100644 index 48e0e01..0000000 --- a/lib/feature/post/widget/PostListViewItemWidget.dart +++ /dev/null @@ -1,166 +0,0 @@ -import 'package:flutter/material.dart'; - -class PostListViewItemWidget extends StatefulWidget { - final String title; - final String uploadTime; - final String content; - final String imageUrl; - - const PostListViewItemWidget({ - super.key, - required this.title, - required this.uploadTime, - required this.content, - required this.imageUrl, - }); - - @override - _PostListViewItemWidgetState createState() => _PostListViewItemWidgetState(); -} - -// 카드 스타일을 위한 변수 -final cardDecoration = BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(15.0), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.1), - spreadRadius: 3, - blurRadius: 5, - offset: const Offset(0, 3), // 그림자 위치 - ), - ], -); - -class _PostListViewItemWidgetState extends State { - bool isLiked = false; // 좋아요 상태를 저장하는 변수 - bool showComment = false; // 댓글 표시 여부를 저장하는 변수 - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 15), - decoration: cardDecoration, - child: ClipRRect( - borderRadius: BorderRadius.circular(15.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Stack( - children: [ - // 이미지 - ClipRRect( - borderRadius: BorderRadius.circular(15.0), - child: Image.network( - widget.imageUrl, - width: double.infinity, - height: 200, - fit: BoxFit.cover, - ), - ), - // 제목과 날짜를 이미지 위에 오버레이 - Positioned( - bottom: 10, - left: 10, - right: 10, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - widget.title, - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 20, - color: Colors.white, - shadows: [ - Shadow( - offset: Offset(0, 1), - blurRadius: 3.0, - color: Colors.black, - ), - ], - ), - ), - const SizedBox(height: 5), - Text( - widget.uploadTime, - style: const TextStyle( - color: Colors.white70, - fontSize: 12, - shadows: [ - Shadow( - offset: Offset(0, 1), - blurRadius: 3.0, - color: Colors.black, - ), - ], - ), - ), - ], - ), - ), - ], - ), - const SizedBox(height: 10), - // 본문 내용 - Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: Text( - widget.content, - style: const TextStyle( - fontSize: 16, - ), - ), - ), - const SizedBox(height: 10), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - // 좋아요 버튼 - IconButton( - icon: Icon( - isLiked ? Icons.thumb_up : Icons.thumb_up_off_alt, - color: isLiked ? Colors.blue : Colors.grey, - ), - onPressed: () { - setState(() { - isLiked = !isLiked; // 좋아요 상태 토글 - }); - }, - ), - // 댓글 달기 버튼 - IconButton( - onPressed: () => setState(() { - showComment = !showComment; // 댓글 표시 여부 토글 - }), - icon: const Icon(Icons.comment)) - ], - ), - ), - // 임시 댓글 표시 - if (showComment) - const Padding( - padding: EdgeInsets.symmetric(horizontal: 15), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Divider(), // 구분선 추가 - Text( - '정우님 항상 감사합니다', - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - ), - ), - SizedBox(height: 10), - ], - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/feature/post/widget/PostListViewWidget.dart b/lib/feature/post/widget/PostListViewWidget.dart deleted file mode 100644 index a6531f0..0000000 --- a/lib/feature/post/widget/PostListViewWidget.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:blueberry_flutter_template/feature/post/provider/PostProvider.dart'; -import 'package:blueberry_flutter_template/feature/post/widget/PostListViewItemWidget.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -class PostListViewWidget extends ConsumerWidget { - const PostListViewWidget({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final postList = ref.watch(postListInfoProvider); - - return SafeArea( - child: postList.when( - data: (posts) => ListView.builder( - shrinkWrap: true, - padding: const EdgeInsets.all(8.0), - itemCount: posts.length, - itemBuilder: (context, index) { - final post = posts[index]; - return PostListViewItemWidget( - title: post.title, - uploadTime: post.uploadTime, - content: post.content, - imageUrl: post.imageUrl, - ); - }, - ), - loading: () => const Center(child: CircularProgressIndicator()), - error: (error, stackTrace) => Center(child: Text('Error: $error')), - ), - ); - } -} diff --git a/lib/feature/profile/ProfileDetailScreen.dart b/lib/feature/profile/ProfileDetailScreen.dart deleted file mode 100644 index be452ff..0000000 --- a/lib/feature/profile/ProfileDetailScreen.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'package:flutter/material.dart'; -import 'widget/ProfileDetailWidget.dart'; - -class ProfileDetailScreen extends StatelessWidget { - static const String name = 'ProfileDetailScreen'; - const ProfileDetailScreen({super.key}); - - @override - Widget build(BuildContext context) { - return const ProfileDetailWidget(); - } -} diff --git a/lib/feature/profile/provider/profileDetailProvider.dart b/lib/feature/profile/provider/profileDetailProvider.dart deleted file mode 100644 index e9fd916..0000000 --- a/lib/feature/profile/provider/profileDetailProvider.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -final profileDetailProvider = - StateNotifierProvider((ref) { - return ProfileDetailNotifier(0); -}); - -class ProfileDetailNotifier extends StateNotifier { - ProfileDetailNotifier(super.state); -} - -Future> fetchProfileImageUrls() async { - List imageUrls = []; - - final FirebaseFirestore db = FirebaseFirestore.instance; - CollectionReference profileImageCollection = db.collection('profileImages'); - - QuerySnapshot snapshot = await profileImageCollection.get(); - for (QueryDocumentSnapshot doc in snapshot.docs) { - var data = doc.data() as Map?; // Nullable 검사 및 타입 캐스팅 - if (data != null && data.containsKey('imageUrl')) { - imageUrls.add(data['imageUrl']); - } - } - return imageUrls; -} - -final profileImageUrlsProvider = FutureProvider>((ref) async { - return await fetchProfileImageUrls(); -}); diff --git a/lib/feature/profile/widget/ProfileDetailWidget.dart b/lib/feature/profile/widget/ProfileDetailWidget.dart deleted file mode 100644 index 015718f..0000000 --- a/lib/feature/profile/widget/ProfileDetailWidget.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -class ProfileDetailWidget extends StatelessWidget { - const ProfileDetailWidget({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('Profile Detail'), - actions: [ - IconButton( - icon: const Icon(Icons.arrow_back), - onPressed: () { - context.pop(); - }, - ), - ], - ), - body: Center( - child: GestureDetector( - onTap: () {}, - child: Container( - width: 300, - height: 400, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - border: Border.all(color: Colors.black), - ), - child: const Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - CircleAvatar( - radius: 80, - backgroundImage: AssetImage('assets/logo/logo_1.png'), - ), - SizedBox(height: 20), - Text( - 'Profile Photo', - style: TextStyle(fontSize: 24), - ), - ], - ), - ), - ), - ), - ); - } -} diff --git a/lib/feature/rank/RankScreen.dart b/lib/feature/rank/RankScreen.dart deleted file mode 100644 index afc2ceb..0000000 --- a/lib/feature/rank/RankScreen.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:blueberry_flutter_template/feature/rank/widget/RankViewWidget.dart'; - -final class RankingScreen extends StatelessWidget { - static const String name = 'RankingScreen'; - const RankingScreen({super.key}); - - @override - Widget build(BuildContext context) { - return const RankViewWidget(); - } -} diff --git a/lib/feature/rank/provider/UserRankProvider.dart b/lib/feature/rank/provider/UserRankProvider.dart deleted file mode 100644 index 7fc6b2f..0000000 --- a/lib/feature/rank/provider/UserRankProvider.dart +++ /dev/null @@ -1,110 +0,0 @@ -import 'dart:convert'; - -import 'package:blueberry_flutter_template/model/UserModel.dart'; -import 'package:blueberry_flutter_template/services/cache/CacheService.dart'; -import 'package:blueberry_flutter_template/utils/AppStrings.dart'; -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -final userProvider = FutureProvider>((ref) async { - final cacheService = CacheService.instance; - - // 한국 시간으로 매일 오전 9시 설정 - final now = DateTime.now().toUtc().add(const Duration(hours: 9)); - final tomorrowMorning = DateTime(now.year, now.month, now.day + 1, 9).toUtc(); - - // 캐시 정책 설정 - final cacheConfig = CacheConfig( - cacheKey: AppStrings.userCacheKey, - expiryTime: tomorrowMorning, // 다음 날 오전 9시까지 유효 - ); - - try { - // 캐시된 데이터 가져오기 - final cachedUsers = await _getCachedUsers(cacheService, cacheConfig); - if (cachedUsers != null) return cachedUsers; - - // Firestore에서 데이터 가져오기 - final users = await _fetchUsersFromFirestore(); - - // 캐시에 저장 - await _cacheUsers(cacheService, cacheConfig, users); - - return users; - } catch (e) { - throw Exception('Failed to load users'); - } -}); - -Future?> _getCachedUsers( - CacheService cacheService, CacheConfig cacheConfig) async { - try { - final cachedData = await cacheService.getCachedData(cacheConfig); - if (cachedData != null) { - final cachedUsers = (json.decode(cachedData) as List) - .map((item) => UserModel.fromJson(item as Map)) - .toList(); - return cachedUsers; - } - } catch (e) { - throw Exception('Failed to fetch user cached data'); - } - return null; -} - -Future> _fetchUsersFromFirestore() async { - try { - final fireStore = FirebaseFirestore.instance; - - final likesSnapshot = await fireStore - .collection('likes') - .orderBy('likedUsers', descending: true) - .get(); - - final likedUserIds = likesSnapshot.docs - .expand((doc) => List.from(doc['likedUsers'])) - .toList(); - - final userDocs = await Future.wait( - likedUserIds - .map((userId) => fireStore.collection('users').doc(userId).get()) - .toList(), - ); - - final users = userDocs.map((doc) { - if (doc.exists) { - final data = doc.data()!; - return UserModel( - userClass: data['userClass'] as String, - userId: doc.id, - name: data['name'] as String, - email: data['email'] as String, - age: data['age'] as int, - isMemberShip: data['isMemberShip'] as bool, - profileImageUrl: data['profilePicture'] as String?, - createdAt: DateTime.parse(data['createdAt'] as String), - mbti: data['mbti'] as String, - fcmToken: data['fcmToken'] as String?, - likeGivens: data["likeGivens"] as List, - ); - } else { - throw Exception('User not found'); - } - }).toList(); - - return users; - } catch (e) { - throw Exception('Failed to fetch users from Firestore'); - } -} - -Future _cacheUsers(CacheService cacheService, CacheConfig cacheConfig, - List users) async { - try { - final encodedData = - json.encode(users.map((user) => user.toJson()).toList()); - await cacheService.cacheData(cacheConfig, encodedData); - } catch (e) { - throw Exception('Failed to save userinfo cache'); - } -} diff --git a/lib/feature/rank/widget/RankViewWidget.dart b/lib/feature/rank/widget/RankViewWidget.dart deleted file mode 100644 index fe0b26c..0000000 --- a/lib/feature/rank/widget/RankViewWidget.dart +++ /dev/null @@ -1,73 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import 'package:blueberry_flutter_template/utils/AppTextStyle.dart'; -import 'package:blueberry_flutter_template/model/UserModel.dart'; -import 'package:blueberry_flutter_template/feature/rank/provider/UserRankProvider.dart'; -import 'package:blueberry_flutter_template/utils/AppStrings.dart'; - -class RankViewWidget extends ConsumerWidget { - const RankViewWidget({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final userRankings = ref.watch(userProvider); - - return Scaffold( - appBar: AppBar( - title: const Text(AppStrings.rankViewTitle), - ), - body: RefreshIndicator( - onRefresh: () async { - ref.invalidate(userProvider); // 강제로 프로바이더를 무효화하여 데이터 불러옴 - // ignore: unused_result - await ref.refresh(userProvider.future); - }, - child: buildBody(userRankings), - ), - ); - } - - Widget buildBody(AsyncValue> userRankings) { - return userRankings.when( - data: (users) => buildUserList(users), - loading: () => const Center(child: CircularProgressIndicator()), - error: (error, stack) => Center(child: Text('Error: $error')), - ); - } - - Widget buildUserList(List users) { - return CustomScrollView( - slivers: [ - SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) { - final user = users[index]; - return userRankingList( - rank: index + 1, - userName: user.name, - ); - }, - childCount: users.length, - ), - ), - ], - ); - } - - Widget userRankingList({ - required int rank, - required String userName, - }) { - return ListTile( - leading: Text( - '$rank', - style: black16TextStyle, - ), - title: Text( - userName, - style: black16TextStyle, - ), - ); - } -} diff --git a/lib/feature/setting/SettingScreen.dart b/lib/feature/setting/SettingScreen.dart deleted file mode 100644 index 62a324a..0000000 --- a/lib/feature/setting/SettingScreen.dart +++ /dev/null @@ -1,89 +0,0 @@ -import 'package:blueberry_flutter_template/core/OssLicenseScreen.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; - -import 'provider/NotificationProvider.dart'; -import '../../core/provider/ThemeProvider.dart'; - -class SettingScreen extends ConsumerWidget { - static const String name = 'SettingsScreen'; - - const SettingScreen({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final themeNotifier = ref.read(themeNotifierProvider.notifier); - final isDarkMode = ref.watch(themeNotifierProvider) == ThemeMode.dark; - - return Scaffold( - appBar: AppBar( - leading: IconButton( - onPressed: () => context.pop(), icon: const Icon(Icons.arrow_back)), - title: const Text('Setting'), - ), - body: Center( - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('Notification'), - const SizedBox(width: 20), - const Icon(Icons.notifications), - Switch.adaptive( - value: ref.watch(notificationProvider), - onChanged: - ref.read(notificationProvider.notifier).setNotification, - ), - ], - ), - const SizedBox(height: 20), - ElevatedButton( - onPressed: () {}, - child: const Text('Change Password'), - ), - const Divider(height: 60), - ElevatedButton( - onPressed: () {}, - child: const Text('Policy'), - ), - const SizedBox(height: 20), - ElevatedButton( - onPressed: () {}, - child: const Text('Contact us'), - ), - const SizedBox(height: 20), - ElevatedButton( - onPressed: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => const OssLicenseScreen(), - ), - ); - }, - child: const Text('Open source page'), - ), - const SizedBox(height: 20), - ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.red, - foregroundColor: Colors.white, - ), - onPressed: () {}, - child: const Text('Account delete'), - ), - const SizedBox(height: 40), - SwitchListTile( - title: const Text('Dark Mode'), - value: isDarkMode, - onChanged: (bool value) { - themeNotifier.toggleTheme(); - }, - ), - ], - ), - ), - ); - } -} diff --git a/lib/feature/setting/provider/NotificationProvider.dart b/lib/feature/setting/provider/NotificationProvider.dart deleted file mode 100644 index 4f3a3f0..0000000 --- a/lib/feature/setting/provider/NotificationProvider.dart +++ /dev/null @@ -1,57 +0,0 @@ -import 'package:blueberry_flutter_template/utils/FlutterSecureStorage.dart'; -import 'package:blueberry_flutter_template/utils/StorageKeys.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:permission_handler/permission_handler.dart'; - -import '../../../services/notification/firebase_cloud_messaging_manager.dart'; - -final notificationProvider = - StateNotifierProvider((ref) { - return NotificationNotifier(false); -}); - -class NotificationNotifier extends StateNotifier { - final _storage = PreferenceStorage(); - - NotificationNotifier(super.state) { - checkPermission(); - } - - // 권한 확인 - Future checkPermission() async { - final status = await Permission.notification.status; - - if (status.isGranted == false) return; - - _storage.readString(StorageKeys.fcmToken).then((value) { - debugPrint('Saved fcmToken: $value'); - state = value != null && value.isNotEmpty; - }); - } - - void setNotification(bool value) async { - if (value == false) { - FirebaseCloudMessagingManager.deleteToken(); - _storage.write(StorageKeys.fcmToken, ''); - state = false; - } else { - final status = await Permission.notification.status; - - if (status.isDenied) { - await Permission.notification.request(); - } else if (status.isPermanentlyDenied) { - await openAppSettings(); - } - - if ((await Permission.notification.status).isGranted) { - final token = await FirebaseCloudMessagingManager.getToken(); - - debugPrint('fcmToken: $token'); - - _storage.write(StorageKeys.fcmToken, token); - state = true; - } - } - } -} diff --git a/lib/feature/signup/SignUpScreen.dart b/lib/feature/signup/SignUpScreen.dart deleted file mode 100644 index 7ba3592..0000000 --- a/lib/feature/signup/SignUpScreen.dart +++ /dev/null @@ -1,179 +0,0 @@ -import 'package:blueberry_flutter_template/feature/signup/provider/SignUpDataProviders.dart'; -import 'package:blueberry_flutter_template/feature/signup/widget/EmailDuplicateWidget.dart'; -import 'package:blueberry_flutter_template/feature/signup/widget/EmailVerifyWidget.dart'; -import 'package:blueberry_flutter_template/feature/signup/widget/NameInputWidget.dart'; -import 'package:blueberry_flutter_template/feature/signup/widget/NickNameInputWidget.dart'; -import 'package:blueberry_flutter_template/feature/signup/widget/PasswordConfirmWidget.dart'; -import 'package:blueberry_flutter_template/feature/signup/widget/PasswordInputWidget.dart'; -import 'package:blueberry_flutter_template/feature/signup/widget/PrivacyPolicyWidget.dart'; -import 'package:blueberry_flutter_template/feature/signup/widget/TermsOfServiceWidget.dart'; -import 'package:blueberry_flutter_template/utils/AppStrings.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; - -import '../phoneauth/ConfirmationPage.dart'; - -final PageController _pageController = PageController(); - -class SignUpScreen extends ConsumerStatefulWidget { - static const String name = 'SignUpScreen'; - - const SignUpScreen({super.key}); - - @override - ConsumerState createState() => _SignUpScreenState(); -} - -class _SignUpScreenState extends ConsumerState { - @override - Widget build(BuildContext context) { - ref.watch(emailProvider); - ref.watch(passwordConfirmProvider); - return Scaffold( - appBar: AppBar( - leading: IconButton( - icon: const Icon(Icons.arrow_back), - color: Colors.black, - onPressed: () => context.pop()), - title: const Text(AppStrings.signUpPageTitle), - ), - body: PageView( - controller: _pageController, - physics: const NeverScrollableScrollPhysics(), - children: [ - EmailInputPage( - onNext: () => _pageController.nextPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ), - ), - EmailVerification( - onNext: () => _pageController.nextPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ), - ), - PasswordInputPage( - onNext: () => _pageController.nextPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ), - ), - PasswordConfirmPage( - onNext: () => _pageController.nextPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ), - ), - NameNickNameInputPage( - onNext: () => _pageController.nextPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ), - ), - TermsOfServicePage( - onNext: () => _pageController.nextPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ), - ), - PrivacyPolicyPage( - onNext: () => _pageController.nextPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ), - ), - ConfirmationPage( - onNext: () => Navigator.of(context).pop(), - ), - ], - ), - ); - } -} - -class EmailInputPage extends StatelessWidget { - final VoidCallback onNext; - - const EmailInputPage({super.key, required this.onNext}); - - @override - Widget build(BuildContext context) { - return EmailDuplicateWidget(onNext: onNext); - } -} - -class EmailVerification extends StatelessWidget { - final VoidCallback onNext; - - const EmailVerification({super.key, required this.onNext}); - - @override - Widget build(BuildContext context) { - return EmailVerifyWidget(onNext: onNext); - } -} - -class PasswordInputPage extends StatelessWidget { - final VoidCallback onNext; - - const PasswordInputPage({super.key, required this.onNext}); - - @override - Widget build(BuildContext context) { - return PasswordInputWidget(onNext: onNext); - } -} - -class PasswordConfirmPage extends StatelessWidget { - final VoidCallback onNext; - - const PasswordConfirmPage({super.key, required this.onNext}); - - @override - Widget build(BuildContext context) { - return PasswordConfirmWidget(onNext: onNext); - } -} - -class NameNickNameInputPage extends StatelessWidget { - final VoidCallback onNext; - - const NameNickNameInputPage({super.key, required this.onNext}); - - @override - Widget build(BuildContext context) { - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const NameInputWidget(), - NickNameInputWidget(onNext: onNext), - ], - ), - ); - } -} - -class TermsOfServicePage extends StatelessWidget { - final VoidCallback onNext; - - const TermsOfServicePage({super.key, required this.onNext}); - - @override - Widget build(BuildContext context) { - return TermsOfServiceWidget(onNext: onNext); - } -} - -class PrivacyPolicyPage extends StatelessWidget { - final VoidCallback onNext; - - const PrivacyPolicyPage({super.key, required this.onNext}); - - @override - Widget build(BuildContext context) { - return PrivacyPolicyWidget(onNext: onNext); - } -} diff --git a/lib/feature/signup/provider/SignUpDataProviders.dart b/lib/feature/signup/provider/SignUpDataProviders.dart deleted file mode 100644 index c2fb7d3..0000000 --- a/lib/feature/signup/provider/SignUpDataProviders.dart +++ /dev/null @@ -1,20 +0,0 @@ -// 이메일 인증 ( 아이디 ) -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -final emailProvider = StateProvider.autoDispose((ref) => ''); -final emailVerificationCodeProvider = StateProvider((ref) => ''); - -// 이름, 닉네임 생성 -final nameProvider = StateProvider.autoDispose((ref) => ''); -final nicknameProvider = StateProvider.autoDispose((ref) => ''); - -// 비밀번호 생성 -final passwordProvider = StateProvider.autoDispose((ref) => ''); -final passwordConfirmProvider = StateProvider.autoDispose((ref) => ''); - -// 휴대폰 번호 인증 ( 구매할 때 휴대폰 인증 ) ( 따로 만들기 ) -final residentRegistrationNumberProvider = - StateProvider.autoDispose((ref) => ''); -final phoneNumberProvider = StateProvider.autoDispose((ref) => ''); -final verificationNumberProvider = - StateProvider.autoDispose((ref) => ''); diff --git a/lib/feature/signup/provider/SignUpEmailDuplicationProvider.dart b/lib/feature/signup/provider/SignUpEmailDuplicationProvider.dart deleted file mode 100644 index fda1418..0000000 --- a/lib/feature/signup/provider/SignUpEmailDuplicationProvider.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -final emailDuplicateProvider = - StateNotifierProvider((ref) { - return EmailDuplicateNotifier(); -}); - -class EmailDuplicateState { - String? email; - bool isDuplication = false; - - EmailDuplicateState({this.email, required this.isDuplication}); -} - -class EmailDuplicateNotifier extends StateNotifier { - EmailDuplicateNotifier() : super(EmailDuplicateState(isDuplication: false)); - - final FirebaseFirestore _firestore = FirebaseFirestore.instance; - - Future isDuplication(String email) async { - try { - QuerySnapshot querySnapshot = await _firestore - .collection('users') - .where('email', isEqualTo: email) - .get(); - if (querySnapshot.docs.isNotEmpty) { - // 이메일이 이미 존재하는 경우 - state = EmailDuplicateState(email: email, isDuplication: true); - return true; - } else { - // 이메일이 존재하지 않는 경우 - state = EmailDuplicateState(email: email, isDuplication: false); - return false; - } - } catch (e) { - print('$e'); - state = EmailDuplicateState(email: email, isDuplication: false); - return false; - } - } -} diff --git a/lib/feature/signup/widget/EmailDuplicateWidget.dart b/lib/feature/signup/widget/EmailDuplicateWidget.dart deleted file mode 100644 index 92bdca6..0000000 --- a/lib/feature/signup/widget/EmailDuplicateWidget.dart +++ /dev/null @@ -1,99 +0,0 @@ -import 'package:blueberry_flutter_template/feature/signup/provider/SignUpDataProviders.dart'; -import 'package:blueberry_flutter_template/feature/signup/provider/SignUpEmailDuplicationProvider.dart'; -import 'package:blueberry_flutter_template/services/verifications/EmailVerificationService.dart'; -import 'package:blueberry_flutter_template/utils/AppStrings.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -class EmailDuplicateWidget extends ConsumerWidget { - final VoidCallback onNext; - final TextEditingController _emailController = TextEditingController(); - final EmailVerificationService emailVerificationService = - EmailVerificationService(); - final bool isEmailAvailable = false; - - EmailDuplicateWidget({super.key, required this.onNext}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final email = ref.watch(emailProvider.notifier); - final emailDuplicate = ref.watch(emailDuplicateProvider.notifier); - final formKey = GlobalKey(); - final RegExp emailRegExp = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); - - return Padding( - padding: const EdgeInsets.all(16.0), - child: Form( - key: formKey, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TextFormField( - controller: _emailController, - onChanged: (value) => email.state = value, - decoration: - const InputDecoration(labelText: AppStrings.emailInputLabel), - validator: (value) { - if (value == null || value.isEmpty) { - return AppStrings.errorMessage_emptyEmail; - } else if (!emailRegExp.hasMatch(value)) { - return AppStrings.errorMessage_invalidEmail; - } - return null; - }, - ), - const Text(AppStrings.requiredYourEmail), - const SizedBox(height: 20), - _duplicationBtn(emailDuplicate, context, formKey), - const SizedBox(height: 20), - _nextBtn(formKey, context) - ], - ), - ), - ); - } - - ElevatedButton _duplicationBtn(EmailDuplicateNotifier emailDuplicate, - BuildContext context, GlobalKey formKey) { - return ElevatedButton( - onPressed: () async { - if (formKey.currentState?.validate() ?? false) { - String email = _emailController.text; - bool isDuplicate = await emailDuplicate.isDuplication(email); - if (isDuplicate) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text(AppStrings.errorMessage_emailAlreadyInUse)), - ); - } else { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text(AppStrings.successMessage_emailValidation)), - ); - } - } else { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text(AppStrings.errorMessage_checkEmail)), - ); - } - }, - child: const Text(AppStrings.checkDuplicateEmail), - ); - } - - ElevatedButton _nextBtn(GlobalKey formKey, BuildContext context) { - return ElevatedButton( - onPressed: () async { - if (formKey.currentState?.validate() ?? false) { - await emailVerificationService - .sendVerificationEmail(_emailController.text); - onNext(); - } else { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text(AppStrings.errorMessage_checkEmail)), - ); - } - }, - child: const Text('NEXT')); - } -} diff --git a/lib/feature/signup/widget/EmailVerifyWidget.dart b/lib/feature/signup/widget/EmailVerifyWidget.dart deleted file mode 100644 index d9c7b6d..0000000 --- a/lib/feature/signup/widget/EmailVerifyWidget.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:blueberry_flutter_template/feature/signup/provider/SignUpDataProviders.dart'; -import 'package:blueberry_flutter_template/services/verifications/EmailVerificationService.dart'; -import 'package:blueberry_flutter_template/utils/AppStrings.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -class EmailVerifyWidget extends ConsumerWidget { - final VoidCallback onNext; - EmailVerifyWidget({super.key, required this.onNext}); - - final TextEditingController _codeController = TextEditingController(); - final EmailVerificationService emailVerificationService = - EmailVerificationService(); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final RegExp codeRegExp = RegExp(r'^[0-9]{5}$'); - final formKey = GlobalKey(); - final email = ref.watch(emailProvider); - - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text('$email 이메일로 전송된 인증번호를 입력해주세요.'), - const SizedBox(height: 10), - Form( - key: formKey, - child: TextFormField( - controller: _codeController, - decoration: - const InputDecoration(labelText: AppStrings.verifyCode), - validator: (value) { - if (value == null || value.isEmpty) { - return AppStrings.errorMessage_emptyVerifyCode; - } else if (!codeRegExp.hasMatch(value)) { - return AppStrings.errorMessage_wrongVerifyCode; - } - return null; - }, - ), - ), - const SizedBox(height: 10), - ElevatedButton( - onPressed: () async { - await emailVerificationService.verifyCode( - email, _codeController.text, onNext); - }, - child: const Text(AppStrings.checkVerifyCode)), - ], - ), - ); - } -} diff --git a/lib/feature/signup/widget/NameInputWidget.dart b/lib/feature/signup/widget/NameInputWidget.dart deleted file mode 100644 index 1276739..0000000 --- a/lib/feature/signup/widget/NameInputWidget.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:blueberry_flutter_template/feature/signup/provider/SignUpDataProviders.dart'; -import 'package:blueberry_flutter_template/utils/AppStrings.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -class NameInputWidget extends ConsumerWidget { - const NameInputWidget({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final name = ref.watch(nameProvider.notifier); - final formKey = GlobalKey(); - final RegExp nameRegExp = RegExp(r'^[가-힣]{4,}$'); - - return Padding( - padding: const EdgeInsets.all(16.0), - child: Form( - key: formKey, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TextFormField( - onChanged: (value) => name.state = value, - decoration: const InputDecoration(labelText: '이름 입력'), - validator: (value) { - if (value == null || value.isEmpty) { - return AppStrings.errorMessage_emptyName; - } else if (!nameRegExp.hasMatch(value)) { - return AppStrings.errorMessage_worngName; - } - return null; - }, - ), - const SizedBox(height: 20), - ], - ), - ), - ); - } -} diff --git a/lib/feature/signup/widget/NickNameInputWidget.dart b/lib/feature/signup/widget/NickNameInputWidget.dart deleted file mode 100644 index 195e0e5..0000000 --- a/lib/feature/signup/widget/NickNameInputWidget.dart +++ /dev/null @@ -1,65 +0,0 @@ -import 'package:blueberry_flutter_template/feature/signup/provider/SignUpDataProviders.dart'; -import 'package:blueberry_flutter_template/services/FirebaseAuthServiceProvider.dart'; -import 'package:blueberry_flutter_template/utils/AppStrings.dart'; -import 'package:blueberry_flutter_template/utils/ForbiddenPatterns.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -class NickNameInputWidget extends ConsumerWidget { - final VoidCallback onNext; - - const NickNameInputWidget({super.key, required this.onNext}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final nickname = ref.watch(nicknameProvider.notifier); - final formKey = GlobalKey(); - final RegExp nickNameRegExp = RegExp(r'^[가-힣a-zA-Z0-9]{2,}$'); - - return Padding( - padding: const EdgeInsets.all(16.0), - child: Form( - key: formKey, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TextFormField( - onChanged: (value) => nickname.state = value, - decoration: const InputDecoration(labelText: '닉네임 입력'), - validator: (value) { - if (value == null || value.isEmpty) { - return AppStrings.errorMessage_emptyNickName; - } else if (!nickNameRegExp.hasMatch(value)) { - return AppStrings.errorMessage_wrongNickName; - } - for (var patterns in forbiddenPatterns) { - if (patterns.hasMatch(value)) { - return AppStrings.errorMessage_forbiddenNickName; - } - } - return null; - }, - // 특문 막는 정규식, 일부 금칙어 설정 해야함 - ), - ElevatedButton( - onPressed: () { - try { - if (formKey.currentState!.validate()) { - final user = - ref.watch(firebaseAuthServiceProvider).getCurrentUser(); - user?.sendEmailVerification(); - onNext(); - print('okay'); - } - } catch (e) { - print(e); - } - }, - child: const Text('Next'), - ), - ], - ), - ), - ); - } -} diff --git a/lib/feature/signup/widget/PasswordConfirmWidget.dart b/lib/feature/signup/widget/PasswordConfirmWidget.dart deleted file mode 100644 index c673e40..0000000 --- a/lib/feature/signup/widget/PasswordConfirmWidget.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:blueberry_flutter_template/feature/signup/provider/SignUpDataProviders.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import '../../../utils/AppStrings.dart'; - -class PasswordConfirmWidget extends ConsumerWidget { - final VoidCallback onNext; - - const PasswordConfirmWidget({super.key, required this.onNext}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final password = ref.watch(passwordProvider); - final passwordConfirm = ref.watch(passwordConfirmProvider.notifier); - final formKey = GlobalKey(); - - return Padding( - padding: const EdgeInsets.all(16.0), - child: Form( - key: formKey, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TextFormField( - onChanged: (value) => passwordConfirm.state = value, - obscureText: true, - decoration: const InputDecoration( - labelText: AppStrings.passwordInputLabel), - validator: (value) { - if (value == null || value.isEmpty) { - return AppStrings.errorMessage_emptyPassword; - } else if (password != value) { - return AppStrings.errorMessage_duplicatedPassword; - } - return null; - }, - ), - const SizedBox(height: 20), - ElevatedButton( - onPressed: () async { - try { - onNext(); - } catch (e) { - print('failed signUp $e'); - } - }, - child: const Text('Next'), - ), - ], - ), - ), - ); - } -} diff --git a/lib/feature/signup/widget/PasswordInputWidget.dart b/lib/feature/signup/widget/PasswordInputWidget.dart deleted file mode 100644 index 353fa06..0000000 --- a/lib/feature/signup/widget/PasswordInputWidget.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'package:blueberry_flutter_template/feature/signup/provider/SignUpDataProviders.dart'; -import 'package:blueberry_flutter_template/utils/AppStrings.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -class PasswordInputWidget extends ConsumerWidget { - final VoidCallback onNext; - - const PasswordInputWidget({super.key, required this.onNext}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final password = ref.watch(passwordProvider.notifier); - final RegExp passwordRegExp = - RegExp(r'^(?=.*[!@#\$&*~])(?=.*[!@#\$&*~]).{8,}$'); - final formKey = GlobalKey(); - - return Padding( - padding: const EdgeInsets.all(16.0), - child: Form( - key: formKey, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TextFormField( - onChanged: (value) => password.state = value, - decoration: const InputDecoration(labelText: '비밀번호 입력'), - validator: (value) { - if (value == null || value.isEmpty) { - return AppStrings.errorMessage_emptyPassword; - } else if (!passwordRegExp.hasMatch(value)) { - return AppStrings.errorMessage_regexpPassword; - } - return null; - }, - ), - const SizedBox(height: 20), - ElevatedButton( - onPressed: () { - if (formKey.currentState?.validate() ?? false) { - onNext(); - } else { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text(AppStrings.errorMessage_checkPassword)), - ); - } - }, - child: const Text('Next'), - ), - ], - ), - ), - ); - } -} diff --git a/lib/feature/signup/widget/PrivacyPolicyWidget.dart b/lib/feature/signup/widget/PrivacyPolicyWidget.dart deleted file mode 100644 index a5365f6..0000000 --- a/lib/feature/signup/widget/PrivacyPolicyWidget.dart +++ /dev/null @@ -1,107 +0,0 @@ -import 'package:flutter/material.dart'; - -class PrivacyPolicyWidget extends StatefulWidget { - final VoidCallback onNext; - - const PrivacyPolicyWidget({super.key, required this.onNext}); - - @override - State createState() => _PrivacyPolicyPageState(); -} - -class _PrivacyPolicyPageState extends State { - final ScrollController _scrollController = ScrollController(); - bool isBottom = false; - - @override - void initState() { - _scrollController.addListener(() { - if (_scrollController.position.pixels == - _scrollController.position.maxScrollExtent) { - setState(() { - isBottom = true; - }); - } else { - setState(() { - isBottom = false; - }); - } - }); - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('개인정보 처리방침'), - ), - body: SingleChildScrollView( - controller: _scrollController, - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - getPolicy(), - style: const TextStyle(fontSize: 16), - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: () { - if (isBottom) { - widget.onNext(); - } else { - _scrollController.animateTo( - _scrollController.position.maxScrollExtent, - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); - } - }, - child: const Icon(Icons.check), - )); - } -} - -String getPolicy() { - const String companyName = "블루베리 스토어"; - const String name = "손정우"; - const String phone = "010-8513-6357 / (+81)10-9698-6357"; - const String email = "sonjungwoo9@gmail.com"; - - return ''' -"$companyName"는 (이하 "회사"는) 고객님의 개인정보를 중요시하며, "정보통신망 이용촉진 및 정보보호"에 관한 법률을 준수하고 있습니다. 회사는 개인정보처리방침을 통하여 고객님께서 제공하시는 개인정보가 어떠한 용도와 방식으로 이용되고 있으며, 개인정보보호를 위해 어떠한 조치가 취해지고 있는지 알려드립니다. - -■ 수집하는 개인정보 항목 및 수집방법 가. 수집하는 개인정보의 항목 o 회사는 회원가입, 상담, 서비스 신청 등을 위해 아래와 같은 개인정보를 수집하고 있습니다. - 회원가입시 : 이름 , 생년월일 , 성별 , 로그인ID , 비밀번호 , 휴대전화번호 , 이메일 , 14세미만 가입자의 경우 법정대리인의 정보 - 서비스 신청시 : 주소, 결제 정보 - -o 서비스 이용 과정이나 사업 처리 과정에서 서비스이용기록, 접속로그, 쿠키, 접속 IP, 결제 기록, 불량이용 기록이 생성되어 수집될 수 있습니다. - -나. 수집방법 - 홈페이지, 서면양식, 게시판, 이메일, 이벤트 응모, 배송요청, 전화, 팩스, 생성 정보 수집 툴을 통한 수집 - -■ 개인정보의 수집 및 이용목적 회사는 수집한 개인정보를 다음의 목적을 위해 활용합니다. o 서비스 제공에 관한 계약 이행 및 서비스 제공에 따른 요금정산 콘텐츠 제공 , 구매 및 요금 결제 , 물품배송 또는 청구지 등 발송 , 금융거래 본인 인증 및 금융 서비스 o 회원 관리 회원제 서비스 이용에 따른 본인확인 , 개인 식별 , 불량회원의 부정 이용 방지와 비인가 사용 방지 , 가입 의사 확인 , 연령확인 , 만14세 미만 아동 개인정보 수집 시 법정 대리인 동의여부 확인, 불만처리 등 민원처리 , 고지사항 전달 o 마케팅 및 광고에 활용 이벤트 등 광고성 정보 전달 , 접속 빈도 파악 또는 회원의 서비스 이용에 대한 통계 - -■ 개인정보의 보유 및 이용기간 원칙적으로, 개인정보 수집 및 이용목적이 달성된 후에는 해당 정보를 지체 없이 파기합니다. 단, 다음의 정보에 대해서는 아래의 이유로 명시한 기간 동안 보존합니다. - -가. 회사 내부방침에 의한 정보보유 사유 회원이 탈퇴한 경우에도 불량회원의 부정한 이용의 재발을 방지, 분쟁해결 및 수사기관의 요청에 따른 협조를 위하여, 이용계약 해지일로부터 oo년간 회원의 정보를 보유할 수 있습니다. - -나. 관련 법령에 의한 정보 보유 사유 전자상거래등에서의소비자보호에관한법률 등 관계법령의 규정에 의하여 보존할 필요가 있는 경우 회사는 아래와 같이 관계법령에서 정한 일정한 기간 동안 회원정보를 보관합니다. o 계약 또는 청약철회 등에 관한 기록 -보존이유 : 전자상거래등에서의소비자보호에관한법률 -보존기간 : 5년 o 대금 결제 및 재화 등의 공급에 관한 기록 -보존이유: 전자상거래등에서의소비자보호에관한법률 -보존기간 : 5년 o 소비자 불만 또는 분쟁처리에 관한 기록 -보존이유 : 전자상거래등에서의소비자보호에관한법률 -보존기간 : 3년 o 로그 기록 -보존이유: 통신비밀보호법 -보존기간 : 3개월 - -■ 개인정보의 파기절차 및 방법 회사는 원칙적으로 개인정보 수집 및 이용목적이 달성된 후에는 해당 정보를 지체없이 파기합니다. 파기절차 및 방법은 다음과 같습니다. o 파기절차 회원님이 회원가입 등을 위해 입력하신 정보는 목적이 달성된 후 별도의 DB로 옮겨져(종이의 경우 별도의 서류함) 내부 방침 및 기타 관련 법령에 의한 정보보호 사유에 따라(보유 및 이용기간 참조) 일정 기간 저장된 후 파기되어집니다. 별도 DB로 옮겨진 개인정보는 법률에 의한 경우가 아니고서는 보유되어지는 이외의 다른 목적으로 이용되지 않습니다. o 파기방법 전자적 파일형태로 저장된 개인정보는 기록을 재생할 수 없는 기술적 방법을 사용하여 삭제합니다. - -■ 개인정보 제공 회사는 이용자의 개인정보를 원칙적으로 외부에 제공하지 않습니다. 다만, 아래의 경우에는 예외로 합니다. o 이용자들이 사전에 동의한 경우 o 법령의 규정에 의거하거나, 수사 목적으로 법령에 정해진 절차와 방법에 따라 수사기관의 요구가 있는 경우 - - ■ 이용자 및 법정대리인의 권리와 그 행사방법 o 이용자는 언제든지 등록되어 있는 자신의 개인정보를 조회하거나 수정할 수 있으며 가입해지를 요청할 수도 있습니다. o 이용자들의 개인정보 조회,수정을 위해서는 "개인정보변경"(또는 "회원정보수정" 등)을 가입해지(동의철회)를 위해서는 "회원탈퇴"를 클릭하여 본인 확인 절차를 거치신 후 직접 열람, 정정 또는 탈퇴가 가능합니다. o 혹은 개인정보보호책임자에게 서면, 전화 또는 이메일로 연락하시면 지체없이 조치하겠습니다. o 귀하가 개인정보의 오류에 대한 정정을 요청하신 경우에는 정정을 완료하기 전까지 당해 개인정보를 이용 또는 제공하지 않습니다. 또한 잘못된 개인정보를 제3자에게 이미 제공한 경우에는 정정 처리결과를 제3자에게 지체없이 통지하여 정정이 이루어지도록 하겠습니다. o 회사는 이용자의 요청에 의해 해지 또는 삭제된 개인정보는 "회사가 수집하는 개인정보의 보유 및 이용기간"에 명시된 바에 따라 처리하고 그 외의 용도로 열람 또는 이용할 수 없도록 처리하고 있습니다. - -■ 개인정보 자동수집 장치의 설치, 운영 및 그 거부에 관한 사항 회사는 귀하의 정보를 수시로 저장하고 찾아내는 "쿠키(cookie)" 등을 운용합니다. 쿠키란 웹사이트를 운영하는데 이용되는 서버가 귀하의 브라우저에 보내는 아주 작은 텍스트 파일로서 귀하의 컴퓨터 하드디스크에 저장됩니다. 회사은(는) 다음과 같은 목적을 위해 쿠키를 사용합니다. o 쿠키 등 사용 목적 1. 회원과 비회원의 접속 빈도나 방문 시간 등을 분석, 이용자의 취향과 관심분야를 파악 및 자취 추적, 각종 이벤트 참여 정도 및 방문 회수 파악 등을 통한 타겟 마케팅 및 개인 맞춤 서비스 제공 2. 귀하는 쿠키 설치에 대한 선택권을 가지고 있습니다. 따라서, 귀하는 웹브라우저에서 옵션을 설정함으로써 모든 쿠키를 허용하거나, 쿠키가 저장될 때마다 확인을 거치거나, 아니면 모든 쿠키의 저장을 거부할 수도 있습니다. o 쿠키 설정 거부 방법 1. 쿠키 설정을 거부하는 방법으로는 회원님이 사용하시는 웹 브라우저의 옵션을 선택함으로써 모든 쿠키를 허용하거나 쿠키를 저장할 때마다 확인을 거치거나, 모든 쿠키의 저장을 거부할 수 있습니다. 2. 설정방법 예(인터넷 익스플로어의 경우) : 웹 브라우저 상단의 도구 > 인터넷 옵션 > 개인정보 3. 단, 귀하께서 쿠키 설치를 거부하였을 경우 서비스 제공에 어려움이 있을 수 있습니다. - -■ 개인정보에 관한 민원서비스 회사는 고객의 개인정보를 보호하고 개인정보와 관련한 불만을 처리하기 위하여 아래와 같이 관련 부서 및 개인정보보호책임자를 지정하고 있습니다. o 개인정보보호담당자 성명 : $name 소속 : 개인 전화번호 : $phone 이메일 : $email - -o 개인정보보호책임자 성명 : $name : 개인 전화번호 : $phone 이메일 : $email - -o 귀하께서는 회사의 서비스를 이용하시며 발생하는 모든 개인정보보호 관련 민원을 개인정보보호책임자 혹은 담당부서로 신고하실 수 있습니다. o 회사는 이용자들의 신고사항에 대해 신속하게 충분한 답변을 드릴 것입니다. o 기타 개인정보침해에 대한 신고나 상담이 필요하신 경우에는 아래 기관에 문의하시기 바랍니다. 개인정보침해신고센터 (privacy.kisa.or.kr / 국번 없이 118) 개인정보분쟁조정위원회 (kopico.go.kr / 1833-6972) 대검찰청 사이버수사과 (spo.go.kr / 지역번호+1301) 경찰청 사이버안전국 (cyberbureau.police.go.kr / 국번없이 182) - - '''; -} diff --git a/lib/feature/signup/widget/TermsOfServiceWidget.dart b/lib/feature/signup/widget/TermsOfServiceWidget.dart deleted file mode 100644 index e93ed58..0000000 --- a/lib/feature/signup/widget/TermsOfServiceWidget.dart +++ /dev/null @@ -1,141 +0,0 @@ -import 'package:flutter/material.dart'; - -class TermsOfServiceWidget extends StatefulWidget { - final VoidCallback onNext; - - const TermsOfServiceWidget({super.key, required this.onNext}); - - @override - State createState() => _TermsOfServicePageState(); -} - -class _TermsOfServicePageState extends State { - final ScrollController _scrollController = ScrollController(); - bool isBottom = false; - - @override - void initState() { - _scrollController.addListener(() { - if (_scrollController.position.pixels == - _scrollController.position.maxScrollExtent) { - setState(() { - isBottom = true; - }); - } else { - setState(() { - isBottom = false; - }); - } - }); - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('개인정보처리방침'), - ), - body: SingleChildScrollView( - controller: _scrollController, - padding: const EdgeInsets.all(16.0), - child: getTerms(), - ), - floatingActionButton: (FloatingActionButton( - onPressed: () { - isBottom - ? widget.onNext() - : _scrollController.animateTo( - _scrollController.position.maxScrollExtent, - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); - }, - child: const Icon(Icons.check), - ))); - } -} - -Column getTerms() { - const String companyName = "블루베리 스토어"; - return const Column( - children: [ - Text('전자상거래(인터넷사이버몰) 표준약관'), - Text('표준약관 제10023호\n(2015. 6. 26. 개정)'), - SizedBox(height: 16), - Text( - '제1조(목적) 이 약관은 $companyName(전자상거래 사업자)가 운영하는 OO 사이버 몰(이하 “몰”이라 한다)에서 제공하는 인터넷 관련 서비스(이하 “서비스”라 한다)를 이용함에 있어 사이버 몰과 이용자의 권리․의무 및 책임사항을 규정함을 목적으로 합니다.'), - SizedBox(height: 16), - Text('※「PC통신, 무선 등을 이용하는 전자상거래에 대해서도 그 성질에 반하지 않는 한 이 약관을 준용합니다.」'), - SizedBox(height: 16), - Text( - '제2조(정의)\n ① “몰”이란 $companyName가 재화 또는 용역(이하 “재화 등”이라 함)을 이용자에게 제공하기 위하여 컴퓨터 등 정보통신설비를 이용하여 재화 등을 거래할 수 있도록 설정한 가상의 영업장을 말하며, 아울러 사이버몰을 운영하는 사업자의 의미로도 사용합니다.\n ② “이용자”란 “몰”에 접속하여 이 약관에 따라 “몰”이 제공하는 서비스를 받는 회원 및 비회원을 말합니다.\n ③ ‘회원’이라 함은 “몰”에 회원등록을 한 자로서, 계속적으로 “몰”이 제공하는 서비스를 이용할 수 있는 자를 말합니다.\n ④ ‘비회원’이라 함은 회원에 가입하지 않고 “몰”이 제공하는 서비스를 이용하는 자를 말합니다.'), - SizedBox(height: 16), - Text( - '제3조 (약관 등의 명시와 설명 및 개정)\n① “몰”은 이 약관의 내용과 상호 및 대표자 성명, 영업소 소재지 주소(소비자의 불만을 처리할 수 있는 곳의 주소를 포함), 전화번호․모사전송번호․전자우편주소, 사업자등록번호, 통신판매업 신고번호, 개인정보관리책임자등을 이용자가 쉽게 알 수 있도록 00 사이버몰의 초기 서비스화면(전면)에 게시합니다. 다만, 약관의 내용은 이용자가 연결화면을 통하여 볼 수 있도록 할 수 있습니다.② “몰은 이용자가 약관에 동의하기에 앞서 약관에 정하여져 있는 내용 중 청약철회․배송책임․환불조건 등과 같은 중요한 내용을 이용자가 이해할 수 있도록 별도의 연결화면 또는 팝업화면 등을 제공하여 이용자의 확인을 구하여야 합니다.③ “몰”은 「전자상거래 등에서의 소비자보호에 관한 법률」, 「약관의 규제에 관한 법률」, 「전자문서 및 전자거래기본법」, 「전자금융거래법」, 「전자서명법」, 「정보통신망 이용촉진 및 정보보호 등에 관한 법률」, 「방문판매 등에 관한 법률」, 「소비자기본법」 등 관련 법을 위배하지 않는 범위에서 이 약관을 개정할 수 있습니다.④ “몰”이 약관을 개정할 경우에는 적용일자 및 개정사유를 명시하여 현행약관과 함께 몰의 초기화면에 그 적용일자 7일 이전부터 적용일자 전일까지 공지합니다. 다만, 이용자에게 불리하게 약관내용을 변경하는 경우에는 최소한 30일 이상의 사전 유예기간을 두고 공지합니다. 이 경우 "몰“은 개정 전 내용과 개정 후 내용을 명확하게 비교하여 이용자가 알기 쉽도록 표시합니다.⑤ “몰”이 약관을 개정할 경우에는 그 개정약관은 그 적용일자 이후에 체결되는 계약에만 적용되고 그 이전에 이미 체결된 계약에 대해서는 개정 전의 약관조항이 그대로 적용됩니다. 다만 이미 계약을 체결한 이용자가 개정약관 조항의 적용을 받기를 원하는 뜻을 제3항에 의한 개정약관의 공지기간 내에 “몰”에 송신하여 “몰”의 동의를 받은 경우에는 개정약관 조항이 적용됩니다.⑥ 이 약관에서 정하지 아니한 사항과 이 약관의 해석에 관하여는 전자상거래 등에서의 소비자보호에 관한 법률, 약관의 규제 등에 관한 법률, 공정거래위원회가 정하는 전자상거래 등에서의 소비자 보호지침 및 관계법령 또는 상관례에 따릅니다.'), - SizedBox(height: 16), - Text( - '제4조(서비스의 제공 및 변경)\n① “몰”은 다음과 같은 업무를 수행합니다. 1. 재화 또는 용역에 대한 정보 제공 및 구매계약의 체결 2. 구매계약이 체결된 재화 또는 용역의 배송 3. 기타 “몰”이 정하는 업무 ② “몰”은 재화 또는 용역의 품절 또는 기술적 사양의 변경 등의 경우에는 장차 체결되는 계약에 의해 제공할 재화 또는 용역의 내용을 변경할 수 있습니다. 이 경우에는 변경된 재화 또는 용역의 내용 및 제공일자를 명시하여 현재의 재화 또는 용역의 내용을 게시한 곳에 즉시 공지합니다. ③ “몰”이 제공하기로 이용자와 계약을 체결한 서비스의 내용을 재화등의 품절 또는 기술적 사양의 변경 등의 사유로 변경할 경우에는 그 사유를 이용자에게 통지 가능한 주소로 즉시 통지합니다. ④ 전항의 경우 “몰”은 이로 인하여 이용자가 입은 손해를 배상합니다. 다만, “몰”이 고의 또는 과실이 없음을 입증하는 경우에는 그러하지 아니합니다.'), - SizedBox(height: 16), - Text( - '제5조(서비스의 중단)\n① “몰”은 컴퓨터 등 정보통신설비의 보수점검․교체 및 고장, 통신의 두절 등의 사유가 발생한 경우에는 서비스의 제공을 일시적으로 중단할 수 있습니다. ② “몰”은 제1항의 사유로 서비스의 제공이 일시적으로 중단됨으로 인하여 이용자 또는 제3자가 입은 손해에 대하여 배상합니다. 단, “몰”이 고의 또는 과실이 없음을 입증하는 경우에는 그러하지 아니합니다. ③ 사업종목의 전환, 사업의 포기, 업체 간의 통합 등의 이유로 서비스를 제공할 수 없게 되는 경우에는 “몰”은 제8조에 정한 방법으로 이용자에게 통지하고 당초 “몰”에서 제시한 조건에 따라 소비자에게 보상합니다. 다만, “몰”이 보상기준 등을 고지하지 아니한 경우에는 이용자들의 마일리지 또는 적립금 등을 “몰”에서 통용되는 통화가치에 상응하는 현물 또는 현금으로 이용자에게 지급합니다.'), - SizedBox(height: 16), - Text( - '제6조(회원가입)\n① 이용자는 “몰”이 정한 가입 양식에 따라 회원정보를 기입한 후 이 약관에 동의한다는 의사표시를 함으로서 회원가입을 신청합니다. ② “몰”은 제1항과 같이 회원으로 가입할 것을 신청한 이용자 중 다음 각 호에 해당하지 않는 한 회원으로 등록합니다.1. 가입신청자가 이 약관 제7조제3항에 의하여 이전에 회원자격을 상실한 적이 있는 경우, 다만 제7조제3항에 의한 회원자격 상실 후 3년이 경과한 자로서 “몰”의 회원재가입 승낙을 얻은 경우에는 예외로 한다.2. 등록 내용에 허위, 기재누락, 오기가 있는 경우3. 기타 회원으로 등록하는 것이 “몰”의 기술상 현저히 지장이 있다고 판단되는 경우③ 회원가입계약의 성립 시기는 “몰”의 승낙이 회원에게 도달한 시점으로 합니다.④ 회원은 회원가입 시 등록한 사항에 변경이 있는 경우, 상당한 기간 이내에 “몰”에 대하여 회원정보 수정 등의 방법으로 그 변경사항을 알려야 합니다.'), - SizedBox(height: 16), - Text( - '제7조(회원 탈퇴 및 자격 상실 등)\n ① 회원은 “몰”에 언제든지 탈퇴를 요청할 수 있으며 “몰”은 즉시 회원탈퇴를 처리합니다.② 회원이 다음 각 호의 사유에 해당하는 경우, “몰”은 회원자격을 제한 및 정지시킬 수 있습니다.1. 가입 신청 시에 허위 내용을 등록한 경우2. “몰”을 이용하여 구입한 재화 등의 대금, 기타 “몰”이용에 관련하여 회원이 부담하는 채무를 기일에 지급하지 않는 경우3. 다른 사람의 “몰” 이용을 방해하거나 그 정보를 도용하는 등 전자상거래 질서를 위협하는 경우4. “몰”을 이용하여 법령 또는 이 약관이 금지하거나 공서양속에 반하는 행위를 하는 경우③ “몰”이 회원 자격을 제한․정지 시킨 후, 동일한 행위가 2회 이상 반복되거나 30일 이내에 그 사유가 시정되지 아니하는 경우 “몰”은 회원자격을 상실시킬 수 있습니다.④ “몰”이 회원자격을 상실시키는 경우에는 회원등록을 말소합니다. 이 경우 회원에게 이를 통지하고, 회원등록 말소 전에 최소한 30일 이상의 기간을 정하여 소명할 기회를 부여합니다.'), - SizedBox(height: 16), - Text( - '제8조(회원에 대한 통지)\n ① “몰”이 회원에 대한 통지를 하는 경우, 회원이 “몰”과 미리 약정하여 지정한 전자우편 주소로 할 수 있습니다. ② “몰”은 불특정다수 회원에 대한 통지의 경우 1주일이상 “몰” 게시판에 게시함으로서 개별 통지에 갈음할 수 있습니다. 다만, 회원 본인의 거래와 관련하여 중대한 영향을 미치는 사항에 대하여는 개별통지를 합니다.'), - SizedBox(height: 16), - Text( - '제9조(구매신청 및 개인정보 제공 동의 등) \n① “몰”이용자는 “몰”상에서 다음 또는 이와 유사한 방법에 의하여 구매를 신청하며, “몰”은 이용자가 구매신청을 함에 있어서 다음의 각 내용을 알기 쉽게 제공하여야 합니다. 1. 재화 등의 검색 및 선택 2. 받는 사람의 성명, 주소, 전화번호, 전자우편주소(또는 이동전화번호) 등의 입력 3. 약관내용, 청약철회권이 제한되는 서비스, 배송료․설치비 등의 비용부담과 관련한 내용에 대한 확인 4. 이 약관에 동의하고 위 3.호의 사항을 확인하거나 거부하는 표시 (예, 마우스 클릭) 5. 재화등의 구매신청및 이에 관한 확인 또는 “몰”의 확인에 대한 동의 6. 결제방법의 선택 ② “몰”이 제3자에게 구매자 개인정보를 제공할 필요가 있는 경우 1) 개인정보를 제공받는 자, 2)개인정보를 제공받는 자의 개인정보 이용목적, 3) 제공하는 개인정보의 항목, 4) 개인정보를 제공받는 자의 개인정보 보유 및 이용기간을 구매자에게 알리고 동의를 받아야 합니다. (동의를 받은 사항이 변경되는 경우에도 같습니다.) ③ “몰”이 제3자에게 구매자의 개인정보를 취급할 수 있도록 업무를 위탁하는 경우에는 1) 개인정보 취급위탁을 받는 자, 2) 개인정보 취급위탁을 하는 업무의 내용을 구매자에게 알리고 동의를 받아야 합니다. (동의를 받은 사항이 변경되는 경우에도 같습니다.) 다만, 서비스제공에 관한 계약이행을 위해 필요하고 구매자의 편의증진과 관련된 경우에는 「정보통신망 이용촉진 및 정보보호 등에 관한 법률」에서 정하고 있는 방법으로 개인정보 취급방침을 통해 알림으로써 고지절차와 동의절차를 거치지 않아도 됩니다.'), - SizedBox(height: 16), - Text( - '제10조 (계약의 성립) \n ① “몰”은 제9조와 같은 구매신청에 대하여 다음 각 호에 해당하면 승낙하지 않을 수 있습니다. 다만, 미성년자와 계약을 체결하는 경우에는 법정대리인의 동의를 얻지 못하면 미성년자 본인 또는 법정대리인이 계약을 취소할 수 있다는 내용을 고지하여야 합니다. 1. 신청 내용에 허위, 기재누락, 오기가 있는 경우 2. 미성년자가 담배, 주류 등 청소년보호법에서 금지하는 재화 및 용역을 구매하는 경우 3. 기타 구매신청에 승낙하는 것이 “몰” 기술상 현저히 지장이 있다고 판단하는 경우 ② “몰”의 승낙이 제12조제1항의 수신확인통지형태로 이용자에게 도달한 시점에 계약이 성립한 것으로 봅니다. ③ “몰”의 승낙의 의사표시에는 이용자의 구매 신청에 대한 확인 및 판매가능 여부, 구매신청의 정정 취소 등에 관한 정보 등을 포함하여야 합니다.'), - SizedBox(height: 16), - Text( - '제11조(지급방법) \n“몰”에서 구매한 재화 또는 용역에 대한 대금지급방법은 다음 각 호의 방법중 가용한 방법으로 할 수 있습니다. 단, “몰”은 이용자의 지급방법에 대하여 재화 등의 대금에 어떠한 명목의 수수료도 추가하여 징수할 수 없습니다.1. 폰뱅킹, 인터뱅킹, 메일 뱅킹 등의 각종 계좌이체 2. 선불카드, 직불카드, 신용카드 등의 각종 카드 결제3. 온라인무통장입금4. 전자화폐에 의한 결제5. 수령 시 대금지급6. 마일리지 등 “몰”이 지급한 포인트에 의한 결제7. “몰”과 계약을 맺었거나 “몰”이 인정한 상품권에 의한 결제 8. 기타 전자적 지급 방법에 의한 대금 지급 등'), - SizedBox(height: 16), - Text( - '제12조(수신확인통지․구매신청 변경 및 취소)\n① “몰”은 이용자의 구매신청이 있는 경우 이용자에게 수신확인통지를 합니다. ② 수신확인통지를 받은 이용자는 의사표시의 불일치 등이 있는 경우에는 수신확인통지를 받은 후 즉시 구매신청 변경 및 취소를 요청할 수 있고 “몰”은 배송 전에 이용자의 요청이 있는 경우에는 지체 없이 그 요청에 따라 처리하여야 합니다. 다만 이미 대금을 지불한 경우에는 제15조의 청약철회 등에 관한 규정에 따릅니다.'), - SizedBox(height: 16), - Text( - '제13조(재화 등의 공급) \n① “몰”은 이용자와 재화 등의 공급시기에 관하여 별도의 약정이 없는 이상, 이용자가 청약을 한 날부터 7일 이내에 재화 등을 배송할 수 있도록 주문제작, 포장 등 기타의 필요한 조치를 취합니다. 다만, “몰”이 이미 재화 등의 대금의 전부 또는 일부를 받은 경우에는 대금의 전부 또는 일부를 받은 날부터 3영업일 이내에 조치를 취합니다. 이때 “몰”은 이용자가 재화 등의 공급 절차 및 진행 사항을 확인할 수 있도록 적절한 조치를 합니다. ② “몰”은 이용자가 구매한 재화에 대해 배송수단, 수단별 배송비용 부담자, 수단별 배송기간 등을 명시합니다. 만약 “몰”이 약정 배송기간을 초과한 경우에는 그로 인한 이용자의 손해를 배상하여야 합니다. 다만 “몰”이 고의․과실이 없음을 입증한 경우에는 그러하지 아니합니다.'), - SizedBox(height: 16), - Text( - '제14조(환급) “몰”은 이용자가 구매신청한 재화 등이 품절 등의 사유로 인도 또는 제공을 할 수 없을 때에는 지체 없이 그 사유를 이용자에게 통지하고 사전에 재화 등의 대금을 받은 경우에는 대금을 받은 날부터 3영업일 이내에 환급하거나 환급에 필요한 조치를 취합니다.'), - SizedBox(height: 16), - Text( - '제15조(청약철회 등) \n① “몰”과 재화등의 구매에 관한 계약을 체결한 이용자는 「전자상거래 등에서의 소비자보호에 관한 법률」 제13조 제2항에 따른 계약내용에 관한 서면을 받은 날(그 서면을 받은 때보다 재화 등의 공급이 늦게 이루어진 경우에는 재화 등을 공급받거나 재화 등의 공급이 시작된 날을 말합니다)부터 7일 이내에는 청약의 철회를 할 수 있습니다. 다만, 청약철회에 관하여 「전자상거래 등에서의 소비자보호에 관한 법률」에 달리 정함이 있는 경우에는 동 법 규정에 따릅니다. ② 이용자는 재화 등을 배송 받은 경우 다음 각 호의 1에 해당하는 경우에는 반품 및 교환을 할 수 없습니다. 1. 이용자에게 책임 있는 사유로 재화 등이 멸실 또는 훼손된 경우(다만, 재화 등의 내용을 확인하기 위하여 포장 등을 훼손한 경우에는 청약철회를 할 수 있습니다) 2. 이용자의 사용 또는 일부 소비에 의하여 재화 등의 가치가 현저히 감소한 경우 3. 시간의 경과에 의하여 재판매가 곤란할 정도로 재화등의 가치가 현저히 감소한 경우 4. 같은 성능을 지닌 재화 등으로 복제가 가능한 경우 그 원본인 재화 등의 포장을 훼손한 경우 ③ 제2항제2호 내지 제4호의 경우에 “몰”이 사전에 청약철회 등이 제한되는 사실을 소비자가 쉽게 알 수 있는 곳에 명기하거나 시용상품을 제공하는 등의 조치를 하지 않았다면 이용자의 청약철회 등이 제한되지 않습니다. ④ 이용자는 제1항 및 제2항의 규정에 불구하고 재화 등의 내용이 표시·광고 내용과 다르거나 계약내용과 다르게 이행된 때에는 당해 재화 등을 공급받은 날부터 3월 이내, 그 사실을 안 날 또는 알 수 있었던 날부터 30일 이내에 청약철회 등을 할 수 있습니다.'), - SizedBox(height: 16), - Text( - '제16조(청약철회 등의 효과)\n① “몰”은 이용자로부터 재화 등을 반환받은 경우 3영업일 이내에 이미 지급받은 재화 등의 대금을 환급합니다. 이 경우 “몰”이 이용자에게 재화등의 환급을 지연한때에는 그 지연기간에 대하여 「전자상거래 등에서의 소비자보호에 관한 법률 시행령」제21조의2에서 정하는 지연이자율을 곱하여 산정한 지연이자를 지급합니다. ② “몰”은 위 대금을 환급함에 있어서 이용자가 신용카드 또는 전자화폐 등의 결제수단으로 재화 등의 대금을 지급한 때에는 지체 없이 당해 결제수단을 제공한 사업자로 하여금 재화 등의 대금의 청구를 정지 또는 취소하도록 요청합니다. ③ 청약철회 등의 경우 공급받은 재화 등의 반환에 필요한 비용은 이용자가 부담합니다. “몰”은 이용자에게 청약철회 등을 이유로 위약금 또는 손해배상을 청구하지 않습니다. 다만 재화 등의 내용이 표시·광고 내용과 다르거나 계약내용과 다르게 이행되어 청약철회 등을 하는 경우 재화 등의 반환에 필요한 비용은 “몰”이 부담합니다. ④ 이용자가 재화 등을 제공받을 때 발송비를 부담한 경우에 “몰”은 청약철회 시 그 비용을 누가 부담하는지를 이용자가 알기 쉽도록 명확하게 표시합니다.'), - SizedBox(height: 16), - Text( - '제17조(개인정보보호)\n① “몰”은 이용자의 개인정보 수집시 서비스제공을 위하여 필요한 범위에서 최소한의 개인정보를 수집합니다. ② “몰”은 회원가입시 구매계약이행에 필요한 정보를 미리 수집하지 않습니다. 다만, 관련 법령상 의무이행을 위하여 구매계약 이전에 본인확인이 필요한 경우로서 최소한의 특정 개인정보를 수집하는 경우에는 그러하지 아니합니다. ③ “몰”은 이용자의 개인정보를 수집·이용하는 때에는 당해 이용자에게 그 목적을 고지하고 동의를 받습니다. ④ “몰”은 수집된 개인정보를 목적외의 용도로 이용할 수 없으며, 새로운 이용목적이 발생한 경우 또는 제3자에게 제공하는 경우에는 이용·제공단계에서 당해 이용자에게 그 목적을 고지하고 동의를 받습니다. 다만, 관련 법령에 달리 정함이 있는 경우에는 예외로 합니다. ⑤ “몰”이 제2항과 제3항에 의해 이용자의 동의를 받아야 하는 경우에는 개인정보관리 책임자의 신원(소속, 성명 및 전화번호, 기타 연락처), 정보의 수집목적 및 이용목적, 제3자에 대한 정보제공 관련사항(제공받은자, 제공목적 및 제공할 정보의 내용) 등 「정보통신망 이용촉진 및 정보보호 등에 관한 법률」 제22조제2항이 규정한 사항을 미리 명시하거나 고지해야 하며 이용자는 언제든지 이 동의를 철회할 수 있습니다. ⑥ 이용자는 언제든지 “몰”이 가지고 있는 자신의 개인정보에 대해 열람 및 오류정정을 요구할 수 있으며 “몰”은 이에 대해 지체 없이 필요한 조치를 취할 의무를 집니다. 이용자가 오류의 정정을 요구한 경우에는 “몰”은 그 오류를 정정할 때까지 당해 개인정보를 이용하지 않습니다. ⑦ “몰”은 개인정보 보호를 위하여 이용자의 개인정보를 취급하는 자를 최소한으로 제한하여야 하며 신용카드, 은행계좌 등을 포함한 이용자의 개인정보의 분실, 도난, 유출, 동의 없는 제3자 제공, 변조 등으로 인한 이용자의 손해에 대하여 모든 책임을 집니다. ⑧ “몰” 또는 그로부터 개인정보를 제공받은 제3자는 개인정보의 수집목적 또는 제공받은 목적을 달성한 때에는 당해 개인정보를 지체 없이 파기합니다. ⑨ “몰”은 개인정보의 수집·이용·제공에 관한 동의 란을 미리 선택한 것으로 설정해두지 않습니다. 또한 개인정보의 수집·이용·제공에 관한 이용자의 동의거절시 제한되는 서비스를 구체적으로 명시하고, 필수수집항목이 아닌 개인정보의 수집·이용·제공에 관한 이용자의 동의 거절을 이유로 회원가입 등 서비스 제공을 제한하거나 거절하지 않습니다.'), - SizedBox(height: 16), - Text( - '제18조(“몰“의 의무)\n① “몰”은 법령과 이 약관이 금지하거나 공서양속에 반하는 행위를 하지 않으며 이 약관이 정하는 바에 따라 지속적이고, 안정적으로 재화․용역을 제공하는데 최선을 다하여야 합니다. ② “몰”은 이용자가 안전하게 인터넷 서비스를 이용할 수 있도록 이용자의 개인정보(신용정보 포함)보호를 위한 보안 시스템을 갖추어야 합니다. ③ “몰”이 상품이나 용역에 대하여 「표시․광고의 공정화에 관한 법률」 제3조 소정의 부당한 표시․광고행위를 함으로써 이용자가 손해를 입은 때에는 이를 배상할 책임을 집니다. ④ “몰”은 이용자가 원하지 않는 영리목적의 광고성 전자우편을 발송하지 않습니다.'), - SizedBox(height: 16), - Text( - '제19조(회원의 ID 및 비밀번호에 대한 의무)\n① 제17조의 경우를 제외한 ID와 비밀번호에 관한 관리책임은 회원에게 있습니다. ② 회원은 자신의 ID 및 비밀번호를 제3자에게 이용하게 해서는 안됩니다. ③ 회원이 자신의 ID 및 비밀번호를 도난당하거나 제3자가 사용하고 있음을 인지한 경우에는 바로 “몰”에 통보하고 “몰”의 안내가 있는 경우에는 그에 따라야 합니다.'), - SizedBox(height: 16), - Text( - '제20조(이용자의 의무) 이용자는 다음 행위를 하여서는 안 됩니다.\n1. 신청 또는 변경시 허위 내용의 등록 2. 타인의 정보 도용 3. “몰”에 게시된 정보의 변경 4. “몰”이 정한 정보 이외의 정보(컴퓨터 프로그램 등) 등의 송신 또는 게시 5. “몰” 기타 제3자의 저작권 등 지적재산권에 대한 침해 6. “몰” 기타 제3자의 명예를 손상시키거나 업무를 방해하는 행위 7. 외설 또는 폭력적인 메시지, 화상, 음성, 기타 공서양속에 반하는 정보를 몰에 공개 또는 게시하는 행위'), - SizedBox(height: 16), - Text( - '제21조(연결“몰”과 피연결“몰” 간의 관계)\n ① 상위 “몰”과 하위 “몰”이 하이퍼링크(예: 하이퍼링크의 대상에는 문자, 그림 및 동화상 등이 포함됨)방식 등으로 연결된 경우, 전자를 연결 “몰”(웹 사이트)이라고 하고 후자를 피연결 “몰”(웹사이트)이라고 합니다. ② 연결“몰”은 피연결“몰”이 독자적으로 제공하는 재화 등에 의하여 이용자와 행하는 거래에 대해서 보증 책임을 지지 않는다는 뜻을 연결“몰”의 초기화면 또는 연결되는 시점의 팝업화면으로 명시한 경우에는 그 거래에 대한 보증 책임을 지지 않습니다.'), - SizedBox(height: 16), - Text( - '제22조(저작권의 귀속 및 이용제한)\n ① “몰“이 작성한 저작물에 대한 저작권 기타 지적재산권은 ”몰“에 귀속합니다. ② 이용자는 “몰”을 이용함으로써 얻은 정보 중 “몰”에게 지적재산권이 귀속된 정보를 “몰”의 사전 승낙 없이 복제, 송신, 출판, 배포, 방송 기타 방법에 의하여 영리목적으로 이용하거나 제3자에게 이용하게 하여서는 안됩니다. ③ “몰”은 약정에 따라 이용자에게 귀속된 저작권을 사용하는 경우 당해 이용자에게 통보하여야 합니다.'), - SizedBox(height: 16), - Text( - '제23조(분쟁해결)\n① “몰”은 이용자가 제기하는 정당한 의견이나 불만을 반영하고 그 피해를 보상처리하기 위하여 피해보상처리기구를 설치․운영합니다. ② “몰”은 이용자로부터 제출되는 불만사항 및 의견은 우선적으로 그 사항을 처리합니다. 다만, 신속한 처리가 곤란한 경우에는 이용자에게 그 사유와 처리일정을 즉시 통보해 드립니다. ③ “몰”과 이용자 간에 발생한 전자상거래 분쟁과 관련하여 이용자의 피해구제신청이 있는 경우에는 공정거래위원회 또는 시·도지사가 의뢰하는 분쟁조정기관의 조정에 따를 수 있습니다.'), - SizedBox(height: 16), - Text( - '제24조(재판권 및 준거법)\n ① “몰”과 이용자 간에 발생한 전자상거래 분쟁에 관한 소송은 제소 당시의 이용자의 주소에 의하고, 주소가 없는 경우에는 거소를 관할하는 지방법원의 전속관할로 합니다. 다만, 제소 당시 이용자의 주소 또는 거소가 분명하지 않거나 외국 거주자의 경우에는 민사소송법상의 관할법원에 제기합니다.\n ② “몰”과 이용자 간에 제기된 전자상거래 소송에는 한국법을 적용합니다.'), - ], - ); -} diff --git a/lib/feature/user_report/provider/ReportModalSheet.dart b/lib/feature/user_report/provider/ReportModalSheet.dart deleted file mode 100644 index b107d85..0000000 --- a/lib/feature/user_report/provider/ReportModalSheet.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import '../../../model/FriendModel.dart'; -import '../../../model/UserReportModel.dart'; -import '../../user_report/provider/userReportProvider.dart'; -import '../../../utils/AppStrings.dart'; - -class ReportModalSheet extends ConsumerWidget { - final FriendModel friend; - - const ReportModalSheet({super.key, required this.friend}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - return Container( - padding: const EdgeInsets.all(16), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text( - AppStrings.reportConfirmationMessage - .replaceFirst('%s', friend.name), - style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - ), - const SizedBox(height: 16), - ElevatedButton( - child: const Text(AppStrings.reportReasonSpamAccount), - onPressed: () => - reportUser(context, ref, AppStrings.reportReasonSpamAccount), - ), - const SizedBox(height: 8), - ElevatedButton( - child: const Text(AppStrings.reportReasonFakeAccount), - onPressed: () => - reportUser(context, ref, AppStrings.reportReasonFakeAccount), - ), - const SizedBox(height: 8), - ElevatedButton( - child: const Text(AppStrings.reportReasonInappropriateNamePhoto), - onPressed: () => reportUser( - context, ref, AppStrings.reportReasonInappropriateNamePhoto), - ), - ], - ), - ); - } - - void reportUser(BuildContext context, WidgetRef ref, String reason) async { - final report = UserReportModel( - reportedUserId: friend.userId, - reporterUserId: 'currentUserId', // 현재 로그인한 사용자의 ID로 대체 - reason: reason, - timestamp: DateTime.now(), - ); - - try { - await ref.read(userReportProvider.notifier).addReport(report); - if (!context.mounted) return; - Navigator.of(context).pop(); - showSnackBar(context, AppStrings.reportSuccessMessage); - } catch (e) { - if (!context.mounted) return; - showSnackBar(context, AppStrings.reportErrorMessage); - } - } - - void showSnackBar(BuildContext context, String message) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(message)), - ); - } -} diff --git a/lib/feature/user_report/provider/UserReportProvider.dart b/lib/feature/user_report/provider/UserReportProvider.dart deleted file mode 100644 index c9aa320..0000000 --- a/lib/feature/user_report/provider/UserReportProvider.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:cloud_firestore/cloud_firestore.dart'; - -import '../../../model/UserReportModel.dart'; - -// 사용자 신고 목록을 제공하는 StateNotifierProvider -final userReportProvider = - StateNotifierProvider>((ref) { - return UserReportNotifier(); -}); - -class UserReportNotifier extends StateNotifier> { - final FirebaseFirestore firestore = FirebaseFirestore.instance; - - UserReportNotifier() : super([]); - - Future addReport(UserReportModel report) async { - await firestore.collection('userReports').add(report.toJson()); - state = [...state, report]; - } -} diff --git a/lib/feature/voiceOutput/VoiceOutputScreen.dart b/lib/feature/voiceOutput/VoiceOutputScreen.dart index 148c0af..953798d 100644 --- a/lib/feature/voiceOutput/VoiceOutputScreen.dart +++ b/lib/feature/voiceOutput/VoiceOutputScreen.dart @@ -1,7 +1,10 @@ -import './widget/VoiceOutputWidget.dart'; import 'package:flutter/material.dart'; +import './widget/VoiceOutputWidget.dart'; + class VoiceOutputScreen extends StatelessWidget { + static const name = 'VoiceOutputScreen'; + const VoiceOutputScreen({super.key}); @override diff --git a/lib/main.dart b/lib/main.dart index 8250350..1c6c798 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -9,7 +9,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:intl/date_symbol_data_local.dart'; import 'core/provider/ThemeProvider.dart'; import 'firebase_options.dart'; @@ -40,7 +39,6 @@ Future main() async { await dotenv.load(fileName: '.env'); } - await initializeDateFormatting('en_US', null); runApp(const ProviderScope( child: MyApp(), )); diff --git a/lib/model/AnimalModel.dart b/lib/model/AnimalModel.dart deleted file mode 100644 index aeac7cf..0000000 --- a/lib/model/AnimalModel.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'generated/AnimalModel.freezed.dart'; -part 'generated/AnimalModel.g.dart'; - -@freezed -class AnimalModel with _$AnimalModel { - const factory AnimalModel({ - required int animalId, - required int userId, - required String name, - String? species, - String? breed, - int? age, - String? gender, - String? profilePicture, - String? bio, - }) = _AnimalModel; - - factory AnimalModel.fromJson(Map json) => - _$AnimalModelFromJson(json); -} diff --git a/lib/model/ChatListItemModel.dart b/lib/model/ChatListItemModel.dart deleted file mode 100644 index bd9b273..0000000 --- a/lib/model/ChatListItemModel.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'generated/ChatListItemModel.freezed.dart'; -part 'generated/ChatListItemModel.g.dart'; - -@freezed -class ChatListItemModel with _$ChatListItemModel { - const factory ChatListItemModel({ - required String roomId, - required String otherUserId, - required String lastMessage, - required DateTime lastMessageTime, - }) = _ChatMessageModel; - - factory ChatListItemModel.fromJson(Map json) => - _$ChatListItemModelFromJson(json); -} diff --git a/lib/model/ChatMessageModel.dart b/lib/model/ChatMessageModel.dart deleted file mode 100644 index 550f87b..0000000 --- a/lib/model/ChatMessageModel.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'generated/ChatMessageModel.freezed.dart'; -part 'generated/ChatMessageModel.g.dart'; - -@freezed -class ChatMessageModel with _$ChatMessageModel { - const factory ChatMessageModel({ - required String senderId, - required String message, - required DateTime timestamp, - required bool isRead, - }) = _ChatMessageModel; - - factory ChatMessageModel.fromJson(Map json) => - _$ChatMessageModelFromJson(json); -} diff --git a/lib/model/DogProfileModel.dart b/lib/model/DogProfileModel.dart deleted file mode 100644 index dd4ccd6..0000000 --- a/lib/model/DogProfileModel.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'generated/DogProfileModel.freezed.dart'; -part 'generated/DogProfileModel.g.dart'; - -@freezed -class DogProfileModel with _$DogProfileModel { - const factory DogProfileModel( - {required String name, - required String gender, - required String breed, - required String bio, - required String imageUrl, - required String location, - required String petID, - required String userID}) = _DogProfileModel; - - factory DogProfileModel.fromJson(Map json) => - _$DogProfileModelFromJson(json); -} diff --git a/lib/model/FriendModel.dart b/lib/model/FriendModel.dart deleted file mode 100644 index 79eec39..0000000 --- a/lib/model/FriendModel.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'generated/FriendModel.freezed.dart'; -part 'generated/FriendModel.g.dart'; - -@freezed -class FriendModel with _$FriendModel { - const factory FriendModel({ - required String userId, - required String friendId, - required String name, - required String status, - required String imageName, - required int likes, - required DateTime lastConnect, - }) = _FriendModel; - - factory FriendModel.fromJson(Map json) => - _$FriendModelFromJson(json); -} diff --git a/lib/model/GoogleMapPlace.dart b/lib/model/GoogleMapPlaceModel.dart similarity index 56% rename from lib/model/GoogleMapPlace.dart rename to lib/model/GoogleMapPlaceModel.dart index 6e5737a..c9e5097 100644 --- a/lib/model/GoogleMapPlace.dart +++ b/lib/model/GoogleMapPlaceModel.dart @@ -1,15 +1,17 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; -class Place { +class GoogleMapPlaceModel { final String name; final LatLng location; final String? phoneNumber; - Place({required this.name, required this.location, this.phoneNumber}); + GoogleMapPlaceModel( + {required this.name, required this.location, this.phoneNumber}); - factory Place.fromJson(Map json, String? phoneNumber) { + factory GoogleMapPlaceModel.fromJson( + Map json, String? phoneNumber) { final location = json['geometry']['location']; - return Place( + return GoogleMapPlaceModel( name: json['name'], location: LatLng(location['lat'], location['lng']), phoneNumber: phoneNumber, diff --git a/lib/model/LikeModel.dart b/lib/model/LikeModel.dart deleted file mode 100644 index e38a931..0000000 --- a/lib/model/LikeModel.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'generated/LikeModel.freezed.dart'; -part 'generated/LikeModel.g.dart'; - -@freezed -class LikeModel with _$LikeModel { - const factory LikeModel({ - required int likeId, - required int userId, - required int postId, - required DateTime createdAt, - }) = _LikeModel; - - factory LikeModel.fromJson(Map json) => - _$LikeModelFromJson(json); -} diff --git a/lib/model/MBTIModel.dart b/lib/model/MBTIModel.dart deleted file mode 100644 index 69b1712..0000000 --- a/lib/model/MBTIModel.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -import '../feature/mbti/widget/MBTIHomeWidget.dart'; - -part 'generated/MBTIModel.freezed.dart'; -part 'generated/MBTIModel.g.dart'; - -@freezed -class MBTIModel with _$MBTIModel { - const MBTIModel._(); - const factory MBTIModel({ - required Extroversion extroversion, - required Sensing sensing, - required Thinking thinking, - required Judging judging, - }) = _MBTIModel; - - factory MBTIModel.fromJson(Map json) => - _$MBTIModelFromJson(json); - - // 2진수 각 자리 index 위치 MBTI 검색 - MBTIType getMBTI() { - int index = 0; - - if (extroversion == Extroversion.E) index += 8; - if (sensing == Sensing.S) index += 4; - if (thinking == Thinking.T) index += 2; - if (judging == Judging.J) index += 1; - - return MBTIType.values[index]; - } -} diff --git a/lib/model/MBTIQuestionModel.dart b/lib/model/MBTIQuestionModel.dart deleted file mode 100644 index 38dfb5d..0000000 --- a/lib/model/MBTIQuestionModel.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'generated/MBTIQuestionModel.freezed.dart'; -part 'generated/MBTIQuestionModel.g.dart'; - -@freezed -class MBTIQuestionModel with _$MBTIQuestionModel { - const factory MBTIQuestionModel({ - required String question, - required String type, - required String imageUrl, - }) = _MBTIQuestionModel; - - factory MBTIQuestionModel.fromJson(Map json) => - _$MBTIQuestionModelFromJson(json); -} diff --git a/lib/model/PaymentModel.dart b/lib/model/PaymentModel.dart deleted file mode 100644 index b3a7407..0000000 --- a/lib/model/PaymentModel.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'generated/PaymentModel.freezed.dart'; -part 'generated/PaymentModel.g.dart'; - -@freezed -class PaymentModel with _$PaymentModel { - const factory PaymentModel({ - required int paymentId, - required int userId, - required double amount, - required String paymentMethod, - required DateTime createdAt, - }) = _PaymentModel; - - factory PaymentModel.fromJson(Map json) => - _$PaymentModelFromJson(json); -} diff --git a/lib/model/PersonModel.dart b/lib/model/PersonModel.dart deleted file mode 100644 index dfc493a..0000000 --- a/lib/model/PersonModel.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'generated/PersonModel.freezed.dart'; -part 'generated/PersonModel.g.dart'; - -@freezed -class PersonModel with _$PersonModel { - const factory PersonModel({ - required String name, - required int age, - }) = _PersonModel; - - factory PersonModel.fromJson(Map json) => - _$PersonModelFromJson(json); -} diff --git a/lib/model/PostModel.dart b/lib/model/PostModel.dart deleted file mode 100644 index ae7e5ed..0000000 --- a/lib/model/PostModel.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'generated/PostModel.freezed.dart'; -part 'generated/PostModel.g.dart'; - -@freezed -class PostModel with _$PostModel { - const factory PostModel({ - required String title, - required String content, - required String imageUrl, - required String uploadTime, - }) = _PostModel; - - factory PostModel.fromJson(Map json) => - _$PostModelFromJson(json); -} diff --git a/lib/model/RankingModel.dart b/lib/model/RankingModel.dart deleted file mode 100644 index 8489925..0000000 --- a/lib/model/RankingModel.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'generated/RankingModel.freezed.dart'; -part 'generated/RankingModel.g.dart'; - -@freezed -class RankingModel with _$RankingModel { - const factory RankingModel({ - required int userId, - required int rank, - required int score, - required DateTime updatedAt, - }) = _RankingModel; - - factory RankingModel.fromJson(Map json) => - _$RankingModelFromJson(json); -} diff --git a/lib/model/SaleItemModel.dart b/lib/model/SaleItemModel.dart deleted file mode 100644 index 206f125..0000000 --- a/lib/model/SaleItemModel.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'generated/SaleItemModel.freezed.dart'; -part 'generated/SaleItemModel.g.dart'; - -@freezed -class SaleItemModel with _$SaleItemModel { - const factory SaleItemModel({ - required int id, - required String name, - required String description, - required double price, - required int quantity, - required String imageUrl, - }) = _SaleItem; - - factory SaleItemModel.fromJson(Map json) => - _$SaleItemModelFromJson(json); -} diff --git a/lib/model/UserDataModel.dart b/lib/model/UserDataModel.dart deleted file mode 100644 index c940421..0000000 --- a/lib/model/UserDataModel.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'generated/UserDataModel.freezed.dart'; -part 'generated/UserDataModel.g.dart'; - -@freezed -class UserDataModel with _$UserDataModel { - factory UserDataModel( - {@Default('Unknown') String name, - @Default('No email') String email, - @Default('NULL') String mbti}) = _UserDataModel; - - factory UserDataModel.fromJson(Map json) => - _$UserDataModelFromJson(json); -} diff --git a/lib/router/RouterProvider.dart b/lib/router/RouterProvider.dart index a620226..ef0abb5 100644 --- a/lib/router/RouterProvider.dart +++ b/lib/router/RouterProvider.dart @@ -1,10 +1,3 @@ -import 'package:blueberry_flutter_template/feature/admin/AdminLoadingPage.dart'; -import 'package:blueberry_flutter_template/feature/admin/AdminScreen.dart'; -import 'package:blueberry_flutter_template/feature/admin/AdminUserDetailPage.dart'; -import 'package:blueberry_flutter_template/feature/admin/AdminUserListPage.dart'; -import 'package:blueberry_flutter_template/feature/chat/ChatScreen.dart'; -import 'package:blueberry_flutter_template/feature/payment/widget/WebPaymentWidget.dart'; -import 'package:blueberry_flutter_template/feature/setting/SettingScreen.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; @@ -12,15 +5,6 @@ import 'package:talker_flutter/talker_flutter.dart'; import '../core/SplashScreen.dart'; import '../core/TopScreen.dart'; -import '../feature/camera/SettingInsideAccountManagerWidget.dart'; -import '../feature/chat/ChatRoomScreen.dart'; -import '../feature/friend/FriendsListScreen.dart'; -import '../feature/mbti/MBTIScreen.dart'; -import '../feature/mypage/MyPageScreen.dart'; -import '../feature/post/PostingScreen.dart'; -import '../feature/profile/ProfileDetailScreen.dart'; -import '../feature/rank/RankScreen.dart'; -import '../feature/signup/SignUpScreen.dart'; import '../utils/ResponsiveLayoutBuilder.dart'; import '../utils/Talker.dart'; @@ -42,104 +26,7 @@ final routerProvider = Provider((ref) { name: TopScreen.name, builder: (context, state) => ResponsiveLayoutBuilder(context, const TopScreen()), - routes: [ - GoRoute( - path: 'signup', - name: SignUpScreen.name, - builder: (context, state) => - ResponsiveLayoutBuilder(context, const SignUpScreen()), - ), - GoRoute( - path: 'setting', - name: SettingScreen.name, - builder: (context, state) => - ResponsiveLayoutBuilder(context, const SettingScreen()), - ), - GoRoute( - path: 'posting', - name: PostingScreen.name, - builder: (context, state) => - ResponsiveLayoutBuilder(context, const PostingScreen()), - ), - GoRoute( - path: 'mypage', - name: MyPageScreen.name, - builder: (context, state) => - ResponsiveLayoutBuilder(context, const MyPageScreen()), - ), - GoRoute( - path: 'settingaccount', - name: SettingAccountManagerWidget.name, - builder: (context, state) => ResponsiveLayoutBuilder( - context, const SettingAccountManagerWidget()), - ), - GoRoute( - path: 'webpayment', - name: WebPaymentWidget.name, - builder: (context, state) => - ResponsiveLayoutBuilder(context, const WebPaymentWidget()), - ), - GoRoute( - path: 'chat', - name: ChatScreen.name, - builder: (context, state) => - ResponsiveLayoutBuilder(context, const ChatScreen()), - ), - GoRoute( - path: 'mbti', - name: MBTIScreen.name, - builder: (context, state) => - ResponsiveLayoutBuilder(context, const MBTIScreen()), - ), - GoRoute( - path: 'adminloading', - name: AdminLoadingPage.name, - builder: (context, state) => - ResponsiveLayoutBuilder(context, const AdminLoadingPage()), - ), - GoRoute( - path: 'adminmain', - name: AdminScreen.name, - builder: (context, state) => - ResponsiveLayoutBuilder(context, const AdminScreen()), - ), - GoRoute( - path: 'userlistinadmin', - name: AdminUserListPage.name, - builder: (context, state) => - ResponsiveLayoutBuilder(context, const AdminUserListPage()), - ), - GoRoute( - path: 'userdetailinadmin', - name: AdminUserDetailPage.name, - builder: (context, state) => - ResponsiveLayoutBuilder(context, const AdminUserDetailPage()), - ), - GoRoute( - path: 'chatroom', - name: ChatRoomScreen.name, - builder: (context, state) => - ResponsiveLayoutBuilder(context, const ChatRoomScreen()), - ), - GoRoute( - path: 'friends', - name: FriendsListScreen.name, - builder: (context, state) => - ResponsiveLayoutBuilder(context, const FriendsListScreen()), - ), - GoRoute( - path: 'ranking', - name: RankingScreen.name, - builder: (context, state) => - ResponsiveLayoutBuilder(context, const RankingScreen()), - ), - GoRoute( - path: 'profiledetail', - name: ProfileDetailScreen.name, - builder: (context, state) => - ResponsiveLayoutBuilder(context, const ProfileDetailScreen()), - ) - ], + routes: [GoRoute(path: '')], ), ], ); diff --git a/lib/services/FirebaseAuthServiceProvider.dart b/lib/services/FirebaseAuthService.dart similarity index 86% rename from lib/services/FirebaseAuthServiceProvider.dart rename to lib/services/FirebaseAuthService.dart index 5a4a6c5..930ce61 100644 --- a/lib/services/FirebaseAuthServiceProvider.dart +++ b/lib/services/FirebaseAuthService.dart @@ -1,7 +1,6 @@ import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -/// FirebaseAuthServiceProvider.dart +/// FirebaseAuthService.dart /// /// Firebase Auth Service Provider /// - FirebaseAuthService를 제공하는 Provider @@ -15,14 +14,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; /// /// @jwson-automation -final firebaseAuthServiceProvider = Provider((ref) { - return FirebaseAuthService(FirebaseAuth.instance); -}); - class FirebaseAuthService { - final FirebaseAuth _auth; - - FirebaseAuthService(this._auth); + final FirebaseAuth _auth = FirebaseAuth.instance; // 사용자 상태 스트림 Stream get user => _auth.authStateChanges(); diff --git a/lib/services/FirebaseService.dart b/lib/services/FirebaseService.dart deleted file mode 100644 index 287d64e..0000000 --- a/lib/services/FirebaseService.dart +++ /dev/null @@ -1,92 +0,0 @@ -import 'package:blueberry_flutter_template/model/UserModel.dart'; -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:firebase_database/firebase_database.dart'; -import '../model/ChatMessageModel.dart'; - -class FirebaseService { - final FirebaseFirestore _firestore = FirebaseFirestore.instance; - - // ChatScreen.dart - Future addChatMessage(String roomId, String message) async { - final messageRef = FirebaseDatabase.instance.ref("chats/$roomId/messages"); - var user = FirebaseAuth.instance.currentUser; - - if (user == null) { - throw Exception('No current user found'); - } - - final snapshot = await messageRef.get(); - final int nextIndex = snapshot.children.length; - - final newMessage = ChatMessageModel( - senderId: user.uid, - message: message, - timestamp: DateTime.now(), - isRead: false, - ); - - final json = newMessage.toJson(); - json['timestamp'] = newMessage.timestamp.millisecondsSinceEpoch; - await messageRef.child(nextIndex.toString()).set(json); - } - - Future upDateUserDB(String email, String name) async { - try { - var user = FirebaseAuth.instance.currentUser; - - if (user == null) { - throw Exception('No current user found'); - } - - UserModel newUser = UserModel( - userId: user.uid, - name: name, - email: email, - age: 1, - isMemberShip: false, - profileImageUrl: '', - createdAt: DateTime.now(), - mbti: 'NULL', - userClass: 'user', - likeGivens: [""], - ); - // 멤버쉽 모델은 추후에 인앱 결제시 유저가 구독하고 있거나 유저 상태에 대한 변경을 주기 위해 추가했음 - await _firestore.collection('users').doc(user.uid).set(newUser.toJson()); - } catch (e) { - print('Error updating user: $e'); - throw Exception('Failed to update user'); - } - } - - Future updateUserMemberShip() async { - // 인앱 멤버쉽 결제시 호출해서 업데이트 함 - try { - var user = FirebaseAuth.instance.currentUser; - - if (user == null) { - throw Exception('No current user found'); - } - - await _firestore.collection('users').doc(user.uid).update({ - 'isMemberShip': true, - }); - } catch (e) { - print('Error updating user membership: $e'); - throw Exception('Failed to update user membership'); - } - } - - // 채팅방 생성 함수 - Future createChatRoom(String roomName) async { - try { - await _firestore.collection('chatRoomList').add({ - 'roomName': roomName, - 'timestamp': DateTime.now(), - }); - } catch (e) { - print('Error creating chat room: $e'); - throw Exception('Failed to create chat room'); - } - } -} diff --git a/lib/services/FirebaseStoreServiceProvider.dart b/lib/services/FirestoreService.dart similarity index 87% rename from lib/services/FirebaseStoreServiceProvider.dart rename to lib/services/FirestoreService.dart index 279452a..8b6fe81 100644 --- a/lib/services/FirebaseStoreServiceProvider.dart +++ b/lib/services/FirestoreService.dart @@ -1,15 +1,9 @@ import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../model/UserModel.dart'; -final firebaseStoreServiceProvider = - Provider((ref) => FirestoreService(FirebaseFirestore.instance)); - class FirestoreService { - final FirebaseFirestore _db; - - FirestoreService(this._db); + final FirebaseFirestore _db = FirebaseFirestore.instance; // User 정보 생성 Future createUser(UserModel user) async { diff --git a/lib/services/InAppPurchaseService.dart b/lib/services/InAppPurchaseService.dart deleted file mode 100644 index 825c6a7..0000000 --- a/lib/services/InAppPurchaseService.dart +++ /dev/null @@ -1,92 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:blueberry_flutter_template/services/FirebaseService.dart'; -import 'package:in_app_purchase/in_app_purchase.dart'; - -class InAppPurchaseService { - static const String _kIosProductId = 'com.gaeting.example.userLevel'; - static const String _kAndroidProductId = 'your_android_product_id'; - - final InAppPurchase inAppPurchase = InAppPurchase.instance; - late StreamSubscription> subscription; - - final firebaseService = FirebaseService(); - - InAppPurchaseService() { - initStoreInfo(); - } - - void initStoreInfo() { - final Stream> purchaseUpdated = - inAppPurchase.purchaseStream; - subscription = purchaseUpdated.listen((purchaseDetailsList) { - _listenToPurchaseUpdated(purchaseDetailsList); - }, onDone: () { - // 결제가 끝났을 때 처리 - }, onError: (error) { - // 에러 처리 - }); - } - - Future buyMembership() async { - final String productId = - Platform.isIOS ? _kIosProductId : _kAndroidProductId; - - final ProductDetails product = await _getProductDetails(productId); - - final PurchaseParam purchaseParam = PurchaseParam(productDetails: product); - - try { - final bool success = - await inAppPurchase.buyNonConsumable(purchaseParam: purchaseParam); - if (!success) { - throw Exception('Purchase failed'); - } - } catch (e) { - print('Error during purchase: $e'); - rethrow; - } - } - - Future _getProductDetails(String productId) async { - final ProductDetailsResponse response = - await inAppPurchase.queryProductDetails({productId}); - - if (response.notFoundIDs.isNotEmpty) { - throw Exception('Product not found'); - } - - return response.productDetails.first; - } - - void _listenToPurchaseUpdated( - List purchaseDetailsList) async { - for (final PurchaseDetails purchaseDetails in purchaseDetailsList) { - if (purchaseDetails.status == PurchaseStatus.purchased) { - // 구매 완료 - await _verifyPurchase(purchaseDetails); - } else if (purchaseDetails.status == PurchaseStatus.error) { - // 구매 오류 - print('Error: ${purchaseDetails.error}'); - } - - if (purchaseDetails.pendingCompletePurchase) { - await inAppPurchase.completePurchase(purchaseDetails); - } - } - } - - Future _verifyPurchase(PurchaseDetails purchaseDetails) async { - // 결제처리가 완료되면 여기서 서버에 업데이트를 요청합니다. - const String membershipProductID = 'com.gaeting.example.userLevel'; - if (purchaseDetails.productID == membershipProductID) { - await firebaseService.updateUserMemberShip(); - } - } - - void dispose() { - subscription.cancel(); - // 기타 필요한 정리 작업 - } -} diff --git a/lib/services/PaymentService.dart b/lib/services/PaymentService.dart deleted file mode 100644 index 7fe2cf8..0000000 --- a/lib/services/PaymentService.dart +++ /dev/null @@ -1,76 +0,0 @@ -import 'package:blueberry_flutter_template/model/SaleItemModel.dart'; -import 'package:bootpay/bootpay.dart'; -import 'package:bootpay/model/payload.dart'; -import 'package:cloud_functions/cloud_functions.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/foundation.dart'; -import 'package:bootpay/model/user.dart' as BootpayUser; - -// payment_service.dart - -class PaymentService { - Future requestPayment( - String username, String userEmail, SaleItemModel item) async { - String webApplicationId = 'put your JS API key here'; - String androidApplicationId = 'put your Android API key here'; - String iosApplicationId = 'put your iOS API key here'; - - Payload payload = Payload(); - payload.androidApplicationId = androidApplicationId; - payload.iosApplicationId = iosApplicationId; - payload.webApplicationId = webApplicationId; - - if (kIsWeb) { - payload.extra?.openType = "iframe"; - } - - payload.pg = 'KCP'; - payload.methods = ['card', 'phone', 'vbank', 'bank', 'kakao', 'naver']; - - payload.orderName = item.name; - payload.price = item.price * item.quantity; - payload.orderId = DateTime.now().millisecondsSinceEpoch.toString(); - - BootpayUser.User user = BootpayUser.User(); - user.username = username; - user.email = userEmail; - user.phone = '010-1234-5678'; - user.addr = '서울시 어딘가'; - - try { - Bootpay().requestPayment( - payload: payload, - showCloseButton: false, - closeButton: const Icon(Icons.close, size: 35.0, color: Colors.black54), - onDone: (String data) async { - print('결제 완료: $data'); - try { - HttpsCallable callable = - FirebaseFunctions.instance.httpsCallable('verifyPayment'); - // fuctions 폴더의 verifyPayment 함수 호출 - final result = await callable.call({ - 'paymentData': data, - 'itemId': item.id, - 'userId': userEmail, - 'itemPrice': item.price, - }); - print('영수증 처리 결과: ${result.data}'); - // 결과에 따른 추가 처리 - } catch (e) { - print('영수증 처리 중 오류 발생: $e'); - } - }, - onCancel: (String data) { - print('결제 취소: $data'); - // 결제 취소 후 처리 로직 - }, - onError: (String data) { - print('결제 에러: $data'); - // 결제 에러 후 처리 로직 - }, - ); - } catch (e) { - print('결제 처리 중 에러 발생: $e'); - } - } -} diff --git a/lib/services/PredefinedItems.dart b/lib/services/PredefinedItems.dart deleted file mode 100644 index 0b4e9a9..0000000 --- a/lib/services/PredefinedItems.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:blueberry_flutter_template/model/SaleItemModel.dart'; - -class PredefinedItems { - static const SaleItemModel premiumMembership = SaleItemModel( - id: 1, - name: '프리미엄 회원', - description: '프리미엄 회원 가입', - price: 1000, - quantity: 1, - imageUrl: 'assets/images/premium_membership.png', - ); - - // 결제 아이템을 변경해도 적용 가능한지 체크 하기 위해 추가함 (테스트용) - static const SaleItemModel basicMembership = SaleItemModel( - id: 2, - name: '기본 회원', - description: '기본 회원 가입', - price: 500, - quantity: 1, - imageUrl: 'assets/images/basic_membership.png', - ); - - static SaleItemModel getItemById(int id) { - switch (id) { - case 1: - return premiumMembership; - case 2: - return basicMembership; - // 더 많은 케이스... - default: - throw Exception("Unknown item id: $id"); - } - } -} diff --git a/lib/services/SocialAuthService.dart b/lib/services/SocialAuthService.dart deleted file mode 100644 index 683793b..0000000 --- a/lib/services/SocialAuthService.dart +++ /dev/null @@ -1,177 +0,0 @@ -import 'dart:convert'; -import 'dart:math'; - -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:crypto/crypto.dart'; -import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart'; -import 'package:dio/dio.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter/services.dart'; -import 'package:google_sign_in/google_sign_in.dart'; -import 'package:package_info_plus/package_info_plus.dart'; -import 'package:sign_in_with_apple/sign_in_with_apple.dart'; - -import '../utils/Talker.dart'; - -class SocialAuthService { - get context => null; - - ///Google Sign In - Future signInWithGoogle() async { - ///* GoogleSignIn 객체 생성 - final googleSignIn = GoogleSignIn(); - final GoogleSignInAccount? gUser = await googleSignIn.signIn(); - if (gUser != null) { - final GoogleSignInAuthentication gAuth = await gUser.authentication; - final gCredential = GoogleAuthProvider.credential( - accessToken: gAuth.accessToken, - idToken: gAuth.idToken, - ); - final userCredential = - await FirebaseAuth.instance.signInWithCredential(gCredential); - getAuthenticateWithFirebase(userCredential); - } - } - - ///Apple Sign In - Future signInWithApple() async { - try { - final rawNonce = generateNonce(); - final nonce = sha256ofString(rawNonce); - - final appleIdCredential = await SignInWithApple.getAppleIDCredential( - scopes: [ - AppleIDAuthorizationScopes.email, - AppleIDAuthorizationScopes.fullName, - ], - nonce: nonce, - ); - - final credential = OAuthProvider('apple.com').credential( - idToken: appleIdCredential.identityToken, - accessToken: appleIdCredential.authorizationCode, - rawNonce: rawNonce, - ); - - final result = - await FirebaseAuth.instance.signInWithCredential(credential); - getAuthenticateWithFirebase(result); - } on SignInWithAppleAuthorizationException catch (e) { - // Apple 로그인 관련 오류 처리 - talker.error('Apple 로그인 오류: ${e.code}'); - } catch (e) { - talker.error('Apple 로그인 중 오류 발생: $e'); - throw Exception('Apple 로그인 중 오류 발생: $e'); - } - } - - String generateNonce([int length = 32]) { - const charset = - '0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._'; - final random = Random.secure(); - return List.generate(length, (_) => charset[random.nextInt(charset.length)]) - .join(); - } - - String sha256ofString(String input) { - final bytes = utf8.encode(input); - final digest = sha256.convert(bytes); - return digest.toString(); - } - - ///Github Sign In - Future signInWithGithub() async { - GithubAuthProvider githubAuthProvider = GithubAuthProvider(); - final userCredential = - await FirebaseAuth.instance.signInWithProvider(githubAuthProvider); - getAuthenticateWithFirebase(userCredential); - } - - ///* 인증정보를 바탕으로 firestore에 저장하는 함수 - void getAuthenticateWithFirebase(UserCredential? credential) async { - if (credential?.user == null) { - return null; - } - var ref = FirebaseFirestore.instance - .collection('users') - .doc(FirebaseAuth.instance.currentUser!.uid); - var snapshot = await ref.get(); - if (!snapshot.exists) { - return await FirebaseFirestore.instance - .collection('users') - .doc(FirebaseAuth.instance.currentUser!.uid) - .set({ - 'account_level': 1, - //account_level이 0이되면 Delete timestamp확인하여 14일 뒤 삭제 - 'email': FirebaseAuth.instance.currentUser!.email, - 'name': FirebaseAuth.instance.currentUser!.displayName, - 'age': 0, - 'createdAt': DateTime.timestamp(), - 'profilePicture': "", - }); - } - } - - //=========================withdrawl==============================// - Future appleWithDrawl() async { - String keyID = ''; - String teamID = ''; - String pkgName = ''; - await PackageInfo.fromPlatform().then((value) { - pkgName = value.packageName; - }); - String privateKey = - await rootBundle.loadString('assets/certificate/AuthKey_$keyID.p8'); - - int iat = DateTime.now().toUtc().millisecondsSinceEpoch ~/ - Duration.millisecondsPerSecond; - int exp = DateTime.now() - .add(const Duration(seconds: 15776999)) //30일 - .toUtc() - .millisecondsSinceEpoch ~/ - Duration.millisecondsPerSecond; - - var jwt = JWT({ - 'iss': teamID, - 'iat': iat, - 'exp': exp, - 'aud': 'https://appleid.apple.com', // 기본값 - 'sub': pkgName, - }, header: { - 'alg': 'ES256', // 기본값 - 'kid': keyID, - }); - - final credential = await SignInWithApple.getAppleIDCredential( - scopes: [ - AppleIDAuthorizationScopes.email, - AppleIDAuthorizationScopes.fullName, - ], - ); - - var clientSecret = - jwt.sign(ECPrivateKey(privateKey), algorithm: JWTAlgorithm.ES256); - - Map body = { - 'client_id': pkgName, - 'client_secret': clientSecret, - 'token': credential.identityToken - }; - final dio = Dio(); - Map header = { - 'Content-Type': 'application/x-www-form-url' - 'encoded' - }; - dio.options.headers = header; - try { - final res = - await dio.post('https://appleid.apple.com/auth/revoke', data: body); - if (res.statusCode == 200) { - return true; - } - return false; - } catch (e) { - return false; - } - } -} diff --git a/lib/services/camera/CameraService.dart b/lib/services/camera/CameraService.dart deleted file mode 100644 index efcee70..0000000 --- a/lib/services/camera/CameraService.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:camera/camera.dart'; - -class CameraService { - CameraController? controller; - List cameras = []; - bool _readyTakePhoto = false; - final bool _changeCamera = false; - - bool get readyTakePhoto => _readyTakePhoto; - bool get changeCamera => _changeCamera; - - Future initializeCameras() async { - cameras = await availableCameras(); - controller = CameraController(cameras[0], ResolutionPreset.high); - await controller!.initialize(); - } - - Future toggleCamera() async { - if (cameras.length < 2) return; - - final lensDirection = controller!.description.lensDirection; - CameraDescription newCamera; - if (lensDirection == CameraLensDirection.back) { - newCamera = cameras.firstWhere( - (camera) => camera.lensDirection == CameraLensDirection.front, - ); - } else { - newCamera = cameras.firstWhere( - (camera) => camera.lensDirection == CameraLensDirection.back, - ); - } - - await controller?.dispose(); - controller = CameraController(newCamera, ResolutionPreset.high); - try { - await controller!.initialize(); - _readyTakePhoto = true; - } catch (e) { - print("Error toggling camera: $e"); - _readyTakePhoto = false; - } - } - - Future takePhoto() async { - if (controller != null && controller!.value.isInitialized) { - try { - final XFile file = await controller!.takePicture(); - return file; - } catch (e) { - print("Error taking photo: $e"); - return null; - } - } - return null; - } - - void dispose() { - controller!.dispose(); - } -} diff --git a/lib/services/verifications/EmailVerificationService.dart b/lib/services/verifications/EmailVerificationService.dart deleted file mode 100644 index 91c15c6..0000000 --- a/lib/services/verifications/EmailVerificationService.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'dart:ui'; - -import 'package:blueberry_flutter_template/utils/Talker.dart'; -import 'package:cloud_functions/cloud_functions.dart'; - -class EmailVerificationService { - Future sendVerificationEmail(String email) async { - try { - final callable = FirebaseFunctions.instance - .httpsCallable('emailVerification-sendVerificationEmail'); - final result = await callable.call({'email': email}); - talker.log('이메일 전송 결과: $result'); - talker.log('Result data: ${result.data}'); - - if (result.data != null && result.data is Map) { - if (result.data['success'] == true) { - talker.log('이메일이 성공적으로 전송되었습니다: ${result.data['message']}'); - } else { - talker.error('이메일 전송 실패: ${result.data['message']}'); - } - } else { - talker.error('예상치 못한 응답 구조'); - } - } on FirebaseFunctionsException catch (e) { - talker.error( - 'Firebase Functions Exception: ${e.code} - ${e.message}, ${e.details}'); - rethrow; - } catch (e) { - talker.error('Unexpected error: $e'); - rethrow; - } - } - - Future verifyCode( - String email, String code, VoidCallback onNext) async { - try { - final HttpsCallable callable = FirebaseFunctions.instance - .httpsCallable('emailVerification-verifyCode'); - final result = await callable.call({'email': email, 'code': code}); - if (result.data['success']) { - talker.log("이메일이 성공적으로 인증되었습니다."); - onNext(); - // 인증 성공 처리 (예: 다음 화면으로 이동) - } else { - talker.log('인증 실패: ${result.data['message']}'); - // 인증 실패 메시지 표시 - } - } on FirebaseFunctionsException catch (e) { - talker.error('Firebase Functions Exception: ${e.code} - ${e.message}'); - // 에러 메시지 표시 - } - } -}