style: apply dart format across project
This commit is contained in:
@@ -13,29 +13,29 @@ import '../l10n/app_localizations.dart';
|
||||
/// AddSubscriptionScreen의 비즈니스 로직을 관리하는 Controller
|
||||
class AddSubscriptionController {
|
||||
final BuildContext context;
|
||||
|
||||
|
||||
// Form Key
|
||||
final formKey = GlobalKey<FormState>();
|
||||
|
||||
|
||||
// Text Controllers
|
||||
final serviceNameController = TextEditingController();
|
||||
final monthlyCostController = TextEditingController();
|
||||
final nextBillingDateController = TextEditingController();
|
||||
final websiteUrlController = TextEditingController();
|
||||
final eventPriceController = TextEditingController();
|
||||
|
||||
|
||||
// Form State
|
||||
String billingCycle = 'monthly';
|
||||
String currency = 'KRW';
|
||||
DateTime? nextBillingDate;
|
||||
bool isLoading = false;
|
||||
String? selectedCategoryId;
|
||||
|
||||
|
||||
// Event State
|
||||
bool isEventActive = false;
|
||||
DateTime? eventStartDate = DateTime.now();
|
||||
DateTime? eventEndDate = DateTime.now().add(const Duration(days: 30));
|
||||
|
||||
|
||||
// Focus Nodes
|
||||
final serviceNameFocus = FocusNode();
|
||||
final monthlyCostFocus = FocusNode();
|
||||
@@ -44,20 +44,20 @@ class AddSubscriptionController {
|
||||
final websiteUrlFocus = FocusNode();
|
||||
final categoryFocus = FocusNode();
|
||||
final currencyFocus = FocusNode();
|
||||
|
||||
|
||||
// Animation Controller
|
||||
AnimationController? animationController;
|
||||
Animation<double>? fadeAnimation;
|
||||
Animation<Offset>? slideAnimation;
|
||||
|
||||
|
||||
// Scroll Controller
|
||||
final ScrollController scrollController = ScrollController();
|
||||
double scrollOffset = 0;
|
||||
|
||||
|
||||
// UI State
|
||||
int currentEditingField = -1;
|
||||
bool isSaveHovered = false;
|
||||
|
||||
|
||||
// Gradient Colors
|
||||
final List<Color> gradientColors = [
|
||||
const Color(0xFF3B82F6),
|
||||
@@ -71,19 +71,19 @@ class AddSubscriptionController {
|
||||
void initialize({required TickerProvider vsync}) {
|
||||
// 결제일 기본값을 오늘 날짜로 설정
|
||||
nextBillingDate = DateTime.now();
|
||||
|
||||
|
||||
// 서비스명 컨트롤러에 리스너 추가
|
||||
serviceNameController.addListener(onServiceNameChanged);
|
||||
|
||||
|
||||
// 웹사이트 URL 컨트롤러에 리스너 추가
|
||||
websiteUrlController.addListener(onWebsiteUrlChanged);
|
||||
|
||||
|
||||
// 애니메이션 컨트롤러 초기화
|
||||
animationController = AnimationController(
|
||||
vsync: vsync,
|
||||
duration: const Duration(milliseconds: 800),
|
||||
);
|
||||
|
||||
|
||||
fadeAnimation = Tween<double>(
|
||||
begin: 0.0,
|
||||
end: 1.0,
|
||||
@@ -91,7 +91,7 @@ class AddSubscriptionController {
|
||||
parent: animationController!,
|
||||
curve: Curves.easeIn,
|
||||
));
|
||||
|
||||
|
||||
slideAnimation = Tween<Offset>(
|
||||
begin: const Offset(0.0, 0.2),
|
||||
end: Offset.zero,
|
||||
@@ -99,12 +99,12 @@ class AddSubscriptionController {
|
||||
parent: animationController!,
|
||||
curve: Curves.easeOut,
|
||||
));
|
||||
|
||||
|
||||
// 스크롤 리스너
|
||||
scrollController.addListener(() {
|
||||
scrollOffset = scrollController.offset;
|
||||
});
|
||||
|
||||
|
||||
// 애니메이션 시작
|
||||
animationController!.forward();
|
||||
}
|
||||
@@ -117,7 +117,7 @@ class AddSubscriptionController {
|
||||
nextBillingDateController.dispose();
|
||||
websiteUrlController.dispose();
|
||||
eventPriceController.dispose();
|
||||
|
||||
|
||||
// Focus Nodes
|
||||
serviceNameFocus.dispose();
|
||||
monthlyCostFocus.dispose();
|
||||
@@ -126,10 +126,10 @@ class AddSubscriptionController {
|
||||
websiteUrlFocus.dispose();
|
||||
categoryFocus.dispose();
|
||||
currencyFocus.dispose();
|
||||
|
||||
|
||||
// Animation
|
||||
animationController?.dispose();
|
||||
|
||||
|
||||
// Scroll
|
||||
scrollController.dispose();
|
||||
}
|
||||
@@ -138,43 +138,46 @@ class AddSubscriptionController {
|
||||
void onServiceNameChanged() {
|
||||
autoSelectCategory();
|
||||
}
|
||||
|
||||
|
||||
/// 웹사이트 URL 변경시 호출
|
||||
void onWebsiteUrlChanged() async {
|
||||
final url = websiteUrlController.text.trim();
|
||||
|
||||
|
||||
// URL이 비어있거나 너무 짧으면 무시
|
||||
if (url.isEmpty || url.length < 5) return;
|
||||
|
||||
|
||||
// 이미 서비스명이 입력되어 있으면 자동 매칭하지 않음
|
||||
if (serviceNameController.text.isNotEmpty) return;
|
||||
|
||||
|
||||
try {
|
||||
// URL로 서비스 정보 찾기
|
||||
final serviceInfo = await SubscriptionUrlMatcher.findServiceByUrl(url);
|
||||
|
||||
|
||||
if (serviceInfo != null && context.mounted) {
|
||||
// 서비스명 자동 입력
|
||||
serviceNameController.text = serviceInfo.serviceName;
|
||||
|
||||
|
||||
// 카테고리 자동 선택
|
||||
final categoryProvider = Provider.of<CategoryProvider>(context, listen: false);
|
||||
final categoryProvider =
|
||||
Provider.of<CategoryProvider>(context, listen: false);
|
||||
final categories = categoryProvider.categories;
|
||||
|
||||
|
||||
// 카테고리 ID로 매칭
|
||||
final matchedCategory = categories.firstWhere(
|
||||
(cat) => cat.name == serviceInfo.categoryNameKr ||
|
||||
cat.name == serviceInfo.categoryNameEn,
|
||||
(cat) =>
|
||||
cat.name == serviceInfo.categoryNameKr ||
|
||||
cat.name == serviceInfo.categoryNameEn,
|
||||
orElse: () => categories.first,
|
||||
);
|
||||
|
||||
|
||||
selectedCategoryId = matchedCategory.id;
|
||||
|
||||
|
||||
// 스낵바로 알림
|
||||
if (context.mounted) {
|
||||
AppSnackBar.showSuccess(
|
||||
context: context,
|
||||
message: AppLocalizations.of(context).serviceRecognized(serviceInfo.serviceName),
|
||||
message: AppLocalizations.of(context)
|
||||
.serviceRecognized(serviceInfo.serviceName),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -187,17 +190,18 @@ class AddSubscriptionController {
|
||||
|
||||
/// 카테고리 자동 선택
|
||||
void autoSelectCategory() {
|
||||
final categoryProvider = Provider.of<CategoryProvider>(context, listen: false);
|
||||
final categoryProvider =
|
||||
Provider.of<CategoryProvider>(context, listen: false);
|
||||
final categories = categoryProvider.categories;
|
||||
|
||||
|
||||
final serviceName = serviceNameController.text.toLowerCase();
|
||||
|
||||
|
||||
// 서비스명에 기반한 카테고리 매칭 로직
|
||||
dynamic matchedCategory;
|
||||
|
||||
|
||||
// 엔터테인먼트 관련 키워드
|
||||
if (serviceName.contains('netflix') ||
|
||||
serviceName.contains('youtube') ||
|
||||
if (serviceName.contains('netflix') ||
|
||||
serviceName.contains('youtube') ||
|
||||
serviceName.contains('disney') ||
|
||||
serviceName.contains('왓챠') ||
|
||||
serviceName.contains('티빙') ||
|
||||
@@ -210,64 +214,64 @@ class AddSubscriptionController {
|
||||
);
|
||||
}
|
||||
// 음악 관련 키워드
|
||||
else if (serviceName.contains('spotify') ||
|
||||
serviceName.contains('apple music') ||
|
||||
serviceName.contains('멜론') ||
|
||||
serviceName.contains('지니') ||
|
||||
serviceName.contains('플로') ||
|
||||
serviceName.contains('벅스')) {
|
||||
else if (serviceName.contains('spotify') ||
|
||||
serviceName.contains('apple music') ||
|
||||
serviceName.contains('멜론') ||
|
||||
serviceName.contains('지니') ||
|
||||
serviceName.contains('플로') ||
|
||||
serviceName.contains('벅스')) {
|
||||
matchedCategory = categories.firstWhere(
|
||||
(cat) => cat.name == 'music',
|
||||
orElse: () => categories.first,
|
||||
);
|
||||
}
|
||||
// 생산성 관련 키워드
|
||||
else if (serviceName.contains('notion') ||
|
||||
serviceName.contains('microsoft') ||
|
||||
serviceName.contains('office') ||
|
||||
serviceName.contains('google') ||
|
||||
serviceName.contains('dropbox') ||
|
||||
serviceName.contains('icloud') ||
|
||||
serviceName.contains('adobe')) {
|
||||
else if (serviceName.contains('notion') ||
|
||||
serviceName.contains('microsoft') ||
|
||||
serviceName.contains('office') ||
|
||||
serviceName.contains('google') ||
|
||||
serviceName.contains('dropbox') ||
|
||||
serviceName.contains('icloud') ||
|
||||
serviceName.contains('adobe')) {
|
||||
matchedCategory = categories.firstWhere(
|
||||
(cat) => cat.name == '생산성',
|
||||
orElse: () => categories.first,
|
||||
);
|
||||
}
|
||||
// 게임 관련 키워드
|
||||
else if (serviceName.contains('xbox') ||
|
||||
serviceName.contains('playstation') ||
|
||||
serviceName.contains('nintendo') ||
|
||||
serviceName.contains('steam') ||
|
||||
serviceName.contains('게임')) {
|
||||
else if (serviceName.contains('xbox') ||
|
||||
serviceName.contains('playstation') ||
|
||||
serviceName.contains('nintendo') ||
|
||||
serviceName.contains('steam') ||
|
||||
serviceName.contains('게임')) {
|
||||
matchedCategory = categories.firstWhere(
|
||||
(cat) => cat.name == '게임',
|
||||
orElse: () => categories.first,
|
||||
);
|
||||
}
|
||||
// 교육 관련 키워드
|
||||
else if (serviceName.contains('coursera') ||
|
||||
serviceName.contains('udemy') ||
|
||||
serviceName.contains('인프런') ||
|
||||
serviceName.contains('패스트캠퍼스') ||
|
||||
serviceName.contains('클래스101')) {
|
||||
else if (serviceName.contains('coursera') ||
|
||||
serviceName.contains('udemy') ||
|
||||
serviceName.contains('인프런') ||
|
||||
serviceName.contains('패스트캠퍼스') ||
|
||||
serviceName.contains('클래스101')) {
|
||||
matchedCategory = categories.firstWhere(
|
||||
(cat) => cat.name == '교육',
|
||||
orElse: () => categories.first,
|
||||
);
|
||||
}
|
||||
// 쇼핑 관련 키워드
|
||||
else if (serviceName.contains('쿠팡') ||
|
||||
serviceName.contains('coupang') ||
|
||||
serviceName.contains('amazon') ||
|
||||
serviceName.contains('네이버') ||
|
||||
serviceName.contains('11번가')) {
|
||||
else if (serviceName.contains('쿠팡') ||
|
||||
serviceName.contains('coupang') ||
|
||||
serviceName.contains('amazon') ||
|
||||
serviceName.contains('네이버') ||
|
||||
serviceName.contains('11번가')) {
|
||||
matchedCategory = categories.firstWhere(
|
||||
(cat) => cat.name == '쇼핑',
|
||||
orElse: () => categories.first,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (matchedCategory != null) {
|
||||
selectedCategoryId = matchedCategory.id;
|
||||
}
|
||||
@@ -276,9 +280,9 @@ class AddSubscriptionController {
|
||||
/// SMS 스캔
|
||||
Future<void> scanSMS({required Function setState}) async {
|
||||
if (kIsWeb) return;
|
||||
|
||||
|
||||
setState(() => isLoading = true);
|
||||
|
||||
|
||||
try {
|
||||
if (!await SMSService.hasSMSPermission()) {
|
||||
final granted = await SMSService.requestSMSPermission();
|
||||
@@ -292,7 +296,7 @@ class AddSubscriptionController {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final subscriptions = await SMSService.scanSubscriptions();
|
||||
if (subscriptions.isEmpty) {
|
||||
if (context.mounted) {
|
||||
@@ -303,48 +307,51 @@ class AddSubscriptionController {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
final subscription = subscriptions.first;
|
||||
|
||||
|
||||
// SMS에서 서비스 정보 추출 시도
|
||||
ServiceInfo? serviceInfo;
|
||||
final smsContent = subscription['smsContent'] ?? '';
|
||||
|
||||
|
||||
if (smsContent.isNotEmpty) {
|
||||
try {
|
||||
serviceInfo = await SubscriptionUrlMatcher.extractServiceFromSms(smsContent);
|
||||
serviceInfo =
|
||||
await SubscriptionUrlMatcher.extractServiceFromSms(smsContent);
|
||||
} catch (e) {
|
||||
if (kDebugMode) {
|
||||
print('AddSubscriptionController: SMS 서비스 추출 실패 - $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
setState(() {
|
||||
// 서비스 정보가 있으면 우선 사용, 없으면 SMS에서 추출한 정보 사용
|
||||
if (serviceInfo != null) {
|
||||
serviceNameController.text = serviceInfo.serviceName;
|
||||
websiteUrlController.text = serviceInfo.serviceUrl ?? '';
|
||||
|
||||
|
||||
// 카테고리 자동 선택
|
||||
final categoryProvider = Provider.of<CategoryProvider>(context, listen: false);
|
||||
final categoryProvider =
|
||||
Provider.of<CategoryProvider>(context, listen: false);
|
||||
final categories = categoryProvider.categories;
|
||||
|
||||
|
||||
final matchedCategory = categories.firstWhere(
|
||||
(cat) => cat.name == serviceInfo!.categoryNameKr ||
|
||||
cat.name == serviceInfo.categoryNameEn,
|
||||
(cat) =>
|
||||
cat.name == serviceInfo!.categoryNameKr ||
|
||||
cat.name == serviceInfo.categoryNameEn,
|
||||
orElse: () => categories.first,
|
||||
);
|
||||
|
||||
|
||||
selectedCategoryId = matchedCategory.id;
|
||||
} else {
|
||||
// 기존 로직 사용
|
||||
serviceNameController.text = subscription['serviceName'] ?? '';
|
||||
}
|
||||
|
||||
|
||||
// 비용 처리 및 통화 단위 자동 감지
|
||||
final costValue = subscription['monthlyCost']?.toString() ?? '';
|
||||
|
||||
|
||||
if (costValue.isNotEmpty) {
|
||||
// 달러 표시가 있거나 소수점이 있으면 달러로 판단
|
||||
if (costValue.contains('\$') || costValue.contains('.')) {
|
||||
@@ -353,41 +360,41 @@ class AddSubscriptionController {
|
||||
if (!numericValue.contains('.')) {
|
||||
numericValue = '$numericValue.00';
|
||||
}
|
||||
final double parsedValue =
|
||||
final double parsedValue =
|
||||
double.tryParse(numericValue.replaceAll(',', '')) ?? 0.0;
|
||||
monthlyCostController.text =
|
||||
monthlyCostController.text =
|
||||
NumberFormat('#,##0.00').format(parsedValue);
|
||||
} else {
|
||||
currency = 'KRW';
|
||||
String numericValue =
|
||||
String numericValue =
|
||||
costValue.replaceAll('₩', '').replaceAll(',', '').trim();
|
||||
final int parsedValue = int.tryParse(numericValue) ?? 0;
|
||||
monthlyCostController.text =
|
||||
monthlyCostController.text =
|
||||
NumberFormat.decimalPattern().format(parsedValue);
|
||||
}
|
||||
} else {
|
||||
monthlyCostController.text = '';
|
||||
}
|
||||
|
||||
|
||||
billingCycle = subscription['billingCycle'] ?? '월간';
|
||||
nextBillingDate = subscription['nextBillingDate'] != null
|
||||
? DateTime.parse(subscription['nextBillingDate'])
|
||||
: DateTime.now();
|
||||
|
||||
|
||||
// 서비스 정보가 없고 서비스명이 있으면 URL 자동 매칭 시도
|
||||
if (serviceInfo == null &&
|
||||
subscription['serviceName'] != null &&
|
||||
if (serviceInfo == null &&
|
||||
subscription['serviceName'] != null &&
|
||||
subscription['serviceName'].isNotEmpty) {
|
||||
final suggestedUrl =
|
||||
final suggestedUrl =
|
||||
SubscriptionUrlMatcher.suggestUrl(subscription['serviceName']);
|
||||
if (suggestedUrl != null && websiteUrlController.text.isEmpty) {
|
||||
websiteUrlController.text = suggestedUrl;
|
||||
}
|
||||
|
||||
|
||||
// 서비스명 기반으로 카테고리 자동 선택
|
||||
autoSelectCategory();
|
||||
}
|
||||
|
||||
|
||||
// 애니메이션 재생
|
||||
animationController!.reset();
|
||||
animationController!.forward();
|
||||
@@ -396,7 +403,8 @@ class AddSubscriptionController {
|
||||
if (context.mounted) {
|
||||
AppSnackBar.showError(
|
||||
context: context,
|
||||
message: AppLocalizations.of(context).smsScanErrorWithMessage(e.toString()),
|
||||
message: AppLocalizations.of(context)
|
||||
.smsScanErrorWithMessage(e.toString()),
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
@@ -412,20 +420,19 @@ class AddSubscriptionController {
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
|
||||
|
||||
try {
|
||||
// 콤마 제거하고 숫자만 추출
|
||||
final monthlyCost =
|
||||
final monthlyCost =
|
||||
double.parse(monthlyCostController.text.replaceAll(',', ''));
|
||||
|
||||
|
||||
// 이벤트 가격 파싱
|
||||
double? eventPrice;
|
||||
if (isEventActive && eventPriceController.text.isNotEmpty) {
|
||||
eventPrice = double.tryParse(
|
||||
eventPriceController.text.replaceAll(',', '')
|
||||
);
|
||||
eventPrice =
|
||||
double.tryParse(eventPriceController.text.replaceAll(',', ''));
|
||||
}
|
||||
|
||||
|
||||
await Provider.of<SubscriptionProvider>(context, listen: false)
|
||||
.addSubscription(
|
||||
serviceName: serviceNameController.text.trim(),
|
||||
@@ -440,7 +447,7 @@ class AddSubscriptionController {
|
||||
eventEndDate: eventEndDate,
|
||||
eventPrice: eventPrice,
|
||||
);
|
||||
|
||||
|
||||
if (context.mounted) {
|
||||
Navigator.pop(context, true); // 성공 여부 반환
|
||||
}
|
||||
@@ -448,11 +455,12 @@ class AddSubscriptionController {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
|
||||
|
||||
if (context.mounted) {
|
||||
AppSnackBar.showError(
|
||||
context: context,
|
||||
message: AppLocalizations.of(context).saveErrorWithMessage(e.toString()),
|
||||
message:
|
||||
AppLocalizations.of(context).saveErrorWithMessage(e.toString()),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -464,4 +472,4 @@ class AddSubscriptionController {
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,17 +17,17 @@ import '../l10n/app_localizations.dart';
|
||||
class DetailScreenController extends ChangeNotifier {
|
||||
final BuildContext context;
|
||||
final SubscriptionModel subscription;
|
||||
|
||||
|
||||
// Text Controllers
|
||||
late TextEditingController serviceNameController;
|
||||
late TextEditingController monthlyCostController;
|
||||
late TextEditingController websiteUrlController;
|
||||
late TextEditingController eventPriceController;
|
||||
|
||||
|
||||
// Display Names
|
||||
String? _displayName;
|
||||
String? get displayName => _displayName;
|
||||
|
||||
|
||||
// Form State
|
||||
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
late String _billingCycle;
|
||||
@@ -35,12 +35,12 @@ class DetailScreenController extends ChangeNotifier {
|
||||
String? _selectedCategoryId;
|
||||
late String _currency;
|
||||
bool _isLoading = false;
|
||||
|
||||
|
||||
// Event State
|
||||
late bool _isEventActive;
|
||||
DateTime? _eventStartDate;
|
||||
DateTime? _eventEndDate;
|
||||
|
||||
|
||||
// Getters
|
||||
String get billingCycle => _billingCycle;
|
||||
DateTime get nextBillingDate => _nextBillingDate;
|
||||
@@ -50,7 +50,7 @@ class DetailScreenController extends ChangeNotifier {
|
||||
bool get isEventActive => _isEventActive;
|
||||
DateTime? get eventStartDate => _eventStartDate;
|
||||
DateTime? get eventEndDate => _eventEndDate;
|
||||
|
||||
|
||||
// Setters
|
||||
set billingCycle(String value) {
|
||||
if (_billingCycle != value) {
|
||||
@@ -58,21 +58,21 @@ class DetailScreenController extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
set nextBillingDate(DateTime value) {
|
||||
if (_nextBillingDate != value) {
|
||||
_nextBillingDate = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
set selectedCategoryId(String? value) {
|
||||
if (_selectedCategoryId != value) {
|
||||
_selectedCategoryId = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
set currency(String value) {
|
||||
if (_currency != value) {
|
||||
_currency = value;
|
||||
@@ -80,35 +80,35 @@ class DetailScreenController extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
set isLoading(bool value) {
|
||||
if (_isLoading != value) {
|
||||
_isLoading = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
set isEventActive(bool value) {
|
||||
if (_isEventActive != value) {
|
||||
_isEventActive = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
set eventStartDate(DateTime? value) {
|
||||
if (_eventStartDate != value) {
|
||||
_eventStartDate = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
set eventEndDate(DateTime? value) {
|
||||
if (_eventEndDate != value) {
|
||||
_eventEndDate = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Focus Nodes
|
||||
final serviceNameFocus = FocusNode();
|
||||
final monthlyCostFocus = FocusNode();
|
||||
@@ -117,7 +117,7 @@ class DetailScreenController extends ChangeNotifier {
|
||||
final websiteUrlFocus = FocusNode();
|
||||
final categoryFocus = FocusNode();
|
||||
final currencyFocus = FocusNode();
|
||||
|
||||
|
||||
// UI State
|
||||
final ScrollController scrollController = ScrollController();
|
||||
double scrollOffset = 0;
|
||||
@@ -125,7 +125,7 @@ class DetailScreenController extends ChangeNotifier {
|
||||
bool isDeleteHovered = false;
|
||||
bool isSaveHovered = false;
|
||||
bool isCancelHovered = false;
|
||||
|
||||
|
||||
// Animation Controller
|
||||
AnimationController? animationController;
|
||||
Animation<double>? fadeAnimation;
|
||||
@@ -140,42 +140,45 @@ class DetailScreenController extends ChangeNotifier {
|
||||
/// 초기화
|
||||
void initialize({required TickerProvider vsync}) {
|
||||
// Text Controllers 초기화
|
||||
serviceNameController = TextEditingController(text: subscription.serviceName);
|
||||
monthlyCostController = TextEditingController(text: subscription.monthlyCost.toString());
|
||||
websiteUrlController = TextEditingController(text: subscription.websiteUrl ?? '');
|
||||
serviceNameController =
|
||||
TextEditingController(text: subscription.serviceName);
|
||||
monthlyCostController =
|
||||
TextEditingController(text: subscription.monthlyCost.toString());
|
||||
websiteUrlController =
|
||||
TextEditingController(text: subscription.websiteUrl ?? '');
|
||||
eventPriceController = TextEditingController();
|
||||
|
||||
|
||||
// Form State 초기화
|
||||
_billingCycle = subscription.billingCycle;
|
||||
_nextBillingDate = subscription.nextBillingDate;
|
||||
_selectedCategoryId = subscription.categoryId;
|
||||
_currency = subscription.currency;
|
||||
|
||||
|
||||
// Event State 초기화
|
||||
_isEventActive = subscription.isEventActive;
|
||||
_eventStartDate = subscription.eventStartDate;
|
||||
_eventEndDate = subscription.eventEndDate;
|
||||
|
||||
|
||||
// 이벤트 가격 초기화
|
||||
if (subscription.eventPrice != null) {
|
||||
if (currency == 'KRW') {
|
||||
eventPriceController.text = NumberFormat.decimalPattern()
|
||||
.format(subscription.eventPrice!.toInt());
|
||||
} else {
|
||||
eventPriceController.text =
|
||||
eventPriceController.text =
|
||||
NumberFormat('#,##0.00').format(subscription.eventPrice!);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 통화 단위에 따른 금액 표시 형식 조정
|
||||
_updateMonthlyCostFormat();
|
||||
|
||||
|
||||
// 애니메이션 초기화
|
||||
animationController = AnimationController(
|
||||
duration: const Duration(milliseconds: 800),
|
||||
vsync: vsync,
|
||||
);
|
||||
|
||||
|
||||
fadeAnimation = Tween<double>(
|
||||
begin: 0.0,
|
||||
end: 1.0,
|
||||
@@ -183,7 +186,7 @@ class DetailScreenController extends ChangeNotifier {
|
||||
parent: animationController!,
|
||||
curve: Curves.easeInOut,
|
||||
));
|
||||
|
||||
|
||||
slideAnimation = Tween<Offset>(
|
||||
begin: const Offset(0.0, 0.3),
|
||||
end: Offset.zero,
|
||||
@@ -191,7 +194,7 @@ class DetailScreenController extends ChangeNotifier {
|
||||
parent: animationController!,
|
||||
curve: Curves.easeOutCubic,
|
||||
));
|
||||
|
||||
|
||||
rotateAnimation = Tween<double>(
|
||||
begin: 0.0,
|
||||
end: 1.0,
|
||||
@@ -199,16 +202,16 @@ class DetailScreenController extends ChangeNotifier {
|
||||
parent: animationController!,
|
||||
curve: Curves.easeOutCubic,
|
||||
));
|
||||
|
||||
|
||||
// 애니메이션 시작
|
||||
animationController!.forward();
|
||||
|
||||
|
||||
// 로케일에 맞는 서비스명 로드
|
||||
_loadDisplayName();
|
||||
|
||||
|
||||
// 서비스명 변경 감지 리스너
|
||||
serviceNameController.addListener(onServiceNameChanged);
|
||||
|
||||
|
||||
// 스크롤 리스너
|
||||
scrollController.addListener(() {
|
||||
scrollOffset = scrollController.offset;
|
||||
@@ -219,16 +222,16 @@ class DetailScreenController extends ChangeNotifier {
|
||||
Future<void> _loadDisplayName() async {
|
||||
final localeProvider = context.read<LocaleProvider>();
|
||||
final locale = localeProvider.locale.languageCode;
|
||||
|
||||
|
||||
final displayName = await SubscriptionUrlMatcher.getServiceDisplayName(
|
||||
serviceName: subscription.serviceName,
|
||||
locale: locale,
|
||||
);
|
||||
|
||||
|
||||
_displayName = displayName;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
|
||||
/// 리소스 정리
|
||||
@override
|
||||
void dispose() {
|
||||
@@ -237,7 +240,7 @@ class DetailScreenController extends ChangeNotifier {
|
||||
monthlyCostController.dispose();
|
||||
websiteUrlController.dispose();
|
||||
eventPriceController.dispose();
|
||||
|
||||
|
||||
// Focus Nodes
|
||||
serviceNameFocus.dispose();
|
||||
monthlyCostFocus.dispose();
|
||||
@@ -246,13 +249,13 @@ class DetailScreenController extends ChangeNotifier {
|
||||
websiteUrlFocus.dispose();
|
||||
categoryFocus.dispose();
|
||||
currencyFocus.dispose();
|
||||
|
||||
|
||||
// Animation
|
||||
animationController?.dispose();
|
||||
|
||||
|
||||
// Scroll
|
||||
scrollController.dispose();
|
||||
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -261,10 +264,12 @@ class DetailScreenController extends ChangeNotifier {
|
||||
if (_currency == 'KRW') {
|
||||
// 원화는 소수점 없이 표시
|
||||
final intValue = subscription.monthlyCost.toInt();
|
||||
monthlyCostController.text = NumberFormat.decimalPattern().format(intValue);
|
||||
monthlyCostController.text =
|
||||
NumberFormat.decimalPattern().format(intValue);
|
||||
} else {
|
||||
// 달러는 소수점 2자리까지 표시
|
||||
monthlyCostController.text = NumberFormat('#,##0.00').format(subscription.monthlyCost);
|
||||
monthlyCostController.text =
|
||||
NumberFormat('#,##0.00').format(subscription.monthlyCost);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,17 +280,18 @@ class DetailScreenController extends ChangeNotifier {
|
||||
|
||||
/// 카테고리 자동 선택
|
||||
void autoSelectCategory() {
|
||||
final categoryProvider = Provider.of<CategoryProvider>(context, listen: false);
|
||||
final categoryProvider =
|
||||
Provider.of<CategoryProvider>(context, listen: false);
|
||||
final categories = categoryProvider.categories;
|
||||
|
||||
|
||||
final serviceName = serviceNameController.text.toLowerCase();
|
||||
|
||||
|
||||
// 서비스명에 기반한 카테고리 매칭 로직
|
||||
CategoryModel? matchedCategory;
|
||||
|
||||
|
||||
// 엔터테인먼트 관련 키워드
|
||||
if (serviceName.contains('netflix') ||
|
||||
serviceName.contains('youtube') ||
|
||||
if (serviceName.contains('netflix') ||
|
||||
serviceName.contains('youtube') ||
|
||||
serviceName.contains('disney') ||
|
||||
serviceName.contains('왓챠') ||
|
||||
serviceName.contains('티빙') ||
|
||||
@@ -298,64 +304,64 @@ class DetailScreenController extends ChangeNotifier {
|
||||
);
|
||||
}
|
||||
// 음악 관련 키워드
|
||||
else if (serviceName.contains('spotify') ||
|
||||
serviceName.contains('apple music') ||
|
||||
serviceName.contains('멜론') ||
|
||||
serviceName.contains('지니') ||
|
||||
serviceName.contains('플로') ||
|
||||
serviceName.contains('벅스')) {
|
||||
else if (serviceName.contains('spotify') ||
|
||||
serviceName.contains('apple music') ||
|
||||
serviceName.contains('멜론') ||
|
||||
serviceName.contains('지니') ||
|
||||
serviceName.contains('플로') ||
|
||||
serviceName.contains('벅스')) {
|
||||
matchedCategory = categories.firstWhere(
|
||||
(cat) => cat.name == 'music',
|
||||
orElse: () => categories.first,
|
||||
);
|
||||
}
|
||||
// 생산성 관련 키워드
|
||||
else if (serviceName.contains('notion') ||
|
||||
serviceName.contains('microsoft') ||
|
||||
serviceName.contains('office') ||
|
||||
serviceName.contains('google') ||
|
||||
serviceName.contains('dropbox') ||
|
||||
serviceName.contains('icloud') ||
|
||||
serviceName.contains('adobe')) {
|
||||
else if (serviceName.contains('notion') ||
|
||||
serviceName.contains('microsoft') ||
|
||||
serviceName.contains('office') ||
|
||||
serviceName.contains('google') ||
|
||||
serviceName.contains('dropbox') ||
|
||||
serviceName.contains('icloud') ||
|
||||
serviceName.contains('adobe')) {
|
||||
matchedCategory = categories.firstWhere(
|
||||
(cat) => cat.name == 'collaborationOffice',
|
||||
orElse: () => categories.first,
|
||||
);
|
||||
}
|
||||
// AI 관련 키워드
|
||||
else if (serviceName.contains('chatgpt') ||
|
||||
serviceName.contains('claude') ||
|
||||
serviceName.contains('gemini') ||
|
||||
serviceName.contains('copilot') ||
|
||||
serviceName.contains('midjourney')) {
|
||||
else if (serviceName.contains('chatgpt') ||
|
||||
serviceName.contains('claude') ||
|
||||
serviceName.contains('gemini') ||
|
||||
serviceName.contains('copilot') ||
|
||||
serviceName.contains('midjourney')) {
|
||||
matchedCategory = categories.firstWhere(
|
||||
(cat) => cat.name == 'aiService',
|
||||
orElse: () => categories.first,
|
||||
);
|
||||
}
|
||||
// 교육 관련 키워드
|
||||
else if (serviceName.contains('coursera') ||
|
||||
serviceName.contains('udemy') ||
|
||||
serviceName.contains('인프런') ||
|
||||
serviceName.contains('패스트캠퍼스') ||
|
||||
serviceName.contains('클래스101')) {
|
||||
else if (serviceName.contains('coursera') ||
|
||||
serviceName.contains('udemy') ||
|
||||
serviceName.contains('인프런') ||
|
||||
serviceName.contains('패스트캠퍼스') ||
|
||||
serviceName.contains('클래스101')) {
|
||||
matchedCategory = categories.firstWhere(
|
||||
(cat) => cat.name == 'programming',
|
||||
orElse: () => categories.first,
|
||||
);
|
||||
}
|
||||
// 쇼핑 관련 키워드
|
||||
else if (serviceName.contains('쿠팡') ||
|
||||
serviceName.contains('coupang') ||
|
||||
serviceName.contains('amazon') ||
|
||||
serviceName.contains('네이버') ||
|
||||
serviceName.contains('11번가')) {
|
||||
else if (serviceName.contains('쿠팡') ||
|
||||
serviceName.contains('coupang') ||
|
||||
serviceName.contains('amazon') ||
|
||||
serviceName.contains('네이버') ||
|
||||
serviceName.contains('11번가')) {
|
||||
matchedCategory = categories.firstWhere(
|
||||
(cat) => cat.name == 'other',
|
||||
orElse: () => categories.first,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (matchedCategory != null) {
|
||||
selectedCategoryId = matchedCategory.id;
|
||||
}
|
||||
@@ -371,30 +377,32 @@ class DetailScreenController extends ChangeNotifier {
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
final provider = Provider.of<SubscriptionProvider>(context, listen: false);
|
||||
|
||||
|
||||
// 웹사이트 URL이 비어있는 경우 자동 매칭 다시 시도
|
||||
String? websiteUrl = websiteUrlController.text;
|
||||
if (websiteUrl.isEmpty) {
|
||||
websiteUrl = SubscriptionUrlMatcher.suggestUrl(serviceNameController.text);
|
||||
websiteUrl =
|
||||
SubscriptionUrlMatcher.suggestUrl(serviceNameController.text);
|
||||
}
|
||||
|
||||
|
||||
// 구독 정보 업데이트
|
||||
|
||||
|
||||
// 콤마 제거하고 숫자만 추출
|
||||
double monthlyCost = 0.0;
|
||||
try {
|
||||
monthlyCost = double.parse(monthlyCostController.text.replaceAll(',', ''));
|
||||
monthlyCost =
|
||||
double.parse(monthlyCostController.text.replaceAll(',', ''));
|
||||
} catch (e) {
|
||||
// 파싱 오류 발생 시 기본값 사용
|
||||
monthlyCost = subscription.monthlyCost;
|
||||
}
|
||||
|
||||
|
||||
debugPrint('[DetailScreenController] 구독 업데이트 시작: '
|
||||
'${subscription.serviceName} → ${serviceNameController.text}, '
|
||||
'금액: ${subscription.monthlyCost} → $monthlyCost ${_currency}');
|
||||
|
||||
|
||||
subscription.serviceName = serviceNameController.text;
|
||||
subscription.monthlyCost = monthlyCost;
|
||||
subscription.websiteUrl = websiteUrl;
|
||||
@@ -402,16 +410,16 @@ class DetailScreenController extends ChangeNotifier {
|
||||
subscription.nextBillingDate = _nextBillingDate;
|
||||
subscription.categoryId = _selectedCategoryId;
|
||||
subscription.currency = _currency;
|
||||
|
||||
|
||||
// 이벤트 정보 업데이트
|
||||
subscription.isEventActive = _isEventActive;
|
||||
subscription.eventStartDate = _eventStartDate;
|
||||
subscription.eventEndDate = _eventEndDate;
|
||||
|
||||
|
||||
// 이벤트 가격 파싱
|
||||
if (_isEventActive && eventPriceController.text.isNotEmpty) {
|
||||
try {
|
||||
subscription.eventPrice =
|
||||
subscription.eventPrice =
|
||||
double.parse(eventPriceController.text.replaceAll(',', ''));
|
||||
} catch (e) {
|
||||
subscription.eventPrice = null;
|
||||
@@ -419,20 +427,20 @@ class DetailScreenController extends ChangeNotifier {
|
||||
} else {
|
||||
subscription.eventPrice = null;
|
||||
}
|
||||
|
||||
|
||||
debugPrint('[DetailScreenController] 업데이트 정보: '
|
||||
'현재가격=${subscription.currentPrice}, '
|
||||
'이벤트활성=${subscription.isEventActive}');
|
||||
|
||||
|
||||
// 구독 업데이트
|
||||
await provider.updateSubscription(subscription);
|
||||
|
||||
|
||||
if (context.mounted) {
|
||||
AppSnackBar.showSuccess(
|
||||
context: context,
|
||||
message: AppLocalizations.of(context).subscriptionUpdated,
|
||||
);
|
||||
|
||||
|
||||
// 변경 사항이 반영될 시간을 주기 위해 짧게 지연 후 결과 반환
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
if (context.mounted) {
|
||||
@@ -445,30 +453,33 @@ class DetailScreenController extends ChangeNotifier {
|
||||
Future<void> deleteSubscription() async {
|
||||
if (context.mounted) {
|
||||
// 로케일에 맞는 서비스명 가져오기
|
||||
final localeProvider = Provider.of<LocaleProvider>(context, listen: false);
|
||||
final localeProvider =
|
||||
Provider.of<LocaleProvider>(context, listen: false);
|
||||
final locale = localeProvider.locale.languageCode;
|
||||
final displayName = await SubscriptionUrlMatcher.getServiceDisplayName(
|
||||
serviceName: subscription.serviceName,
|
||||
locale: locale,
|
||||
);
|
||||
|
||||
|
||||
// 삭제 확인 다이얼로그 표시
|
||||
final shouldDelete = await DeleteConfirmationDialog.show(
|
||||
context: context,
|
||||
serviceName: displayName,
|
||||
);
|
||||
|
||||
|
||||
if (!shouldDelete) return;
|
||||
|
||||
|
||||
// 사용자가 확인한 경우에만 삭제 진행
|
||||
if (context.mounted) {
|
||||
final provider = Provider.of<SubscriptionProvider>(context, listen: false);
|
||||
final provider =
|
||||
Provider.of<SubscriptionProvider>(context, listen: false);
|
||||
await provider.deleteSubscription(subscription.id);
|
||||
|
||||
|
||||
if (context.mounted) {
|
||||
AppSnackBar.showError(
|
||||
context: context,
|
||||
message: AppLocalizations.of(context).subscriptionDeleted(displayName),
|
||||
message:
|
||||
AppLocalizations.of(context).subscriptionDeleted(displayName),
|
||||
icon: Icons.delete_forever_rounded,
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
@@ -482,19 +493,22 @@ class DetailScreenController extends ChangeNotifier {
|
||||
try {
|
||||
// 1. 현재 언어 설정 가져오기
|
||||
final locale = Localizations.localeOf(context).languageCode;
|
||||
|
||||
|
||||
// 2. 해지 안내 URL 찾기
|
||||
String? cancellationUrl = await SubscriptionUrlMatcher.findCancellationUrl(
|
||||
String? cancellationUrl =
|
||||
await SubscriptionUrlMatcher.findCancellationUrl(
|
||||
serviceName: subscription.serviceName,
|
||||
websiteUrl: subscription.websiteUrl,
|
||||
locale: locale == 'ko' ? 'kr' : 'en',
|
||||
);
|
||||
|
||||
|
||||
// 3. 해지 안내 URL이 없으면 구글 검색
|
||||
if (cancellationUrl == null) {
|
||||
final searchQuery = '${subscription.serviceName} ${locale == 'ko' ? '해지 방법' : 'cancel subscription'}';
|
||||
cancellationUrl = 'https://www.google.com/search?q=${Uri.encodeComponent(searchQuery)}';
|
||||
|
||||
final searchQuery =
|
||||
'${subscription.serviceName} ${locale == 'ko' ? '해지 방법' : 'cancel subscription'}';
|
||||
cancellationUrl =
|
||||
'https://www.google.com/search?q=${Uri.encodeComponent(searchQuery)}';
|
||||
|
||||
if (context.mounted) {
|
||||
AppSnackBar.showInfo(
|
||||
context: context,
|
||||
@@ -502,7 +516,7 @@ class DetailScreenController extends ChangeNotifier {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 4. URL 열기
|
||||
final Uri url = Uri.parse(cancellationUrl);
|
||||
if (!await launchUrl(url, mode: LaunchMode.externalApplication)) {
|
||||
@@ -517,9 +531,9 @@ class DetailScreenController extends ChangeNotifier {
|
||||
if (kDebugMode) {
|
||||
print('DetailScreenController: 해지 페이지 열기 실패 - $e');
|
||||
}
|
||||
|
||||
|
||||
// 오류 발생시 일반 웹사이트로 폴백
|
||||
if (subscription.websiteUrl != null &&
|
||||
if (subscription.websiteUrl != null &&
|
||||
subscription.websiteUrl!.isNotEmpty) {
|
||||
final Uri url = Uri.parse(subscription.websiteUrl!);
|
||||
if (!await launchUrl(url, mode: LaunchMode.externalApplication)) {
|
||||
@@ -554,7 +568,7 @@ class DetailScreenController extends ChangeNotifier {
|
||||
const Color(0xFF0EA5E9), // 하늘
|
||||
const Color(0xFFEC4899), // 분홍
|
||||
];
|
||||
|
||||
|
||||
return colors[hash % colors.length];
|
||||
}
|
||||
|
||||
@@ -569,4 +583,4 @@ class DetailScreenController extends ChangeNotifier {
|
||||
end: Alignment.bottomRight,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,11 +59,13 @@ class SmsScanController extends ChangeNotifier {
|
||||
try {
|
||||
// SMS 스캔 실행
|
||||
print('SMS 스캔 시작');
|
||||
final scannedSubscriptionModels = await _smsScanner.scanForSubscriptions();
|
||||
final scannedSubscriptionModels =
|
||||
await _smsScanner.scanForSubscriptions();
|
||||
print('스캔된 구독: ${scannedSubscriptionModels.length}개');
|
||||
|
||||
if (scannedSubscriptionModels.isNotEmpty) {
|
||||
print('첫 번째 구독: ${scannedSubscriptionModels[0].serviceName}, 반복 횟수: ${scannedSubscriptionModels[0].repeatCount}');
|
||||
print(
|
||||
'첫 번째 구독: ${scannedSubscriptionModels[0].serviceName}, 반복 횟수: ${scannedSubscriptionModels[0].repeatCount}');
|
||||
}
|
||||
|
||||
if (!context.mounted) return;
|
||||
@@ -77,14 +79,17 @@ class SmsScanController extends ChangeNotifier {
|
||||
}
|
||||
|
||||
// SubscriptionModel을 Subscription으로 변환
|
||||
final scannedSubscriptions = _converter.convertModelsToSubscriptions(scannedSubscriptionModels);
|
||||
final scannedSubscriptions =
|
||||
_converter.convertModelsToSubscriptions(scannedSubscriptionModels);
|
||||
|
||||
// 2회 이상 반복 결제된 구독만 필터링
|
||||
final repeatSubscriptions = _filter.filterByRepeatCount(scannedSubscriptions, 2);
|
||||
final repeatSubscriptions =
|
||||
_filter.filterByRepeatCount(scannedSubscriptions, 2);
|
||||
print('반복 결제된 구독: ${repeatSubscriptions.length}개');
|
||||
|
||||
if (repeatSubscriptions.isNotEmpty) {
|
||||
print('첫 번째 반복 구독: ${repeatSubscriptions[0].serviceName}, 반복 횟수: ${repeatSubscriptions[0].repeatCount}');
|
||||
print(
|
||||
'첫 번째 반복 구독: ${repeatSubscriptions[0].serviceName}, 반복 횟수: ${repeatSubscriptions[0].repeatCount}');
|
||||
}
|
||||
|
||||
if (repeatSubscriptions.isEmpty) {
|
||||
@@ -96,16 +101,19 @@ class SmsScanController extends ChangeNotifier {
|
||||
}
|
||||
|
||||
// 구독 목록 가져오기
|
||||
final provider = Provider.of<SubscriptionProvider>(context, listen: false);
|
||||
final provider =
|
||||
Provider.of<SubscriptionProvider>(context, listen: false);
|
||||
final existingSubscriptions = provider.subscriptions;
|
||||
print('기존 구독: ${existingSubscriptions.length}개');
|
||||
|
||||
// 중복 구독 필터링
|
||||
final filteredSubscriptions = _filter.filterDuplicates(repeatSubscriptions, existingSubscriptions);
|
||||
final filteredSubscriptions =
|
||||
_filter.filterDuplicates(repeatSubscriptions, existingSubscriptions);
|
||||
print('중복 제거 후 구독: ${filteredSubscriptions.length}개');
|
||||
|
||||
if (filteredSubscriptions.isNotEmpty) {
|
||||
print('첫 번째 필터링된 구독: ${filteredSubscriptions[0].serviceName}, 반복 횟수: ${filteredSubscriptions[0].repeatCount}');
|
||||
print(
|
||||
'첫 번째 필터링된 구독: ${filteredSubscriptions[0].serviceName}, 반복 횟수: ${filteredSubscriptions[0].repeatCount}');
|
||||
}
|
||||
|
||||
// 중복 제거 후 신규 구독이 없는 경우
|
||||
@@ -123,7 +131,8 @@ class SmsScanController extends ChangeNotifier {
|
||||
} catch (e) {
|
||||
print('SMS 스캔 중 오류 발생: $e');
|
||||
if (context.mounted) {
|
||||
_errorMessage = AppLocalizations.of(context).smsScanErrorWithMessage(e.toString());
|
||||
_errorMessage =
|
||||
AppLocalizations.of(context).smsScanErrorWithMessage(e.toString());
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
@@ -134,20 +143,25 @@ class SmsScanController extends ChangeNotifier {
|
||||
if (_currentIndex >= _scannedSubscriptions.length) return;
|
||||
|
||||
final subscription = _scannedSubscriptions[_currentIndex];
|
||||
|
||||
|
||||
try {
|
||||
final provider = Provider.of<SubscriptionProvider>(context, listen: false);
|
||||
final categoryProvider = Provider.of<CategoryProvider>(context, listen: false);
|
||||
|
||||
final finalCategoryId = _selectedCategoryId ?? subscription.category ?? getDefaultCategoryId(categoryProvider);
|
||||
|
||||
final provider =
|
||||
Provider.of<SubscriptionProvider>(context, listen: false);
|
||||
final categoryProvider =
|
||||
Provider.of<CategoryProvider>(context, listen: false);
|
||||
|
||||
final finalCategoryId = _selectedCategoryId ??
|
||||
subscription.category ??
|
||||
getDefaultCategoryId(categoryProvider);
|
||||
|
||||
// websiteUrl 처리
|
||||
final websiteUrl = websiteUrlController.text.trim().isNotEmpty
|
||||
? websiteUrlController.text.trim()
|
||||
final websiteUrl = websiteUrlController.text.trim().isNotEmpty
|
||||
? websiteUrlController.text.trim()
|
||||
: subscription.websiteUrl;
|
||||
|
||||
print('구독 추가 시도: ${subscription.serviceName}, 카테고리: $finalCategoryId, URL: $websiteUrl');
|
||||
|
||||
print(
|
||||
'구독 추가 시도: ${subscription.serviceName}, 카테고리: $finalCategoryId, URL: $websiteUrl');
|
||||
|
||||
// addSubscription 호출
|
||||
await provider.addSubscription(
|
||||
serviceName: subscription.serviceName,
|
||||
@@ -161,9 +175,9 @@ class SmsScanController extends ChangeNotifier {
|
||||
categoryId: finalCategoryId,
|
||||
currency: subscription.currency,
|
||||
);
|
||||
|
||||
|
||||
print('구독 추가 성공: ${subscription.serviceName}');
|
||||
|
||||
|
||||
moveToNextSubscription(context);
|
||||
} catch (e) {
|
||||
print('구독 추가 중 오류 발생: $e');
|
||||
@@ -187,13 +201,14 @@ class SmsScanController extends ChangeNotifier {
|
||||
if (_currentIndex >= _scannedSubscriptions.length) {
|
||||
navigateToHome(context);
|
||||
}
|
||||
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void navigateToHome(BuildContext context) {
|
||||
// NavigationProvider를 사용하여 홈 화면으로 이동
|
||||
final navigationProvider = Provider.of<NavigationProvider>(context, listen: false);
|
||||
final navigationProvider =
|
||||
Provider.of<NavigationProvider>(context, listen: false);
|
||||
navigationProvider.updateCurrentIndex(0);
|
||||
}
|
||||
|
||||
@@ -221,4 +236,4 @@ class SmsScanController extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user