feat(app): add manual entry and sharing flows
This commit is contained in:
@@ -3,33 +3,46 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
import '../../domain/entities/restaurant.dart';
|
||||
import '../providers/di_providers.dart';
|
||||
import '../providers/restaurant_provider.dart';
|
||||
|
||||
/// 식당 추가 화면의 상태 모델
|
||||
class AddRestaurantState {
|
||||
final bool isLoading;
|
||||
final bool isSearching;
|
||||
final String? errorMessage;
|
||||
final Restaurant? fetchedRestaurantData;
|
||||
final RestaurantFormData formData;
|
||||
final List<Restaurant> searchResults;
|
||||
|
||||
const AddRestaurantState({
|
||||
this.isLoading = false,
|
||||
this.isSearching = false,
|
||||
this.errorMessage,
|
||||
this.fetchedRestaurantData,
|
||||
required this.formData,
|
||||
this.searchResults = const [],
|
||||
});
|
||||
|
||||
AddRestaurantState copyWith({
|
||||
bool? isLoading,
|
||||
bool? isSearching,
|
||||
String? errorMessage,
|
||||
Restaurant? fetchedRestaurantData,
|
||||
RestaurantFormData? formData,
|
||||
List<Restaurant>? searchResults,
|
||||
bool clearFetchedRestaurant = false,
|
||||
bool clearError = false,
|
||||
}) {
|
||||
return AddRestaurantState(
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
errorMessage: errorMessage ?? this.errorMessage,
|
||||
fetchedRestaurantData: fetchedRestaurantData ?? this.fetchedRestaurantData,
|
||||
isSearching: isSearching ?? this.isSearching,
|
||||
errorMessage: clearError ? null : (errorMessage ?? this.errorMessage),
|
||||
fetchedRestaurantData: clearFetchedRestaurant
|
||||
? null
|
||||
: (fetchedRestaurantData ?? this.fetchedRestaurantData),
|
||||
formData: formData ?? this.formData,
|
||||
searchResults: searchResults ?? this.searchResults,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -156,7 +169,12 @@ class AddRestaurantViewModel extends StateNotifier<AddRestaurantState> {
|
||||
final Ref _ref;
|
||||
|
||||
AddRestaurantViewModel(this._ref)
|
||||
: super(const AddRestaurantState(formData: RestaurantFormData()));
|
||||
: super(const AddRestaurantState(formData: RestaurantFormData()));
|
||||
|
||||
/// 상태 초기화
|
||||
void reset() {
|
||||
state = const AddRestaurantState(formData: RestaurantFormData());
|
||||
}
|
||||
|
||||
/// 네이버 URL로부터 식당 정보 가져오기
|
||||
Future<void> fetchFromNaverUrl(String url) async {
|
||||
@@ -165,11 +183,11 @@ class AddRestaurantViewModel extends StateNotifier<AddRestaurantState> {
|
||||
return;
|
||||
}
|
||||
|
||||
state = state.copyWith(isLoading: true, errorMessage: null);
|
||||
state = state.copyWith(isLoading: true, clearError: true);
|
||||
|
||||
try {
|
||||
final notifier = _ref.read(restaurantNotifierProvider.notifier);
|
||||
final restaurant = await notifier.addRestaurantFromUrl(url);
|
||||
final repository = _ref.read(restaurantRepositoryProvider);
|
||||
final restaurant = await repository.previewRestaurantFromUrl(url);
|
||||
|
||||
state = state.copyWith(
|
||||
isLoading: false,
|
||||
@@ -177,42 +195,83 @@ class AddRestaurantViewModel extends StateNotifier<AddRestaurantState> {
|
||||
formData: RestaurantFormData.fromRestaurant(restaurant),
|
||||
);
|
||||
} catch (e) {
|
||||
state = state.copyWith(
|
||||
isLoading: false,
|
||||
errorMessage: e.toString(),
|
||||
);
|
||||
state = state.copyWith(isLoading: false, errorMessage: e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/// 네이버 검색으로 식당 목록 검색
|
||||
Future<void> searchRestaurants(
|
||||
String query, {
|
||||
double? latitude,
|
||||
double? longitude,
|
||||
}) async {
|
||||
if (query.trim().isEmpty) {
|
||||
state = state.copyWith(
|
||||
errorMessage: '검색어를 입력해주세요.',
|
||||
searchResults: const [],
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
state = state.copyWith(isSearching: true, clearError: true);
|
||||
|
||||
try {
|
||||
final repository = _ref.read(restaurantRepositoryProvider);
|
||||
final results = await repository.searchRestaurantsFromNaver(
|
||||
query: query,
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
);
|
||||
state = state.copyWith(isSearching: false, searchResults: results);
|
||||
} catch (e) {
|
||||
state = state.copyWith(isSearching: false, errorMessage: e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/// 검색 결과 선택
|
||||
void selectSearchResult(Restaurant restaurant) {
|
||||
state = state.copyWith(
|
||||
fetchedRestaurantData: restaurant,
|
||||
formData: RestaurantFormData.fromRestaurant(restaurant),
|
||||
clearError: true,
|
||||
);
|
||||
}
|
||||
|
||||
/// 식당 정보 저장
|
||||
Future<bool> saveRestaurant() async {
|
||||
final notifier = _ref.read(restaurantNotifierProvider.notifier);
|
||||
|
||||
try {
|
||||
state = state.copyWith(isLoading: true, clearError: true);
|
||||
Restaurant restaurantToSave;
|
||||
|
||||
|
||||
// 네이버에서 가져온 데이터가 있으면 업데이트
|
||||
final fetchedData = state.fetchedRestaurantData;
|
||||
if (fetchedData != null) {
|
||||
restaurantToSave = fetchedData.copyWith(
|
||||
name: state.formData.name,
|
||||
category: state.formData.category,
|
||||
subCategory: state.formData.subCategory.isEmpty
|
||||
? state.formData.category
|
||||
subCategory: state.formData.subCategory.isEmpty
|
||||
? state.formData.category
|
||||
: state.formData.subCategory,
|
||||
description: state.formData.description.isEmpty
|
||||
? null
|
||||
description: state.formData.description.isEmpty
|
||||
? null
|
||||
: state.formData.description,
|
||||
phoneNumber: state.formData.phoneNumber.isEmpty
|
||||
? null
|
||||
phoneNumber: state.formData.phoneNumber.isEmpty
|
||||
? null
|
||||
: state.formData.phoneNumber,
|
||||
roadAddress: state.formData.roadAddress,
|
||||
jibunAddress: state.formData.jibunAddress.isEmpty
|
||||
? state.formData.roadAddress
|
||||
jibunAddress: state.formData.jibunAddress.isEmpty
|
||||
? state.formData.roadAddress
|
||||
: state.formData.jibunAddress,
|
||||
latitude: double.tryParse(state.formData.latitude) ?? fetchedData.latitude,
|
||||
longitude: double.tryParse(state.formData.longitude) ?? fetchedData.longitude,
|
||||
naverUrl: state.formData.naverUrl.isEmpty ? null : state.formData.naverUrl,
|
||||
latitude:
|
||||
double.tryParse(state.formData.latitude) ?? fetchedData.latitude,
|
||||
longitude:
|
||||
double.tryParse(state.formData.longitude) ??
|
||||
fetchedData.longitude,
|
||||
naverUrl: state.formData.naverUrl.isEmpty
|
||||
? null
|
||||
: state.formData.naverUrl,
|
||||
updatedAt: DateTime.now(),
|
||||
);
|
||||
} else {
|
||||
@@ -221,9 +280,10 @@ class AddRestaurantViewModel extends StateNotifier<AddRestaurantState> {
|
||||
}
|
||||
|
||||
await notifier.addRestaurantDirect(restaurantToSave);
|
||||
state = state.copyWith(isLoading: false);
|
||||
return true;
|
||||
} catch (e) {
|
||||
state = state.copyWith(errorMessage: e.toString());
|
||||
state = state.copyWith(isLoading: false, errorMessage: e.toString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -235,12 +295,13 @@ class AddRestaurantViewModel extends StateNotifier<AddRestaurantState> {
|
||||
|
||||
/// 에러 메시지 초기화
|
||||
void clearError() {
|
||||
state = state.copyWith(errorMessage: null);
|
||||
state = state.copyWith(clearError: true);
|
||||
}
|
||||
}
|
||||
|
||||
/// AddRestaurantViewModel Provider
|
||||
final addRestaurantViewModelProvider =
|
||||
StateNotifierProvider.autoDispose<AddRestaurantViewModel, AddRestaurantState>(
|
||||
(ref) => AddRestaurantViewModel(ref),
|
||||
);
|
||||
StateNotifierProvider.autoDispose<
|
||||
AddRestaurantViewModel,
|
||||
AddRestaurantState
|
||||
>((ref) => AddRestaurantViewModel(ref));
|
||||
|
||||
Reference in New Issue
Block a user