feat(app): seed restaurants, geocode addresses, refresh sharing

This commit is contained in:
JiWoong Sul
2025-11-26 19:01:00 +09:00
parent 2a01fa50c6
commit 0e8c06bade
29 changed files with 18319 additions and 427 deletions

View File

@@ -3,6 +3,8 @@ import 'package:lunchpick/core/utils/category_mapper.dart';
import 'package:lunchpick/domain/entities/restaurant.dart';
import 'package:lunchpick/domain/repositories/restaurant_repository.dart';
import 'package:lunchpick/presentation/providers/di_providers.dart';
import 'package:lunchpick/presentation/providers/location_provider.dart';
import 'package:lunchpick/core/utils/distance_calculator.dart';
import 'package:uuid/uuid.dart';
/// 맛집 목록 Provider
@@ -11,6 +13,35 @@ final restaurantListProvider = StreamProvider<List<Restaurant>>((ref) {
return repository.watchRestaurants();
});
/// 거리 정보를 포함한 맛집 목록 Provider (현재 위치 기반)
final sortedRestaurantsByDistanceProvider =
StreamProvider<List<({Restaurant restaurant, double? distanceKm})>>((ref) {
final restaurantsStream = ref.watch(restaurantListProvider.stream);
final positionAsync = ref.watch(currentLocationProvider);
final position = positionAsync.maybeWhen(
data: (pos) => pos ?? defaultPosition(),
orElse: () => defaultPosition(),
);
return restaurantsStream.map((restaurants) {
final sorted =
restaurants.map<({Restaurant restaurant, double? distanceKm})>((r) {
final distanceKm = DistanceCalculator.calculateDistance(
lat1: position.latitude,
lon1: position.longitude,
lat2: r.latitude,
lon2: r.longitude,
);
return (restaurant: r, distanceKm: distanceKm);
}).toList()..sort(
(a, b) => (a.distanceKm ?? double.infinity).compareTo(
b.distanceKm ?? double.infinity,
),
);
return sorted;
});
});
/// 특정 맛집 Provider
final restaurantProvider = FutureProvider.family<Restaurant?, String>((
ref,
@@ -20,10 +51,14 @@ final restaurantProvider = FutureProvider.family<Restaurant?, String>((
return repository.getRestaurantById(id);
});
/// 카테고리 목록 Provider
final categoriesProvider = FutureProvider<List<String>>((ref) async {
final repository = ref.watch(restaurantRepositoryProvider);
return repository.getAllCategories();
/// 카테고리 목록 Provider (맛집 스트림을 구독해 즉시 갱신)
final categoriesProvider = StreamProvider<List<String>>((ref) {
final restaurantsStream = ref.watch(restaurantListProvider.stream);
return restaurantsStream.map((restaurants) {
final categories = restaurants.map((r) => r.category).toSet().toList()
..sort();
return categories;
});
});
/// 맛집 관리 StateNotifier
@@ -76,24 +111,12 @@ class RestaurantNotifier extends StateNotifier<AsyncValue<void>> {
state = const AsyncValue.loading();
try {
final updated = Restaurant(
id: restaurant.id,
name: restaurant.name,
category: restaurant.category,
subCategory: restaurant.subCategory,
description: restaurant.description,
phoneNumber: restaurant.phoneNumber,
roadAddress: restaurant.roadAddress,
jibunAddress: restaurant.jibunAddress,
latitude: restaurant.latitude,
longitude: restaurant.longitude,
lastVisitDate: restaurant.lastVisitDate,
source: restaurant.source,
createdAt: restaurant.createdAt,
updatedAt: DateTime.now(),
final nextSource = restaurant.source == DataSource.PRESET
? DataSource.USER_INPUT
: restaurant.source;
await _repository.updateRestaurant(
restaurant.copyWith(source: nextSource, updatedAt: DateTime.now()),
);
await _repository.updateRestaurant(updated);
state = const AsyncValue.data(null);
} catch (e, stack) {
state = AsyncValue.error(e, stack);