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

@@ -217,28 +217,31 @@ class _RandomSelectionScreenState extends ConsumerState<RandomSelectionScreen> {
Consumer(
builder: (context, ref, child) {
final locationAsync = ref.watch(
currentLocationProvider,
currentLocationWithFallbackProvider,
);
final restaurantsAsync = ref.watch(
restaurantListProvider,
);
if (locationAsync.hasValue &&
restaurantsAsync.hasValue) {
final location = locationAsync.value;
final restaurants = restaurantsAsync.value;
final location = locationAsync.maybeWhen(
data: (pos) => pos,
orElse: () => null,
);
final restaurants = restaurantsAsync.maybeWhen(
data: (list) => list,
orElse: () => null,
);
if (location != null && restaurants != null) {
final count = _getRestaurantCountInRange(
restaurants,
location,
_distanceValue,
);
return Text(
'$count개 맛집 포함',
style: AppTypography.caption(isDark),
);
}
if (location != null && restaurants != null) {
final count = _getRestaurantCountInRange(
restaurants,
location,
_distanceValue,
);
return Text(
'$count개 맛집 포함',
style: AppTypography.caption(isDark),
);
}
return Text(
@@ -439,15 +442,17 @@ class _RandomSelectionScreenState extends ConsumerState<RandomSelectionScreen> {
}
bool _canRecommend() {
final locationAsync = ref.read(currentLocationProvider);
final locationAsync = ref.read(currentLocationWithFallbackProvider);
final restaurantsAsync = ref.read(restaurantListProvider);
if (!locationAsync.hasValue || !restaurantsAsync.hasValue) {
return false;
}
final location = locationAsync.value;
final restaurants = restaurantsAsync.value;
final location = locationAsync.maybeWhen(
data: (pos) => pos,
orElse: () => null,
);
final restaurants = restaurantsAsync.maybeWhen(
data: (list) => list,
orElse: () => null,
);
if (location == null || restaurants == null || restaurants.isEmpty) {
return false;
@@ -491,10 +496,7 @@ class _RandomSelectionScreenState extends ConsumerState<RandomSelectionScreen> {
final adWatched = await adService.showInterstitialAd(context);
if (!mounted) return;
if (!adWatched) {
_showSnack(
'광고를 끝까지 시청해야 추천을 받을 수 있어요.',
backgroundColor: AppColors.lightError,
);
_showSnack('광고를 끝까지 시청해야 추천을 받을 수 있어요.', type: _SnackType.error);
return;
}
}
@@ -502,10 +504,7 @@ class _RandomSelectionScreenState extends ConsumerState<RandomSelectionScreen> {
if (!mounted) return;
await _showRecommendationDialog(candidate, recommendedAt: recommendedAt);
} catch (_) {
_showSnack(
'추천을 준비하는 중 문제가 발생했습니다.',
backgroundColor: AppColors.lightError,
);
_showSnack('추천을 준비하는 중 문제가 발생했습니다.', type: _SnackType.error);
} finally {
if (mounted) {
setState(() {
@@ -531,17 +530,14 @@ class _RandomSelectionScreenState extends ConsumerState<RandomSelectionScreen> {
if (result.hasError) {
final message = result.error?.toString() ?? '알 수 없는 오류';
_showSnack(
'추천 중 오류가 발생했습니다: $message',
backgroundColor: AppColors.lightError,
);
_showSnack('추천 중 오류가 발생했습니다: $message', type: _SnackType.error);
return null;
}
if (recommendation == null) {
_showSnack(
'조건에 맞는 식당이 존재하지 않습니다. 광고는 재생되지 않았습니다.',
backgroundColor: AppColors.lightError,
type: _SnackType.warning,
);
}
return recommendation;
@@ -626,10 +622,10 @@ class _RandomSelectionScreenState extends ConsumerState<RandomSelectionScreen> {
if (notificationEnabled && !notificationScheduled && !kIsWeb) {
_showSnack(
'방문 기록은 저장됐지만 알림 권한이나 설정을 확인해 주세요. 방문 알림을 예약하지 못했습니다.',
backgroundColor: AppColors.lightError,
type: _SnackType.warning,
);
} else {
_showSnack('맛있게 드세요! 🍴');
_showSnack('맛있게 드세요! 🍴', type: _SnackType.success);
}
if (mounted) {
setState(() {
@@ -637,25 +633,25 @@ class _RandomSelectionScreenState extends ConsumerState<RandomSelectionScreen> {
});
}
} catch (_) {
_showSnack(
'방문 기록 또는 알림 예약에 실패했습니다.',
backgroundColor: AppColors.lightError,
);
_showSnack('방문 기록 또는 알림 예약에 실패했습니다.', type: _SnackType.error);
}
}
void _showSnack(
String message, {
Color backgroundColor = AppColors.lightPrimary,
}) {
void _showSnack(String message, {_SnackType type = _SnackType.info}) {
if (!mounted) return;
final bgColor = switch (type) {
_SnackType.success => Colors.teal.shade600,
_SnackType.warning => Colors.orange.shade600,
_SnackType.error => AppColors.lightError,
_SnackType.info => Colors.blueGrey.shade600,
};
final topInset = MediaQuery.of(context).viewPadding.top;
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: backgroundColor,
backgroundColor: bgColor,
behavior: SnackBarBehavior.floating,
margin: EdgeInsets.fromLTRB(
16,
@@ -668,3 +664,5 @@ class _RandomSelectionScreenState extends ConsumerState<RandomSelectionScreen> {
);
}
}
enum _SnackType { info, warning, error, success }