LunchPick(오늘 뭐 먹Z?) Flutter 앱의 초기 구현입니다. 주요 기능: - 네이버 지도 연동 맛집 추가 - 랜덤 메뉴 추천 시스템 - 날씨 기반 거리 조정 - 방문 기록 관리 - Bluetooth 맛집 공유 - 다크모드 지원 기술 스택: - Flutter 3.8.1+ - Riverpod 상태 관리 - Hive 로컬 DB - Clean Architecture 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
204 lines
6.9 KiB
Dart
204 lines
6.9 KiB
Dart
import 'package:hive_flutter/hive_flutter.dart';
|
|
import 'package:lunchpick/domain/repositories/settings_repository.dart';
|
|
import 'package:lunchpick/domain/entities/user_settings.dart';
|
|
|
|
class SettingsRepositoryImpl implements SettingsRepository {
|
|
static const String _boxName = 'settings';
|
|
|
|
// Setting keys
|
|
static const String _keyDaysToExclude = 'days_to_exclude';
|
|
static const String _keyMaxDistanceRainy = 'max_distance_rainy';
|
|
static const String _keyMaxDistanceNormal = 'max_distance_normal';
|
|
static const String _keyNotificationDelayMinutes = 'notification_delay_minutes';
|
|
static const String _keyNotificationEnabled = 'notification_enabled';
|
|
static const String _keyDarkModeEnabled = 'dark_mode_enabled';
|
|
static const String _keyFirstRun = 'first_run';
|
|
static const String _keyCategoryWeights = 'category_weights';
|
|
|
|
// Default values
|
|
static const int _defaultDaysToExclude = 7;
|
|
static const int _defaultMaxDistanceRainy = 500;
|
|
static const int _defaultMaxDistanceNormal = 1000;
|
|
static const int _defaultNotificationDelayMinutes = 90;
|
|
static const bool _defaultNotificationEnabled = true;
|
|
static const bool _defaultDarkModeEnabled = false;
|
|
static const bool _defaultFirstRun = true;
|
|
|
|
Future<Box> get _box async => await Hive.openBox(_boxName);
|
|
|
|
@override
|
|
Future<UserSettings> getUserSettings() async {
|
|
final box = await _box;
|
|
|
|
// 저장된 설정값들을 읽어옴
|
|
final revisitPreventionDays = box.get(_keyDaysToExclude, defaultValue: _defaultDaysToExclude);
|
|
final notificationEnabled = box.get(_keyNotificationEnabled, defaultValue: _defaultNotificationEnabled);
|
|
final notificationDelayMinutes = box.get(_keyNotificationDelayMinutes, defaultValue: _defaultNotificationDelayMinutes);
|
|
|
|
// 카테고리 가중치 읽기 (Map<String, double>으로 저장됨)
|
|
final categoryWeightsData = box.get(_keyCategoryWeights);
|
|
Map<String, double> categoryWeights = {};
|
|
if (categoryWeightsData != null) {
|
|
categoryWeights = Map<String, double>.from(categoryWeightsData);
|
|
}
|
|
|
|
// 알림 시간은 분을 시간:분 형식으로 변환
|
|
final hours = notificationDelayMinutes ~/ 60;
|
|
final minutes = notificationDelayMinutes % 60;
|
|
final notificationTime = '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}';
|
|
|
|
return UserSettings(
|
|
revisitPreventionDays: revisitPreventionDays,
|
|
notificationEnabled: notificationEnabled,
|
|
notificationTime: notificationTime,
|
|
categoryWeights: categoryWeights,
|
|
notificationDelayMinutes: notificationDelayMinutes,
|
|
);
|
|
}
|
|
|
|
@override
|
|
Future<void> updateUserSettings(UserSettings settings) async {
|
|
final box = await _box;
|
|
|
|
// 각 설정값 저장
|
|
await box.put(_keyDaysToExclude, settings.revisitPreventionDays);
|
|
await box.put(_keyNotificationEnabled, settings.notificationEnabled);
|
|
await box.put(_keyNotificationDelayMinutes, settings.notificationDelayMinutes);
|
|
|
|
// 카테고리 가중치 저장
|
|
await box.put(_keyCategoryWeights, settings.categoryWeights);
|
|
}
|
|
|
|
@override
|
|
Future<int> getDaysToExclude() async {
|
|
final box = await _box;
|
|
return box.get(_keyDaysToExclude, defaultValue: _defaultDaysToExclude);
|
|
}
|
|
|
|
@override
|
|
Future<void> setDaysToExclude(int days) async {
|
|
final box = await _box;
|
|
await box.put(_keyDaysToExclude, days);
|
|
}
|
|
|
|
@override
|
|
Future<int> getMaxDistanceRainy() async {
|
|
final box = await _box;
|
|
return box.get(_keyMaxDistanceRainy, defaultValue: _defaultMaxDistanceRainy);
|
|
}
|
|
|
|
@override
|
|
Future<void> setMaxDistanceRainy(int meters) async {
|
|
final box = await _box;
|
|
await box.put(_keyMaxDistanceRainy, meters);
|
|
}
|
|
|
|
@override
|
|
Future<int> getMaxDistanceNormal() async {
|
|
final box = await _box;
|
|
return box.get(_keyMaxDistanceNormal, defaultValue: _defaultMaxDistanceNormal);
|
|
}
|
|
|
|
@override
|
|
Future<void> setMaxDistanceNormal(int meters) async {
|
|
final box = await _box;
|
|
await box.put(_keyMaxDistanceNormal, meters);
|
|
}
|
|
|
|
@override
|
|
Future<int> getNotificationDelayMinutes() async {
|
|
final box = await _box;
|
|
return box.get(_keyNotificationDelayMinutes, defaultValue: _defaultNotificationDelayMinutes);
|
|
}
|
|
|
|
@override
|
|
Future<void> setNotificationDelayMinutes(int minutes) async {
|
|
final box = await _box;
|
|
await box.put(_keyNotificationDelayMinutes, minutes);
|
|
}
|
|
|
|
@override
|
|
Future<bool> isNotificationEnabled() async {
|
|
final box = await _box;
|
|
return box.get(_keyNotificationEnabled, defaultValue: _defaultNotificationEnabled);
|
|
}
|
|
|
|
@override
|
|
Future<void> setNotificationEnabled(bool enabled) async {
|
|
final box = await _box;
|
|
await box.put(_keyNotificationEnabled, enabled);
|
|
}
|
|
|
|
@override
|
|
Future<bool> isDarkModeEnabled() async {
|
|
final box = await _box;
|
|
return box.get(_keyDarkModeEnabled, defaultValue: _defaultDarkModeEnabled);
|
|
}
|
|
|
|
@override
|
|
Future<void> setDarkModeEnabled(bool enabled) async {
|
|
final box = await _box;
|
|
await box.put(_keyDarkModeEnabled, enabled);
|
|
}
|
|
|
|
@override
|
|
Future<bool> isFirstRun() async {
|
|
final box = await _box;
|
|
return box.get(_keyFirstRun, defaultValue: _defaultFirstRun);
|
|
}
|
|
|
|
@override
|
|
Future<void> setFirstRun(bool isFirst) async {
|
|
final box = await _box;
|
|
await box.put(_keyFirstRun, isFirst);
|
|
}
|
|
|
|
@override
|
|
Future<void> resetSettings() async {
|
|
final box = await _box;
|
|
await box.clear();
|
|
|
|
// 기본값으로 재설정
|
|
await box.put(_keyDaysToExclude, _defaultDaysToExclude);
|
|
await box.put(_keyMaxDistanceRainy, _defaultMaxDistanceRainy);
|
|
await box.put(_keyMaxDistanceNormal, _defaultMaxDistanceNormal);
|
|
await box.put(_keyNotificationDelayMinutes, _defaultNotificationDelayMinutes);
|
|
await box.put(_keyNotificationEnabled, _defaultNotificationEnabled);
|
|
await box.put(_keyDarkModeEnabled, _defaultDarkModeEnabled);
|
|
await box.put(_keyFirstRun, false); // 리셋 후에는 첫 실행이 아님
|
|
}
|
|
|
|
@override
|
|
Stream<Map<String, dynamic>> watchSettings() async* {
|
|
final box = await _box;
|
|
|
|
// 초기 값 전송
|
|
yield await _getCurrentSettings();
|
|
|
|
// 변경사항 감시
|
|
yield* box.watch().asyncMap((_) async => await _getCurrentSettings());
|
|
}
|
|
|
|
Future<Map<String, dynamic>> _getCurrentSettings() async {
|
|
return {
|
|
_keyDaysToExclude: await getDaysToExclude(),
|
|
_keyMaxDistanceRainy: await getMaxDistanceRainy(),
|
|
_keyMaxDistanceNormal: await getMaxDistanceNormal(),
|
|
_keyNotificationDelayMinutes: await getNotificationDelayMinutes(),
|
|
_keyNotificationEnabled: await isNotificationEnabled(),
|
|
_keyDarkModeEnabled: await isDarkModeEnabled(),
|
|
_keyFirstRun: await isFirstRun(),
|
|
};
|
|
}
|
|
|
|
@override
|
|
Stream<UserSettings> watchUserSettings() async* {
|
|
final box = await _box;
|
|
|
|
// 초기 값 전송
|
|
yield await getUserSettings();
|
|
|
|
// 변경사항 감시
|
|
yield* box.watch().asyncMap((_) async => await getUserSettings());
|
|
}
|
|
} |