Refactor screens to MVC architecture with modular widgets

- Extract business logic from screens into dedicated controllers
- Split large screen files into smaller, reusable widget components
- Add controllers for AddSubscriptionScreen and DetailScreen
- Create modular widgets for subscription and detail features
- Improve code organization and maintainability
- Remove duplicated code and improve reusability

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
JiWoong Sul
2025-07-11 00:21:18 +09:00
parent 4731288622
commit 83c5e3d64e
56 changed files with 9092 additions and 4579 deletions

View File

@@ -80,7 +80,6 @@ class SmsScanner {
final nextBillingDateStr = sms['nextBillingDate'] as String?;
// 실제 반복 횟수 사용 (테스트 데이터에서는 이미 제공됨)
final actualRepeatCount = repeatCount > 0 ? repeatCount : 1;
final isRecurring = (sms['isRecurring'] as bool?) ?? (repeatCount >= 2);
final message = sms['message'] as String? ?? '';
// 통화 단위 감지 - 메시지 내용과 서비스명 모두 검사
@@ -205,79 +204,7 @@ class SmsScanner {
return serviceUrls[serviceName];
}
bool _containsSubscriptionKeywords(String text) {
final keywords = [
'구독',
'결제',
'청구',
'정기',
'자동',
'subscription',
'payment',
'bill',
'invoice'
];
return keywords
.any((keyword) => text.toLowerCase().contains(keyword.toLowerCase()));
}
double? _extractAmount(String text) {
final RegExp amountRegex = RegExp(r'(\d{1,3}(?:,\d{3})*(?:\.\d{2})?)');
final match = amountRegex.firstMatch(text);
if (match != null) {
final amountStr = match.group(1)?.replaceAll(',', '');
return double.tryParse(amountStr ?? '');
}
return null;
}
String? _extractServiceName(String text) {
final serviceNames = [
'Netflix',
'Spotify',
'Disney+',
'Apple Music',
'YouTube Premium',
'Amazon Prime',
'Microsoft 365',
'Google One',
'iCloud',
'Dropbox'
];
for (final name in serviceNames) {
if (text.contains(name)) {
return name;
}
}
return null;
}
String _extractBillingCycle(String text) {
if (text.contains('') || text.contains('month')) {
return 'monthly';
} else if (text.contains('') || text.contains('year')) {
return 'yearly';
} else if (text.contains('') || text.contains('week')) {
return 'weekly';
}
return 'monthly'; // 기본값
}
DateTime _extractNextBillingDate(String text) {
final RegExp dateRegex = RegExp(r'(\d{4}[-/]\d{2}[-/]\d{2})');
final match = dateRegex.firstMatch(text);
if (match != null) {
final dateStr = match.group(1);
if (dateStr != null) {
final date = DateTime.tryParse(dateStr);
if (date != null) {
return date;
}
}
}
return DateTime.now().add(const Duration(days: 30)); // 기본값: 30일 후
}
// 메시지에서 통화 단위를 감지하는 함수
String _detectCurrency(String message) {