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:
@@ -1,6 +1,5 @@
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
/// 환율 정보 서비스 클래스
|
||||
|
||||
@@ -152,13 +152,6 @@ class NotificationService {
|
||||
}
|
||||
}
|
||||
|
||||
// 알림 서비스가 초기화되었는지 확인하는 메서드
|
||||
static bool _isInitialized() {
|
||||
// 웹 플랫폼인 경우 항상 false 반환
|
||||
if (_isWeb) return false;
|
||||
// 초기화 플래그 확인
|
||||
return _initialized;
|
||||
}
|
||||
|
||||
static Future<bool> requestPermission() async {
|
||||
final result = await _notifications
|
||||
@@ -182,7 +175,7 @@ class NotificationService {
|
||||
}
|
||||
|
||||
try {
|
||||
final androidDetails = AndroidNotificationDetails(
|
||||
const androidDetails = AndroidNotificationDetails(
|
||||
'subscription_channel',
|
||||
'구독 알림',
|
||||
channelDescription: '구독 관련 알림을 보여줍니다.',
|
||||
@@ -257,7 +250,7 @@ class NotificationService {
|
||||
try {
|
||||
final notificationId = subscription.id.hashCode;
|
||||
|
||||
final androidDetails = AndroidNotificationDetails(
|
||||
const androidDetails = AndroidNotificationDetails(
|
||||
'subscription_channel',
|
||||
'구독 알림',
|
||||
channelDescription: '구독 만료 알림을 보내는 채널입니다.',
|
||||
@@ -265,13 +258,13 @@ class NotificationService {
|
||||
priority: Priority.high,
|
||||
);
|
||||
|
||||
final iosDetails = DarwinNotificationDetails(
|
||||
const iosDetails = DarwinNotificationDetails(
|
||||
presentAlert: true,
|
||||
presentBadge: true,
|
||||
presentSound: true,
|
||||
);
|
||||
|
||||
final notificationDetails = NotificationDetails(
|
||||
const notificationDetails = NotificationDetails(
|
||||
android: androidDetails,
|
||||
iOS: iosDetails,
|
||||
);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// 구독 서비스와 웹사이트 URL 매칭을 처리하는 서비스 클래스
|
||||
class SubscriptionUrlMatcher {
|
||||
|
||||
Reference in New Issue
Block a user