Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions assets/icons/alarm_setting.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "";
Expand Down Expand Up @@ -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 = "";
Expand Down Expand Up @@ -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 = "";
Expand Down
22 changes: 22 additions & 0 deletions ios/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
}
}
19 changes: 19 additions & 0 deletions lib/core/environment/app_distribution.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import 'dart:io';

import 'package:flutter/services.dart';

class AppDistribution {
static const MethodChannel _channel = MethodChannel('dongsoop/app_distribution');

static Future<bool> isTestFlight() async {
if (!Platform.isIOS) return false;

try {
final result = await _channel.invokeMethod<bool>('isTestFlight');
return result ?? false;
} catch (e) {
print('[isTestFlight Error] Failed to detect TestFlight: $e');
return false;
}
}
}
25 changes: 22 additions & 3 deletions lib/core/presentation/components/admob_native_ad.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -23,25 +24,36 @@ class AdmobNativeAd extends StatefulWidget {
class _AdmobNativeAdState extends State<AdmobNativeAd> {
NativeAd? _nativeAd;
bool _nativeAdIsLoaded = false;
bool _useTestAds = kDebugMode;

final Logger _logger = Logger();

static const String _androidTestAdUnitId = 'ca-app-pub-3940256099942544/2247696110';
static const String _iosTestAdUnitId = 'ca-app-pub-3940256099942544/3986624511';

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<void> _initAndLoadAd() async {
final isTestFlight = await AppDistribution.isTestFlight();
_useTestAds = kDebugMode || isTestFlight;
_logger.d('AdMob use test ads: $_useTestAds');
if (!mounted) return;
_loadAd();
}

Expand Down Expand Up @@ -69,6 +81,13 @@ class _AdmobNativeAdState extends State<AdmobNativeAd> {
onAdFailedToLoad: (ad, error) {
_logger.e('$NativeAd failed to load: $error');
ad.dispose();

if (mounted) {
setState(() {
_nativeAd = null;
_nativeAdIsLoaded = false;
});
}
},
),
request: const AdRequest(),
Expand Down Expand Up @@ -104,7 +123,7 @@ class _AdmobNativeAdState extends State<AdmobNativeAd> {
child: AdWidget(ad: _nativeAd!),
);
}
// 광고 로딩 중에는 빈 공간 또는 스켈레톤 UI를 보여줄 수 있습니다.

return SizedBox(height: widget.height);
}
}
50 changes: 28 additions & 22 deletions lib/core/routing/router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -263,23 +263,21 @@ final router = GoRouter(
),
),
GoRoute(
path: RoutePaths.adminReportSanction,
builder: (context, state) {
final extra = state.extra as Map<String, dynamic>?;
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<String, dynamic>?;
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);
},
Expand All @@ -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,
Expand Down Expand Up @@ -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);
},
),
);
},
),
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import 'domain/chat/model/chat_room_member.dart';

Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
MobileAds.instance.initialize();
await MobileAds.instance.initialize();
await dotenv.load(); // .env 파일 로드
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);

Expand Down
4 changes: 2 additions & 2 deletions lib/presentation/app/marketing_push_guard.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand Down
24 changes: 22 additions & 2 deletions lib/presentation/home/notice_list_page_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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: () {
Expand Down
Loading
Loading