diff --git a/assets/icons/alarm_setting.svg b/assets/icons/alarm_setting.svg
new file mode 100644
index 00000000..faf616f6
--- /dev/null
+++ b/assets/icons/alarm_setting.svg
@@ -0,0 +1,3 @@
+
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index 55e82beb..858caa4e 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -512,7 +512,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.11.1;
+ MARKETING_VERSION = 1.11.2;
PRODUCT_BUNDLE_IDENTIFIER = com.dongsoop.site.dongsoop;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -725,7 +725,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.11.1;
+ MARKETING_VERSION = 1.11.2;
PRODUCT_BUNDLE_IDENTIFIER = com.dongsoop.site.dongsoop;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -765,7 +765,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.11.1;
+ MARKETING_VERSION = 1.11.2;
PRODUCT_BUNDLE_IDENTIFIER = com.dongsoop.site.dongsoop;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift
index 88d3ac1d..40fc52ec 100644
--- a/ios/Runner/AppDelegate.swift
+++ b/ios/Runner/AppDelegate.swift
@@ -52,6 +52,20 @@ class MyAppCheckProviderFactory: NSObject, AppCheckProviderFactory {
}
if let controller = window?.rootViewController as? FlutterViewController {
+ let appDistributionChannel = FlutterMethodChannel(
+ name: "dongsoop/app_distribution",
+ binaryMessenger: controller.binaryMessenger
+ )
+
+ appDistributionChannel.setMethodCallHandler { call, result in
+ switch call.method {
+ case "isTestFlight":
+ result(Self.isRunningInTestFlight())
+ default:
+ result(FlutterMethodNotImplemented)
+ }
+ }
+
pushChannel = FlutterMethodChannel(
name: "app/push",
binaryMessenger: controller.binaryMessenger
@@ -178,4 +192,12 @@ class MyAppCheckProviderFactory: NSObject, AppCheckProviderFactory {
}
super.application(application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler)
}
+
+ private static func isRunningInTestFlight() -> Bool {
+ guard let receiptURL = Bundle.main.appStoreReceiptURL else {
+ return false
+ }
+
+ return receiptURL.lastPathComponent == "sandboxReceipt"
+ }
}
\ No newline at end of file
diff --git a/lib/core/environment/app_distribution.dart b/lib/core/environment/app_distribution.dart
new file mode 100644
index 00000000..290ccde6
--- /dev/null
+++ b/lib/core/environment/app_distribution.dart
@@ -0,0 +1,19 @@
+import 'dart:io';
+
+import 'package:flutter/services.dart';
+
+class AppDistribution {
+ static const MethodChannel _channel = MethodChannel('dongsoop/app_distribution');
+
+ static Future isTestFlight() async {
+ if (!Platform.isIOS) return false;
+
+ try {
+ final result = await _channel.invokeMethod('isTestFlight');
+ return result ?? false;
+ } catch (e) {
+ print('[isTestFlight Error] Failed to detect TestFlight: $e');
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib/core/presentation/components/admob_native_ad.dart b/lib/core/presentation/components/admob_native_ad.dart
index 82941d39..26b306ae 100644
--- a/lib/core/presentation/components/admob_native_ad.dart
+++ b/lib/core/presentation/components/admob_native_ad.dart
@@ -5,6 +5,7 @@ import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:logger/logger.dart';
import 'package:dongsoop/ui/color_styles.dart';
+import 'package:dongsoop/core/environment/app_distribution.dart';
class AdmobNativeAd extends StatefulWidget {
final TemplateType templateType;
@@ -23,6 +24,8 @@ class AdmobNativeAd extends StatefulWidget {
class _AdmobNativeAdState extends State {
NativeAd? _nativeAd;
bool _nativeAdIsLoaded = false;
+ bool _useTestAds = kDebugMode;
+
final Logger _logger = Logger();
static const String _androidTestAdUnitId = 'ca-app-pub-3940256099942544/2247696110';
@@ -30,18 +33,27 @@ class _AdmobNativeAdState extends State {
String get _adUnitId {
if (Platform.isAndroid) {
- if (kDebugMode) return _androidTestAdUnitId;
+ if (_useTestAds) return _androidTestAdUnitId;
return dotenv.maybeGet('ADMOB_ANDROID_NATIVE_ID') ?? _androidTestAdUnitId;
} else if (Platform.isIOS) {
- if (kDebugMode) return _iosTestAdUnitId;
+ if (_useTestAds) return _iosTestAdUnitId;
return dotenv.maybeGet('ADMOB_IOS_NATIVE_ID') ?? _iosTestAdUnitId;
}
+
return '';
}
@override
void initState() {
super.initState();
+ _initAndLoadAd();
+ }
+
+ Future _initAndLoadAd() async {
+ final isTestFlight = await AppDistribution.isTestFlight();
+ _useTestAds = kDebugMode || isTestFlight;
+ _logger.d('AdMob use test ads: $_useTestAds');
+ if (!mounted) return;
_loadAd();
}
@@ -69,6 +81,13 @@ class _AdmobNativeAdState extends State {
onAdFailedToLoad: (ad, error) {
_logger.e('$NativeAd failed to load: $error');
ad.dispose();
+
+ if (mounted) {
+ setState(() {
+ _nativeAd = null;
+ _nativeAdIsLoaded = false;
+ });
+ }
},
),
request: const AdRequest(),
@@ -104,7 +123,7 @@ class _AdmobNativeAdState extends State {
child: AdWidget(ad: _nativeAd!),
);
}
- // 광고 로딩 중에는 빈 공간 또는 스켈레톤 UI를 보여줄 수 있습니다.
+
return SizedBox(height: widget.height);
}
}
diff --git a/lib/core/routing/router.dart b/lib/core/routing/router.dart
index 743d78df..7a9edaf8 100644
--- a/lib/core/routing/router.dart
+++ b/lib/core/routing/router.dart
@@ -43,7 +43,7 @@ import 'package:dongsoop/presentation/my_page/my_page_screen.dart';
import 'package:dongsoop/presentation/report/report_screen.dart';
import 'package:dongsoop/presentation/my_page/feedback/feedback_more_screen.dart';
import 'package:dongsoop/presentation/setting/device_management/device_management_screen.dart';
-import 'package:dongsoop/presentation/setting/notification/notification_screen.dart';
+import 'package:dongsoop/presentation/notification/notification_screen.dart';
import 'package:dongsoop/presentation/setting/setting_screen.dart';
import 'package:dongsoop/presentation/sign_in/password_reset_screen.dart';
import 'package:dongsoop/presentation/sign_in/sign_in_screen.dart';
@@ -263,23 +263,21 @@ final router = GoRouter(
),
),
GoRoute(
- path: RoutePaths.adminReportSanction,
- builder: (context, state) {
- final extra = state.extra as Map?;
- final reportId = extra?['reportId'] as int? ?? 0;
- final targetMemberId = extra?['targetMemberId'] as int? ?? 0;
+ path: RoutePaths.adminReportSanction,
+ builder: (context, state) {
+ final extra = state.extra as Map?;
+ final reportId = extra?['reportId'] as int? ?? 0;
+ final targetMemberId = extra?['targetMemberId'] as int? ?? 0;
- return ReportAdminSanctionScreen(
- reportId: reportId,
- targetMemberId: targetMemberId,
- );
- }),
+ return ReportAdminSanctionScreen(
+ reportId: reportId,
+ targetMemberId: targetMemberId,
+ );
+ }
+ ),
GoRoute(
path: RoutePaths.setting,
builder: (context, state) => SettingScreen(
- onTapNotification: () {
- context.push(RoutePaths.notification);
- },
onTapDevice: () {
context.push(RoutePaths.deviceManagement);
},
@@ -288,7 +286,15 @@ final router = GoRouter(
),
GoRoute(
path: RoutePaths.notification,
- builder: (context, state) => NotificationScreen(),
+ builder: (context, state) => NotificationScreen(
+ onTapNoticeKeyword: () => context.replace(RoutePaths.noticeKeyword),
+ ),
+ ),
+ GoRoute(
+ path: RoutePaths.noticeKeyword,
+ builder: (context, state) => NoticeKeywordScreen(
+ onTapNotification: () => context.replace(RoutePaths.notification),
+ ),
),
GoRoute(
path: RoutePaths.deviceManagement,
@@ -581,7 +587,11 @@ final router = GoRouter(
pageBuilder: (context, state) {
return MaterialPage(
key: state.pageKey,
- child: const NoticeListPageScreen(),
+ child: NoticeListPageScreen(
+ onTapAlarmSetting: () {
+ context.push(RoutePaths.notification);
+ },
+ ),
);
},
),
@@ -705,10 +715,6 @@ final router = GoRouter(
onTapPasswordReset: () => context.push(RoutePaths.passwordReset),
),
),
- GoRoute(
- path: RoutePaths.noticeKeyword,
- builder: (context, state) => const NoticeKeywordScreen(),
- ),
GoRoute(
path: RoutePaths.socialLoginConnect,
builder: (context, state) => SocialLoginConnectScreen(),
@@ -792,8 +798,8 @@ final router = GoRouter(
onTapBlockedUser: () {
context.push(RoutePaths.mypage + RoutePaths.mypageBlock);
},
- onTapNoticeKeyword: () {
- context.push(RoutePaths.mypage + RoutePaths.noticeKeyword);
+ onTapNotification: () {
+ context.push(RoutePaths.notification);
},
onTapSocialLoginConnect: () {
context.push(RoutePaths.mypage + RoutePaths.socialLoginConnect);
diff --git a/lib/main.dart b/lib/main.dart
index f34762a5..433b54c7 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -28,7 +28,7 @@ import 'domain/chat/model/chat_room_member.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
- MobileAds.instance.initialize();
+ await MobileAds.instance.initialize();
await dotenv.load(); // .env 파일 로드
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
diff --git a/lib/presentation/app/marketing_push_guard.dart b/lib/presentation/app/marketing_push_guard.dart
index a52cd391..0e8b7828 100644
--- a/lib/presentation/app/marketing_push_guard.dart
+++ b/lib/presentation/app/marketing_push_guard.dart
@@ -1,7 +1,7 @@
import 'package:dongsoop/core/storage/preferences_service.dart';
import 'package:dongsoop/domain/notification/enum/notification_target.dart';
-import 'package:dongsoop/presentation/setting/notification/view_model/notification_setting_view_model.dart';
-import 'package:dongsoop/presentation/setting/notification/view_model/notification_types.dart';
+import 'package:dongsoop/presentation/notification/view_model/notification_setting_view_model.dart';
+import 'package:dongsoop/presentation/notification/view_model/notification_types.dart';
import 'package:dongsoop/providers/auth_providers.dart';
import 'package:dongsoop/providers/device_providers.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
diff --git a/lib/presentation/home/notice_list_page_screen.dart b/lib/presentation/home/notice_list_page_screen.dart
index 9dfb16d3..ed47e316 100644
--- a/lib/presentation/home/notice_list_page_screen.dart
+++ b/lib/presentation/home/notice_list_page_screen.dart
@@ -12,11 +12,17 @@ import 'package:dongsoop/ui/color_styles.dart';
import 'package:dongsoop/ui/text_styles.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:flutter_svg/svg.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
class NoticeListPageScreen extends HookConsumerWidget {
- const NoticeListPageScreen({super.key});
+ final VoidCallback onTapAlarmSetting;
+
+ const NoticeListPageScreen({
+ super.key,
+ required this.onTapAlarmSetting,
+ });
NoticeTab selectedTab(int index) {
return NoticeTab.values[index];
@@ -67,7 +73,21 @@ class NoticeListPageScreen extends HookConsumerWidget {
return SafeArea(
child: Scaffold(
backgroundColor: ColorStyles.white,
- appBar: const DetailHeader(title: '공지'),
+ appBar: DetailHeader(
+ title: '공지',
+ trailing: IconButton(
+ onPressed: onTapAlarmSetting,
+ icon: SvgPicture.asset(
+ 'assets/icons/alarm_setting.svg',
+ width: 24,
+ height: 24,
+ colorFilter: const ColorFilter.mode(
+ ColorStyles.black,
+ BlendMode.srcIn,
+ ),
+ ),
+ ),
+ ),
body: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
diff --git a/lib/presentation/my_page/my_page_screen.dart b/lib/presentation/my_page/my_page_screen.dart
index 5456ed83..b23dee5f 100644
--- a/lib/presentation/my_page/my_page_screen.dart
+++ b/lib/presentation/my_page/my_page_screen.dart
@@ -21,7 +21,7 @@ class MyPageScreen extends HookConsumerWidget {
final VoidCallback onTapMarket;
final void Function(bool isApply) onTapRecruit;
final VoidCallback onTapBlockedUser;
- final VoidCallback onTapNoticeKeyword;
+ final VoidCallback onTapNotification;
final VoidCallback onTapSocialLoginConnect;
const MyPageScreen({
@@ -37,7 +37,7 @@ class MyPageScreen extends HookConsumerWidget {
required this.onTapMarket,
required this.onTapRecruit,
required this.onTapBlockedUser,
- required this.onTapNoticeKeyword,
+ required this.onTapNotification,
required this.onTapSocialLoginConnect,
});
@@ -76,37 +76,38 @@ class MyPageScreen extends HookConsumerWidget {
)
),
body: SafeArea(
- child: SingleChildScrollView(
- child: Padding(
- padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 16),
- child: myPageState.when(
- data: (user) {
- if (user == null) {
- return LoggedOutPromptCard(
- onTapLogin: onTapSignIn,
- onTapUserFeedback: onTapUserFeedback,
- );
- } else {
- return LoggedInUserCard(
- user: user,
- onTapAdminReport: onTapAdminReport,
- onTapMarket: onTapMarket,
- onTapRecruit: onTapRecruit,
- onTapCalendar: onTapCalendar,
- onTapTimetable: onTapTimetable,
- onTapBlockedUser: onTapBlockedUser,
- onTapAdminBlindDate: onTapAdminBlindDate,
- onTapAdminFeedback: onTapAdminFeedback,
- onTapUserFeedback: onTapUserFeedback,
- onTapNoticeKeyword: onTapNoticeKeyword,
- onTapSocialLoginConnect: onTapSocialLoginConnect,
- );
- }
- },
- error: (e, _) => Center(child: Text('$e', style: TextStyles.normalTextRegular.copyWith(color: ColorStyles.black),)),
- loading: () => Center(child: CircularProgressIndicator()),
- ),
+ child: SingleChildScrollView(
+ child: Padding(
+ padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 16),
+ child: myPageState.when(
+ data: (user) {
+ if (user == null) {
+ return LoggedOutPromptCard(
+ onTapLogin: onTapSignIn,
+ onTapNotification: onTapNotification,
+ onTapUserFeedback: onTapUserFeedback,
+ );
+ } else {
+ return LoggedInUserCard(
+ user: user,
+ onTapAdminReport: onTapAdminReport,
+ onTapMarket: onTapMarket,
+ onTapRecruit: onTapRecruit,
+ onTapCalendar: onTapCalendar,
+ onTapTimetable: onTapTimetable,
+ onTapBlockedUser: onTapBlockedUser,
+ onTapAdminBlindDate: onTapAdminBlindDate,
+ onTapAdminFeedback: onTapAdminFeedback,
+ onTapUserFeedback: onTapUserFeedback,
+ onTapNotification: onTapNotification,
+ onTapSocialLoginConnect: onTapSocialLoginConnect,
+ );
+ }
+ },
+ error: (e, _) => Center(child: Text('$e', style: TextStyles.normalTextRegular.copyWith(color: ColorStyles.black),)),
+ loading: () => Center(child: CircularProgressIndicator()),
),
+ ),
),
),
);
diff --git a/lib/presentation/my_page/widgets/logged_in_user_card.dart b/lib/presentation/my_page/widgets/logged_in_user_card.dart
index 5f3dc1e4..6450bf8c 100644
--- a/lib/presentation/my_page/widgets/logged_in_user_card.dart
+++ b/lib/presentation/my_page/widgets/logged_in_user_card.dart
@@ -16,7 +16,7 @@ class LoggedInUserCard extends HookConsumerWidget {
final VoidCallback onTapTimetable;
final void Function(bool isApply) onTapRecruit;
final VoidCallback onTapBlockedUser;
- final VoidCallback onTapNoticeKeyword;
+ final VoidCallback onTapNotification;
final VoidCallback onTapSocialLoginConnect;
const LoggedInUserCard({
@@ -31,7 +31,7 @@ class LoggedInUserCard extends HookConsumerWidget {
required this.onTapTimetable,
required this.onTapRecruit,
required this.onTapBlockedUser,
- required this.onTapNoticeKeyword,
+ required this.onTapNotification,
required this.onTapSocialLoginConnect,
});
@@ -182,8 +182,8 @@ class LoggedInUserCard extends HookConsumerWidget {
Column(
children: [
MyActivityItem(
- label: '공지 키워드 알림',
- onTap: onTapNoticeKeyword,
+ label: '알림 설정',
+ onTap: onTapNotification,
),
MyActivityItem(
label: '소셜 계정 연동',
diff --git a/lib/presentation/my_page/widgets/logged_out_prompt_card.dart b/lib/presentation/my_page/widgets/logged_out_prompt_card.dart
index 1c381bbd..7a6af182 100644
--- a/lib/presentation/my_page/widgets/logged_out_prompt_card.dart
+++ b/lib/presentation/my_page/widgets/logged_out_prompt_card.dart
@@ -6,11 +6,13 @@ import 'package:dongsoop/ui/text_styles.dart';
class LoggedOutPromptCard extends StatelessWidget {
final VoidCallback onTapLogin;
+ final VoidCallback onTapNotification;
final VoidCallback onTapUserFeedback;
const LoggedOutPromptCard({
super.key,
required this.onTapLogin,
+ required this.onTapNotification,
required this.onTapUserFeedback,
});
@@ -69,7 +71,7 @@ class LoggedOutPromptCard extends StatelessWidget {
),
),
- const SizedBox(height: 16),
+ const SizedBox(height: 24),
Container(
width: double.infinity,
@@ -80,9 +82,17 @@ class LoggedOutPromptCard extends StatelessWidget {
),
),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
- child: MyActivityItem(
- label: '피드백 하러가기',
- onTap: onTapUserFeedback,
+ child: Column(
+ children: [
+ MyActivityItem(
+ label: '알림 설정',
+ onTap: onTapNotification,
+ ),
+ MyActivityItem(
+ label: '피드백 하러가기',
+ onTap: onTapUserFeedback,
+ ),
+ ],
),
),
],
diff --git a/lib/presentation/notice/keyword/notice_keyword_screen.dart b/lib/presentation/notice/keyword/notice_keyword_screen.dart
index 8b78539c..a19241e1 100644
--- a/lib/presentation/notice/keyword/notice_keyword_screen.dart
+++ b/lib/presentation/notice/keyword/notice_keyword_screen.dart
@@ -1,3 +1,4 @@
+import 'package:dongsoop/core/presentation/components/category_tab_bar.dart';
import 'package:dongsoop/core/presentation/components/custom_confirm_dialog.dart';
import 'package:dongsoop/core/presentation/components/detail_header.dart';
import 'package:dongsoop/core/presentation/components/login_required_dialog.dart';
@@ -12,7 +13,12 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
class NoticeKeywordScreen extends HookConsumerWidget {
- const NoticeKeywordScreen({super.key});
+ final VoidCallback onTapNotification;
+
+ const NoticeKeywordScreen({
+ super.key,
+ required this.onTapNotification,
+ });
@override
Widget build(BuildContext context, WidgetRef ref) {
@@ -47,7 +53,7 @@ class NoticeKeywordScreen extends HookConsumerWidget {
child: Scaffold(
backgroundColor: ColorStyles.white,
appBar: DetailHeader(
- title: '공지 키워드 알림',
+ title: '알림 설정',
bottom: TabBar(
overlayColor: const WidgetStatePropertyAll(Colors.transparent),
labelColor: ColorStyles.primary100,
@@ -64,8 +70,10 @@ class NoticeKeywordScreen extends HookConsumerWidget {
),
body: SafeArea(
child: state.isLoading && state.keywords.isEmpty
- ? const Center(child: CircularProgressIndicator(color: ColorStyles.primaryColor,))
- : GestureDetector(
+ ? const Center(child: CircularProgressIndicator(color: ColorStyles.primaryColor,))
+ : Stack(
+ children: [
+ GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
behavior: HitTestBehavior.opaque,
child: TabBarView(
@@ -90,7 +98,28 @@ class NoticeKeywordScreen extends HookConsumerWidget {
),
],
),
- ),
+ ),
+ Positioned(
+ left: 0,
+ right: 0,
+ bottom: 24,
+ child: Center(
+ child: CategoryTabBar(
+ tabs: const ['전체', '키워드'],
+ selectedIndex: 1,
+ onSelected: (i) {
+ if (i == 1) return;
+
+ Future.microtask(() async {
+ onTapNotification();
+ });
+ },
+ isBoard: false,
+ ),
+ ),
+ ),
+ ],
+ ),
),
),
);
diff --git a/lib/presentation/notification/notification_screen.dart b/lib/presentation/notification/notification_screen.dart
new file mode 100644
index 00000000..b72d3a55
--- /dev/null
+++ b/lib/presentation/notification/notification_screen.dart
@@ -0,0 +1,367 @@
+import 'package:dongsoop/core/presentation/components/category_tab_bar.dart';
+import 'package:dongsoop/ui/text_styles.dart';
+import 'package:flutter/material.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:dongsoop/core/presentation/components/detail_header.dart';
+import 'package:dongsoop/presentation/notification/view_model/notification_setting_view_model.dart';
+import 'package:dongsoop/presentation/notification/view_model/notification_types.dart';
+import 'package:dongsoop/presentation/notification/widget/notification_section.dart';
+import 'package:dongsoop/presentation/notification/widget/notification_toggle_row.dart';
+import 'package:dongsoop/providers/auth_providers.dart';
+import 'package:dongsoop/providers/device_providers.dart';
+import 'package:dongsoop/ui/color_styles.dart';
+import 'package:dongsoop/domain/notification/enum/notification_target.dart';
+
+class NotificationScreen extends ConsumerStatefulWidget {
+ final VoidCallback onTapNoticeKeyword;
+
+ const NotificationScreen({
+ super.key,
+ required this.onTapNoticeKeyword,
+ });
+
+ @override
+ ConsumerState createState() => _NotificationScreenState();
+}
+
+class _NotificationScreenState extends ConsumerState {
+
+ void _showSnack(BuildContext context, String message) {
+ final messenger = ScaffoldMessenger.of(context);
+ messenger.hideCurrentSnackBar();
+ messenger.showSnackBar(
+ SnackBar(
+ content: Text(message, style: TextStyles.normalTextRegular),
+ backgroundColor: ColorStyles.gray3,
+ ),
+ );
+ }
+
+ String _todayLabel() {
+ final now = DateTime.now();
+ return '${now.month}월 ${now.day}일';
+ }
+
+ String _consentMessage({
+ required String label,
+ required bool enabled,
+ }) {
+ return '${_todayLabel()} $label 알림 '
+ '${enabled ? '동의했어요' : '동의를 거부했어요'}';
+ }
+
+ @override
+ void initState() {
+ super.initState();
+
+ WidgetsBinding.instance.addPostFrameCallback((_) async {
+ final user = ref.read(userSessionProvider);
+ final target =
+ user != null ? NotificationTarget.user : NotificationTarget.guest;
+
+ final deviceToken =
+ await ref.read(getFcmTokenUseCaseProvider).execute();
+ if (deviceToken == null || deviceToken.isEmpty) return;
+
+ try {
+ await ref
+ .read(notificationSettingViewModelProvider.notifier)
+ .fetchSettings(
+ target: target,
+ deviceToken: deviceToken,
+ );
+ } catch (_) {}
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final user = ref.watch(userSessionProvider);
+ final target =
+ user != null ? NotificationTarget.user : NotificationTarget.guest;
+
+ final isAdmin = user != null && user.role.contains('ADMIN');
+
+ final state = ref.watch(notificationSettingViewModelProvider);
+ final vm = ref.read(notificationSettingViewModelProvider.notifier);
+
+ if (state.error != null) {
+ return Scaffold(
+ backgroundColor: Colors.white,
+ appBar: const DetailHeader(
+ title: '알림 설정',
+ backgroundColor: Colors.white,
+ ),
+ body: Center(
+ child: Text(
+ state.error!,
+ style: TextStyles.normalTextRegular.copyWith(color: ColorStyles.black),
+ textAlign: TextAlign.center,
+ ),
+ ),
+ );
+ }
+
+ final getTokenUseCase = ref.read(getFcmTokenUseCaseProvider);
+
+ Future _requireDeviceToken() async {
+ final deviceToken = await getTokenUseCase.execute();
+ if (deviceToken == null || deviceToken.isEmpty) {
+ _showSnack(context, '오류가 발생했어요. 잠시 후 다시 시도해 주세요.');
+ return null;
+ }
+ return deviceToken;
+ }
+
+ Future onToggle({
+ required String label,
+ required String type,
+ required bool nextValue,
+ }) async {
+ final deviceToken = await _requireDeviceToken();
+ if (deviceToken == null) return;
+
+ try {
+ await vm.setToggle(
+ target: target,
+ deviceToken: deviceToken,
+ notificationType: type,
+ nextValue: nextValue,
+ );
+ _showSnack(
+ context,
+ _consentMessage(label: label, enabled: nextValue),
+ );
+ } catch (e) {
+ _showSnack(context, e.toString());
+ }
+ }
+
+ Future onRecruitApplyToggle(bool nextValue) async {
+ final deviceToken = await _requireDeviceToken();
+ if (deviceToken == null) return;
+
+ try {
+ await vm.setRecruitApplyToggle(
+ target: target,
+ deviceToken: deviceToken,
+ nextValue: nextValue,
+ );
+ _showSnack(
+ context,
+ _consentMessage(label: '지원 현황', enabled: nextValue),
+ );
+ } catch (e) {
+ _showSnack(context, e.toString());
+ }
+ }
+
+ Future onRecruitResultToggle(bool nextValue) async {
+ final deviceToken = await _requireDeviceToken();
+ if (deviceToken == null) return;
+
+ try {
+ await vm.setRecruitResultToggle(
+ target: target,
+ deviceToken: deviceToken,
+ nextValue: nextValue,
+ );
+ _showSnack(
+ context,
+ _consentMessage(label: '지원 결과', enabled: nextValue),
+ );
+ } catch (e) {
+ _showSnack(context, e.toString());
+ }
+ }
+
+ return Scaffold(
+ backgroundColor: ColorStyles.white,
+ appBar: const DetailHeader(
+ title: '알림 설정',
+ backgroundColor: ColorStyles.white,
+ ),
+ body: SafeArea(
+ child: Stack(
+ children: [
+ ListView(
+ children: [
+ NotificationSection(
+ title: '공지사항',
+ subtitle: '교내 공지를 빠르게 확인할 수 있어요',
+ children: [
+ NotificationToggleRow(
+ label: '공지',
+ value: state.isEnabled(NotificationTypes.notice),
+ loading: state.isLoading(NotificationTypes.notice),
+ onChanged: (v) => onToggle(
+ label: '공지',
+ type: NotificationTypes.notice,
+ nextValue: v,
+ ),
+ ),
+ ],
+ ),
+ const Divider(thickness: 4, height: 1, color: ColorStyles.gray1),
+
+ NotificationSection(
+ title: '오늘의 일정',
+ subtitle: '매일 아침 8시에 오늘 일정을 확인할 수 있어요',
+ children: [
+ NotificationToggleRow(
+ label: '시간표',
+ value: state.isEnabled(NotificationTypes.timetable),
+ loading: state.isLoading(NotificationTypes.timetable),
+ onChanged: (v) => onToggle(
+ label: '시간표',
+ type: NotificationTypes.timetable,
+ nextValue: v,
+ ),
+ ),
+ NotificationToggleRow(
+ label: '일정',
+ value: state.isEnabled(NotificationTypes.calendar),
+ loading: state.isLoading(NotificationTypes.calendar),
+ onChanged: (v) => onToggle(
+ label: '일정',
+ type: NotificationTypes.calendar,
+ nextValue: v,
+ ),
+ ),
+ ],
+ ),
+ const Divider(thickness: 4, height: 1, color: ColorStyles.gray1),
+
+ if (user != null)
+ NotificationSection(
+ title: '모집 알림',
+ subtitle: '모집 지원·결과를 빠르게 알려드려요.',
+ children: [
+ NotificationToggleRow(
+ label: '지원 현황',
+ value: state.recruitApplyEnabled,
+ loading: state.recruitApplyLoading,
+ onChanged: onRecruitApplyToggle,
+ ),
+ const SizedBox(height: 4),
+ NotificationToggleRow(
+ label: '지원 결과',
+ value: state.recruitResultEnabled,
+ loading: state.recruitResultLoading,
+ onChanged: onRecruitResultToggle,
+ ),
+ ],
+ ),
+
+ const Divider(thickness: 4, height: 1, color: ColorStyles.gray1),
+
+ if (user != null)
+ NotificationSection(
+ title: '채팅 알림',
+ subtitle: '채팅을 빠르게 확인할 수 있어요',
+ children: [
+ NotificationToggleRow(
+ label: '새로운 채팅',
+ value: state.isEnabled(NotificationTypes.chat),
+ loading: state.isLoading(NotificationTypes.chat),
+ onChanged: (v) => onToggle(
+ label: '새로운 채팅',
+ type: NotificationTypes.chat,
+ nextValue: v,
+ ),
+ ),
+ ],
+ ),
+
+ const Divider(thickness: 4, height: 1, color: ColorStyles.gray1),
+
+ if (user != null) ...[
+ NotificationSection(
+ title: '보안',
+ subtitle: '계정 보안을 위한 알림을 설정할 수 있어요',
+ children: [
+ NotificationToggleRow(
+ label: '새로운 기기 로그인',
+ value: state.isEnabled(NotificationTypes.newDevice),
+ loading: state.isLoading(NotificationTypes.newDevice),
+ onChanged: (v) => onToggle(
+ label: '새로운 기기 로그인',
+ type: NotificationTypes.newDevice,
+ nextValue: v,
+ ),
+ ),
+ ],
+ ),
+ ],
+ const Divider(thickness: 4, height: 1, color: ColorStyles.gray1),
+
+ NotificationSection(
+ title: '기타',
+ subtitle: '과팅 오픈 소식을 확인할 수 있어요',
+ children: [
+ NotificationToggleRow(
+ label: '광고성 푸시 알림',
+ value: state.isEnabled(NotificationTypes.marketing),
+ loading: state.isLoading(NotificationTypes.marketing),
+ onChanged: (v) => onToggle(
+ label: '광고성 푸시 알림',
+ type: NotificationTypes.marketing,
+ nextValue: v,
+ ),
+ ),
+ if (user != null) ...[
+ const SizedBox(height: 4),
+ NotificationToggleRow(
+ label: '과팅 오픈',
+ value: state.isEnabled(NotificationTypes.blinddate),
+ loading: state.isLoading(NotificationTypes.blinddate),
+ onChanged: (v) => onToggle(
+ label: '과팅 오픈',
+ type: NotificationTypes.blinddate,
+ nextValue: v,
+ ),
+ ),
+ ],
+ if (isAdmin) ...[
+ const SizedBox(height: 4),
+ NotificationToggleRow(
+ label: '피드백 도착',
+ value: state.isEnabled(NotificationTypes.feedback),
+ loading: state.isLoading(NotificationTypes.feedback),
+ onChanged: (v) => onToggle(
+ label: '피드백 도착',
+ type: NotificationTypes.feedback,
+ nextValue: v,
+ ),
+ ),
+ ],
+ ],
+ ),
+ ],
+ ),
+ if (user != null)
+ Positioned(
+ left: 0,
+ right: 0,
+ bottom: 24,
+ child: Center(
+ child: CategoryTabBar(
+ tabs: const ['전체', '키워드'],
+ selectedIndex: 0,
+ onSelected: (i) {
+ if (i == 0) return;
+
+ Future.microtask(() async {
+ widget.onTapNoticeKeyword();
+ });
+ },
+ isBoard: false,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/setting/notification/providers/notification_setting_provider.dart b/lib/presentation/notification/providers/notification_setting_provider.dart
similarity index 100%
rename from lib/presentation/setting/notification/providers/notification_setting_provider.dart
rename to lib/presentation/notification/providers/notification_setting_provider.dart
diff --git a/lib/presentation/setting/notification/providers/notification_setting_use_case_provider.dart b/lib/presentation/notification/providers/notification_setting_use_case_provider.dart
similarity index 93%
rename from lib/presentation/setting/notification/providers/notification_setting_use_case_provider.dart
rename to lib/presentation/notification/providers/notification_setting_use_case_provider.dart
index eb4848cf..0698010d 100644
--- a/lib/presentation/setting/notification/providers/notification_setting_use_case_provider.dart
+++ b/lib/presentation/notification/providers/notification_setting_use_case_provider.dart
@@ -1,12 +1,10 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
-
import 'package:dongsoop/domain/notification/use_case/notification_disable_use_case.dart';
import 'package:dongsoop/domain/notification/use_case/notification_enable_use_case.dart';
import 'package:dongsoop/domain/notification/use_case/notification_setting_use_case.dart';
import 'package:dongsoop/domain/notification/use_case/notification_setting_apply_use_case.dart';
import 'package:dongsoop/domain/notification/use_case/notification_setting_result_use_case.dart';
-
-import 'package:dongsoop/presentation/setting/notification/providers/notification_setting_provider.dart';
+import 'package:dongsoop/presentation/notification/providers/notification_setting_provider.dart';
final notificationSettingUseCaseProvider =
Provider((ref) {
diff --git a/lib/presentation/setting/notification/view_model/notification_setting_state.dart b/lib/presentation/notification/view_model/notification_setting_state.dart
similarity index 92%
rename from lib/presentation/setting/notification/view_model/notification_setting_state.dart
rename to lib/presentation/notification/view_model/notification_setting_state.dart
index fd1d8613..b376dd10 100644
--- a/lib/presentation/setting/notification/view_model/notification_setting_state.dart
+++ b/lib/presentation/notification/view_model/notification_setting_state.dart
@@ -1,4 +1,4 @@
-import 'package:dongsoop/presentation/setting/notification/view_model/notification_types.dart';
+import 'package:dongsoop/presentation/notification/view_model/notification_types.dart';
class NotificationSettingState {
final Map enabled;
diff --git a/lib/presentation/setting/notification/view_model/notification_setting_view_model.dart b/lib/presentation/notification/view_model/notification_setting_view_model.dart
similarity index 98%
rename from lib/presentation/setting/notification/view_model/notification_setting_view_model.dart
rename to lib/presentation/notification/view_model/notification_setting_view_model.dart
index a50e0dd1..8895d7d1 100644
--- a/lib/presentation/setting/notification/view_model/notification_setting_view_model.dart
+++ b/lib/presentation/notification/view_model/notification_setting_view_model.dart
@@ -1,16 +1,12 @@
import 'package:flutter/foundation.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
-
-import 'package:dongsoop/presentation/setting/notification/providers/notification_setting_provider.dart';
-
+import 'package:dongsoop/presentation/notification/providers/notification_setting_provider.dart';
import 'package:dongsoop/domain/notification/entity/notification_enable_entity.dart';
import 'package:dongsoop/domain/notification/entity/notification_recruit_entity.dart';
import 'package:dongsoop/domain/notification/enum/notification_target.dart';
import 'package:dongsoop/domain/notification/repository/notification_setting_repository.dart';
-
import 'notification_setting_state.dart';
import 'notification_types.dart';
-
part 'notification_setting_view_model.g.dart';
@riverpod
diff --git a/lib/presentation/setting/notification/view_model/notification_setting_view_model.g.dart b/lib/presentation/notification/view_model/notification_setting_view_model.g.dart
similarity index 100%
rename from lib/presentation/setting/notification/view_model/notification_setting_view_model.g.dart
rename to lib/presentation/notification/view_model/notification_setting_view_model.g.dart
diff --git a/lib/presentation/setting/notification/view_model/notification_types.dart b/lib/presentation/notification/view_model/notification_types.dart
similarity index 100%
rename from lib/presentation/setting/notification/view_model/notification_types.dart
rename to lib/presentation/notification/view_model/notification_types.dart
diff --git a/lib/presentation/setting/notification/widget/notification_section.dart b/lib/presentation/notification/widget/notification_section.dart
similarity index 100%
rename from lib/presentation/setting/notification/widget/notification_section.dart
rename to lib/presentation/notification/widget/notification_section.dart
diff --git a/lib/presentation/setting/notification/widget/notification_toggle_row.dart b/lib/presentation/notification/widget/notification_toggle_row.dart
similarity index 100%
rename from lib/presentation/setting/notification/widget/notification_toggle_row.dart
rename to lib/presentation/notification/widget/notification_toggle_row.dart
diff --git a/lib/presentation/setting/notification/notification_screen.dart b/lib/presentation/setting/notification/notification_screen.dart
deleted file mode 100644
index 06c5d4cb..00000000
--- a/lib/presentation/setting/notification/notification_screen.dart
+++ /dev/null
@@ -1,341 +0,0 @@
-import 'package:dongsoop/ui/text_styles.dart';
-import 'package:flutter/material.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-
-import 'package:dongsoop/core/presentation/components/detail_header.dart';
-import 'package:dongsoop/presentation/setting/notification/view_model/notification_setting_view_model.dart';
-import 'package:dongsoop/presentation/setting/notification/view_model/notification_types.dart';
-import 'package:dongsoop/presentation/setting/notification/widget/notification_section.dart';
-import 'package:dongsoop/presentation/setting/notification/widget/notification_toggle_row.dart';
-import 'package:dongsoop/providers/auth_providers.dart';
-import 'package:dongsoop/providers/device_providers.dart';
-import 'package:dongsoop/ui/color_styles.dart';
-
-import 'package:dongsoop/domain/notification/enum/notification_target.dart';
-
-class NotificationScreen extends ConsumerStatefulWidget {
- const NotificationScreen({super.key});
-
- @override
- ConsumerState createState() =>
- _NotificationScreenState();
-}
-
-class _NotificationScreenState
- extends ConsumerState {
- void _showSnack(BuildContext context, String message) {
- final messenger = ScaffoldMessenger.of(context);
- messenger.hideCurrentSnackBar();
- messenger.showSnackBar(
- SnackBar(
- content: Text(message, style: TextStyles.normalTextRegular),
- backgroundColor: ColorStyles.gray3,
- ),
- );
- }
-
- String _todayLabel() {
- final now = DateTime.now();
- return '${now.month}월 ${now.day}일';
- }
-
- String _consentMessage({
- required String label,
- required bool enabled,
- }) {
- return '${_todayLabel()} $label 알림 '
- '${enabled ? '동의했어요' : '동의를 거부했어요'}';
- }
-
- @override
- void initState() {
- super.initState();
-
- WidgetsBinding.instance.addPostFrameCallback((_) async {
- final user = ref.read(userSessionProvider);
- final target =
- user != null ? NotificationTarget.user : NotificationTarget.guest;
-
- final deviceToken =
- await ref.read(getFcmTokenUseCaseProvider).execute();
- if (deviceToken == null || deviceToken.isEmpty) return;
-
- try {
- await ref
- .read(notificationSettingViewModelProvider.notifier)
- .fetchSettings(
- target: target,
- deviceToken: deviceToken,
- );
- } catch (_) {}
- });
- }
-
- @override
- Widget build(BuildContext context) {
- final user = ref.watch(userSessionProvider);
- final target =
- user != null ? NotificationTarget.user : NotificationTarget.guest;
-
- final isAdmin = user != null && user.role.contains('ADMIN');
-
- final state = ref.watch(notificationSettingViewModelProvider);
- final vm = ref.read(notificationSettingViewModelProvider.notifier);
-
- if (state.error != null) {
- return Scaffold(
- backgroundColor: Colors.white,
- appBar: const DetailHeader(
- title: '알림 설정',
- backgroundColor: Colors.white,
- ),
- body: Center(
- child: Text(
- state.error!,
- style: TextStyles.normalTextRegular.copyWith(color: ColorStyles.black),
- textAlign: TextAlign.center,
- ),
- ),
- );
- }
-
- final getTokenUseCase = ref.read(getFcmTokenUseCaseProvider);
-
- Future _requireDeviceToken() async {
- final deviceToken = await getTokenUseCase.execute();
- if (deviceToken == null || deviceToken.isEmpty) {
- _showSnack(context, '오류가 발생했어요. 잠시 후 다시 시도해 주세요.');
- return null;
- }
- return deviceToken;
- }
-
- Future onToggle({
- required String label,
- required String type,
- required bool nextValue,
- }) async {
- final deviceToken = await _requireDeviceToken();
- if (deviceToken == null) return;
-
- try {
- await vm.setToggle(
- target: target,
- deviceToken: deviceToken,
- notificationType: type,
- nextValue: nextValue,
- );
- _showSnack(
- context,
- _consentMessage(label: label, enabled: nextValue),
- );
- } catch (e) {
- _showSnack(context, e.toString());
- }
- }
-
- Future onRecruitApplyToggle(bool nextValue) async {
- final deviceToken = await _requireDeviceToken();
- if (deviceToken == null) return;
-
- try {
- await vm.setRecruitApplyToggle(
- target: target,
- deviceToken: deviceToken,
- nextValue: nextValue,
- );
- _showSnack(
- context,
- _consentMessage(label: '지원 현황', enabled: nextValue),
- );
- } catch (e) {
- _showSnack(context, e.toString());
- }
- }
-
- Future onRecruitResultToggle(bool nextValue) async {
- final deviceToken = await _requireDeviceToken();
- if (deviceToken == null) return;
-
- try {
- await vm.setRecruitResultToggle(
- target: target,
- deviceToken: deviceToken,
- nextValue: nextValue,
- );
- _showSnack(
- context,
- _consentMessage(label: '지원 결과', enabled: nextValue),
- );
- } catch (e) {
- _showSnack(context, e.toString());
- }
- }
-
- return Scaffold(
- backgroundColor: ColorStyles.gray1,
- appBar: const DetailHeader(
- title: '알림 설정',
- backgroundColor: ColorStyles.gray1,
- ),
- body: SafeArea(
- child: ListView(
- children: [
- NotificationSection(
- title: '공지사항',
- subtitle: '교내 공지를 빠르게 확인할 수 있어요',
- children: [
- NotificationToggleRow(
- label: '공지',
- value: state.isEnabled(NotificationTypes.notice),
- loading: state.isLoading(NotificationTypes.notice),
- onChanged: (v) => onToggle(
- label: '공지',
- type: NotificationTypes.notice,
- nextValue: v,
- ),
- ),
- ],
- ),
- const Divider(thickness: 4, height: 1, color: ColorStyles.gray1),
-
- NotificationSection(
- title: '오늘의 일정',
- subtitle: '매일 아침 8시에 오늘 일정을 확인할 수 있어요',
- children: [
- NotificationToggleRow(
- label: '시간표',
- value: state.isEnabled(NotificationTypes.timetable),
- loading: state.isLoading(NotificationTypes.timetable),
- onChanged: (v) => onToggle(
- label: '시간표',
- type: NotificationTypes.timetable,
- nextValue: v,
- ),
- ),
- NotificationToggleRow(
- label: '일정',
- value: state.isEnabled(NotificationTypes.calendar),
- loading: state.isLoading(NotificationTypes.calendar),
- onChanged: (v) => onToggle(
- label: '일정',
- type: NotificationTypes.calendar,
- nextValue: v,
- ),
- ),
- ],
- ),
- const Divider(thickness: 4, height: 1, color: ColorStyles.gray1),
-
- if (user != null)
- NotificationSection(
- title: '모집 알림',
- subtitle: '모집 지원·결과를 빠르게 알려드려요.',
- children: [
- NotificationToggleRow(
- label: '지원 현황',
- value: state.recruitApplyEnabled,
- loading: state.recruitApplyLoading,
- onChanged: onRecruitApplyToggle,
- ),
- const SizedBox(height: 4),
- NotificationToggleRow(
- label: '지원 결과',
- value: state.recruitResultEnabled,
- loading: state.recruitResultLoading,
- onChanged: onRecruitResultToggle,
- ),
- ],
- ),
-
- const Divider(thickness: 4, height: 1, color: ColorStyles.gray1),
-
- if (user != null)
- NotificationSection(
- title: '채팅 알림',
- subtitle: '채팅을 빠르게 확인할 수 있어요',
- children: [
- NotificationToggleRow(
- label: '새로운 채팅',
- value: state.isEnabled(NotificationTypes.chat),
- loading: state.isLoading(NotificationTypes.chat),
- onChanged: (v) => onToggle(
- label: '새로운 채팅',
- type: NotificationTypes.chat,
- nextValue: v,
- ),
- ),
- ],
- ),
-
- const Divider(thickness: 4, height: 1, color: ColorStyles.gray1),
-
- NotificationSection(
- title: '보안',
- subtitle: '계정 보안을 위한 알림을 설정할 수 있어요',
- children: [
- if (user != null) ...[
- NotificationToggleRow(
- label: '새로운 기기 로그인',
- value: state.isEnabled(NotificationTypes.newDevice),
- loading: state.isLoading(NotificationTypes.newDevice),
- onChanged: (v) => onToggle(
- label: '새로운 기기 로그인',
- type: NotificationTypes.newDevice,
- nextValue: v,
- ),
- ),
- ],
- ],
- ),
-
- const Divider(thickness: 4, height: 1, color: ColorStyles.gray1),
-
- NotificationSection(
- title: '기타',
- subtitle: '과팅 오픈 소식을 확인할 수 있어요',
- children: [
- NotificationToggleRow(
- label: '광고성 푸시 알림',
- value: state.isEnabled(NotificationTypes.marketing),
- loading: state.isLoading(NotificationTypes.marketing),
- onChanged: (v) => onToggle(
- label: '광고성 푸시 알림',
- type: NotificationTypes.marketing,
- nextValue: v,
- ),
- ),
- if (user != null) ...[
- const SizedBox(height: 4),
- NotificationToggleRow(
- label: '과팅 오픈',
- value: state.isEnabled(NotificationTypes.blinddate),
- loading: state.isLoading(NotificationTypes.blinddate),
- onChanged: (v) => onToggle(
- label: '과팅 오픈',
- type: NotificationTypes.blinddate,
- nextValue: v,
- ),
- ),
- ],
- if (isAdmin) ...[
- const SizedBox(height: 4),
- NotificationToggleRow(
- label: '피드백 도착',
- value: state.isEnabled(NotificationTypes.feedback),
- loading: state.isLoading(NotificationTypes.feedback),
- onChanged: (v) => onToggle(
- label: '피드백 도착',
- type: NotificationTypes.feedback,
- nextValue: v,
- ),
- ),
- ],
- ],
- ),
- ],
- ),
- ),
- );
- }
-}
diff --git a/lib/presentation/setting/setting_screen.dart b/lib/presentation/setting/setting_screen.dart
index 5cca6f29..2bac2343 100644
--- a/lib/presentation/setting/setting_screen.dart
+++ b/lib/presentation/setting/setting_screen.dart
@@ -10,13 +10,11 @@ import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
class SettingScreen extends HookConsumerWidget {
- final VoidCallback onTapNotification;
final VoidCallback onTapDevice;
final VoidCallback onTapPasswordReset;
const SettingScreen({
super.key,
- required this.onTapNotification,
required this.onTapDevice,
required this.onTapPasswordReset,
});
@@ -72,7 +70,7 @@ class SettingScreen extends HookConsumerWidget {
title: '이용 안내',
children: [
buildSettingsItem(
- label: '버전 1.11.1',
+ label: '버전 1.11.2',
onTap: () {},
),
buildSettingsItem(
@@ -101,36 +99,33 @@ class SettingScreen extends HookConsumerWidget {
),
],
),
+ SizedBox(height: 24),
- SizedBox(height: 40),
- buildSettingsSection(
- title: '앱 설정',
- children: [
- buildSettingsItem(
- label: '알림 설정',
- onTap: onTapNotification
- ),
- if (user != null)
- buildSettingsItem(
- label: '채팅 캐시 삭제',
- onTap: () async {
- // 채팅 캐시 삭제 다이얼로그
- showDialog(
- context: context,
- builder: (_) => CustomConfirmDialog(
- title: '채팅 캐시 삭제',
- content: '채팅 내역을 삭제하시겠어요?',
- onConfirm: () async {
- await viewModel.localDataDelete();
- Navigator.of(context).pop(); // 다이얼로그 닫기
- },
- ),
- );
- },
- ),
- ],
- ),
- const SizedBox(height: 40),
+ if (user != null) ...[
+ buildSettingsSection(
+ title: '앱 설정',
+ children: [
+ buildSettingsItem(
+ label: '채팅 캐시 삭제',
+ onTap: () async {
+ // 채팅 캐시 삭제 다이얼로그
+ showDialog(
+ context: context,
+ builder: (_) => CustomConfirmDialog(
+ title: '채팅 캐시 삭제',
+ content: '채팅 내역을 삭제하시겠어요?',
+ onConfirm: () async {
+ await viewModel.localDataDelete();
+ Navigator.of(context).pop(); // 다이얼로그 닫기
+ },
+ ),
+ );
+ },
+ ),
+ ],
+ ),
+ const SizedBox(height: 24),
+ ],
if (user != null) ...[
buildSettingsSection(
@@ -146,7 +141,7 @@ class SettingScreen extends HookConsumerWidget {
),
],
),
- const SizedBox(height: 40),
+ const SizedBox(height: 24),
],
if (user != null)
@@ -212,10 +207,12 @@ class SettingScreen extends HookConsumerWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Text(title,
- style: TextStyles.largeTextBold.copyWith(
- color: ColorStyles.black,
- )),
+ Text(
+ title,
+ style: TextStyles.largeTextBold.copyWith(
+ color: ColorStyles.black,
+ ),
+ ),
const SizedBox(height: 16),
...children,
],
diff --git a/macos/Flutter/ephemeral/Flutter-Generated.xcconfig b/macos/Flutter/ephemeral/Flutter-Generated.xcconfig
index 5b78bd36..b7d0b58c 100644
--- a/macos/Flutter/ephemeral/Flutter-Generated.xcconfig
+++ b/macos/Flutter/ephemeral/Flutter-Generated.xcconfig
@@ -3,8 +3,8 @@ FLUTTER_ROOT=/Users/seungwon-woo/development/flutter
FLUTTER_APPLICATION_PATH=/Users/seungwon-woo/dongsoop
COCOAPODS_PARALLEL_CODE_SIGN=true
FLUTTER_BUILD_DIR=build
-FLUTTER_BUILD_NAME=1.11.1
-FLUTTER_BUILD_NUMBER=51
+FLUTTER_BUILD_NAME=1.11.2
+FLUTTER_BUILD_NUMBER=52
DART_OBFUSCATION=false
TRACK_WIDGET_CREATION=true
TREE_SHAKE_ICONS=false
diff --git a/macos/Flutter/ephemeral/flutter_export_environment.sh b/macos/Flutter/ephemeral/flutter_export_environment.sh
index c9b59136..99bc4b14 100755
--- a/macos/Flutter/ephemeral/flutter_export_environment.sh
+++ b/macos/Flutter/ephemeral/flutter_export_environment.sh
@@ -4,8 +4,8 @@ export "FLUTTER_ROOT=/Users/seungwon-woo/development/flutter"
export "FLUTTER_APPLICATION_PATH=/Users/seungwon-woo/dongsoop"
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
export "FLUTTER_BUILD_DIR=build"
-export "FLUTTER_BUILD_NAME=1.11.1"
-export "FLUTTER_BUILD_NUMBER=51"
+export "FLUTTER_BUILD_NAME=1.11.2"
+export "FLUTTER_BUILD_NUMBER=52"
export "DART_OBFUSCATION=false"
export "TRACK_WIDGET_CREATION=true"
export "TREE_SHAKE_ICONS=false"
diff --git a/pubspec.yaml b/pubspec.yaml
index 2de59a3c..614af111 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
-version: 1.11.1+51
+version: 1.11.2+52
environment:
sdk: ^3.6.0