diff --git a/lib/common/component/map/map.dart b/lib/common/component/map/map.dart index 5ecf8abea..f24d44f6d 100644 --- a/lib/common/component/map/map.dart +++ b/lib/common/component/map/map.dart @@ -13,9 +13,9 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; /// タッチ操作をハンドルするWidget /// mapViewModelProviderからMapStateを取得すること class MapTouchHandlerWidget extends HookConsumerWidget { - const MapTouchHandlerWidget({required this.key}); + const MapTouchHandlerWidget({super.key, required this.mapKey}); - final GlobalKey key; + final Key mapKey; @override Widget build(BuildContext context, WidgetRef ref) { @@ -27,7 +27,7 @@ class MapTouchHandlerWidget extends HookConsumerWidget { WidgetsBinding.instance.endOfFrame.then( (_) => Future( () { - ref.read(mapViewModelProvider(key).notifier) + ref.read(mapViewModelProvider(mapKey).notifier) ..registerWidgetSize( context.size!, ) @@ -49,66 +49,39 @@ class MapTouchHandlerWidget extends HookConsumerWidget { [], ); - return Stack( - children: [ - Listener( - onPointerSignal: - ref.read(mapViewModelProvider(key).notifier).recievedPointerSignal, - child: GestureDetector( - onScaleUpdate: - ref.read(mapViewModelProvider(key).notifier).handleScaleUpdate, - onScaleStart: - ref.read(mapViewModelProvider(key).notifier).handleScaleStart, - onScaleEnd: ref.read(mapViewModelProvider(key).notifier).handleScaleEnd, - ), - ), - // 初期値に戻す - - Positioned( - bottom: 0, - right: 0, - child: Padding( - padding: const EdgeInsets.all(20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - FilledButton.tonalIcon( - onPressed: ref.read(mapViewModelProvider(key).notifier).reset, - icon: const Icon(Icons.refresh), - label: const Text('init'), - ), - FilledButton.tonalIcon( - onPressed: () => - ref.read(mapViewModelProvider(key).notifier).animatedMoveTo( - const LatLng(35, 135), - duration: const Duration(milliseconds: 250), - ), - icon: const Icon(Icons.home), - label: const Text('Animated Move To 35,135'), - ), - FilledButton.tonalIcon( - onPressed: () => ref - .read(mapViewModelProvider(key).notifier) - .animatedZoomTo(20), - icon: const Icon(Icons.home), - label: const Text('Animated Zoom To Lv.20'), - ), - FilledButton.tonalIcon( - onPressed: () => - ref.read(mapViewModelProvider(key).notifier).animatedBounds( - [ - const LatLng(45.6, 145.1), - const LatLng(30, 128.8), - ], - ), - icon: const Icon(Icons.home), - label: const Text('Animated Fit Japan Map'), - ), - ], - ), - ), - ), - ], + return Listener( + onPointerSignal: + ref.read(mapViewModelProvider(mapKey).notifier).recievedPointerSignal, + child: GestureDetector( + onScaleUpdate: + ref.read(mapViewModelProvider(mapKey).notifier).handleScaleUpdate, + onScaleStart: + ref.read(mapViewModelProvider(mapKey).notifier).handleScaleStart, + onScaleEnd: + ref.read(mapViewModelProvider(mapKey).notifier).handleScaleEnd, + ), + ); + } +} + +class BaseMapWidget extends ConsumerWidget { + const BaseMapWidget({super.key, required this.mapKey}); + final Key mapKey; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final state = ref.watch(MapViewModelProvider(mapKey)); + final mapData = + ref.watch(mapDataProvider.select((value) => value.projectedData)); + if (mapData == null) { + throw Exception('mapData is null'); + } + return CustomPaint( + painter: MapPainter( + state: state, + mapData: mapData, + ), + size: Size.infinite, ); } } @@ -244,5 +217,6 @@ class MapPainter extends CustomPainter { } @override - bool shouldRepaint(covariant CustomPainter oldDelegate) => true; + bool shouldRepaint(covariant MapPainter oldDelegate) => + oldDelegate.state != state || oldDelegate.mapData != mapData; } diff --git a/lib/common/component/map/view_model/map_viemwodel.dart b/lib/common/component/map/view_model/map_viemwodel.dart index d20c63ca8..8c4dba823 100644 --- a/lib/common/component/map/view_model/map_viemwodel.dart +++ b/lib/common/component/map/view_model/map_viemwodel.dart @@ -1,4 +1,3 @@ -import 'dart:developer'; import 'dart:math' as math; import 'package:eqmonitor/common/component/map/model/map_state.dart'; @@ -15,9 +14,6 @@ part 'map_viemwodel.g.dart'; class MapViewModel extends _$MapViewModel { @override MapState build(Key key) { - ref.listenSelf((previous, next) { - log(next.toString()); - }); return const MapState( offset: Offset.zero, zoomLevel: 1, @@ -123,7 +119,6 @@ class MapViewModel extends _$MapViewModel { if (details.scale != 1.0) { return; } - debugPrint('handleScaleUpdate: ${details.focalPointDelta}'); state = state.move( Offset(details.focalPointDelta.dx, details.focalPointDelta.dy) / state.zoomLevel, @@ -153,13 +148,10 @@ class MapViewModel extends _$MapViewModel { _scaleController.reset(); _scaleStart = null; - log(_gestureType.toString(), name: 'GestureType'); - if (_gestureType == _GestureType.pan) { if (details.velocity.pixelsPerSecond.distance < kMinFlingVelocity) { return; } - // log('MOVE ANIMATION!'); final translation = state.offset; final frictionSimulationX = FrictionSimulation( _interactionEndFrictionCoefficient, @@ -192,7 +184,6 @@ class MapViewModel extends _$MapViewModel { if (details.scaleVelocity.abs() < 0.1) { return; } - // log('SCALE ANIMATION!'); final scale = state.zoomLevel; final frictionSimulation = FrictionSimulation( _interactionEndFrictionCoefficient * kDefaultMouseScrollToScaleFactor, @@ -232,7 +223,6 @@ class MapViewModel extends _$MapViewModel { void recievedPointerSignal(PointerSignalEvent event) { final double scaleChange; - log(event.runtimeType.toString()); if (event is PointerScrollEvent) { if (event.kind == PointerDeviceKind.trackpad) { // トラックパッドのスクロールなので Pan として扱う @@ -415,10 +405,7 @@ class MapViewModel extends _$MapViewModel { ).wait; } - void _onLatLngAnimation() {} - void registerWidgetSize(Size size) { - log(size.toString()); _widgetSize = size; } @@ -439,9 +426,7 @@ class MapViewModel extends _$MapViewModel { // setter set topLeftLatLng(LatLng latLng) { final topLeftPoint = WebMercatorProjection().project(latLng); - debugPrint('topLeftPoint: $topLeftPoint'); final offset = state.globalPointToOffset(topLeftPoint); - debugPrint('offset: $offset'); state = state.move(offset); } diff --git a/lib/common/feature/map/model/state/map_data_state.dart b/lib/common/feature/map/model/state/map_data_state.dart index ab7e755b6..faa3e98a9 100644 --- a/lib/common/feature/map/model/state/map_data_state.dart +++ b/lib/common/feature/map/model/state/map_data_state.dart @@ -8,21 +8,22 @@ import 'package:freezed_annotation/freezed_annotation.dart'; @immutable class MapDataState { const MapDataState({ - required this.isReady, this.data, + this.projectedData, }); - final bool isReady; final MapDataFromSource? data; + final MapProjectedData? projectedData; // copywith MapDataState copyWith({ bool? isReady, MapDataFromSource? data, + MapProjectedData? projectedData, }) { return MapDataState( - isReady: isReady ?? this.isReady, data: data ?? this.data, + projectedData: projectedData ?? this.projectedData, ); } } diff --git a/lib/common/feature/map/provider/map_data_provider.dart b/lib/common/feature/map/provider/map_data_provider.dart index 20f1133a1..572fd0527 100644 --- a/lib/common/feature/map/provider/map_data_provider.dart +++ b/lib/common/feature/map/provider/map_data_provider.dart @@ -15,18 +15,21 @@ part 'map_data_provider.g.dart'; @Riverpod(keepAlive: true) class MapData extends _$MapData { @override - MapDataState build() => const MapDataState( - isReady: false, - ); + MapDataState build() => const MapDataState(); - MapDataFromSource? _mapDataFromSource; - MapProjectedData? _mapProjectedData; Future initialize() async { - _mapDataFromSource ??= await _mapData(); - _mapProjectedData ??= await _projectMap(); - state = state.copyWith( - isReady: true, - ); + if (state.data == null) { + final data = await _mapData(); + state = state.copyWith( + data: data, + ); + } + if (state.projectedData == null) { + final projectedData = await _projectMap(); + state = state.copyWith( + projectedData: projectedData, + ); + } } Future _mapData() async { @@ -74,7 +77,7 @@ class MapData extends _$MapData { } Future _projectMap() async { - if (_mapDataFromSource == null) { + if (state.data == null) { throw Error(); } final jmaProjectedMap = @@ -84,7 +87,7 @@ class MapData extends _$MapData { if (type == MapDataType.worldMap) { continue; } - final map = _mapDataFromSource!.jmaMap[type]!; + final map = state.data!.jmaMap[type]!; final projectedMap = >[]; for (final e in map) { projectedMap.add( @@ -99,7 +102,7 @@ class MapData extends _$MapData { final worldProjectedMap = >[]; - for (final e in _mapDataFromSource!.worldMap) { + for (final e in state.data!.worldMap) { worldProjectedMap.add( MultiPolygonProjectedMapData.fromMapData( e, @@ -109,7 +112,7 @@ class MapData extends _$MapData { } final tsunamiProjectedLine = >[]; - for (final e in _mapDataFromSource!.tsunamiLine) { + for (final e in state.data!.tsunamiLine) { tsunamiProjectedLine.add( MultiLineProjectedMapData.fromLineData( e, @@ -124,11 +127,4 @@ class MapData extends _$MapData { tsunamiLine: tsunamiProjectedLine, ); } - - MapProjectedData get mapProjectedData { - if (_mapProjectedData == null) { - throw Error(); - } - return _mapProjectedData!; - } } diff --git a/lib/common/router/router.dart b/lib/common/router/router.dart index 46a045705..e0e290979 100644 --- a/lib/common/router/router.dart +++ b/lib/common/router/router.dart @@ -2,6 +2,7 @@ import 'package:eqmonitor/app.dart'; import 'package:eqmonitor/common/provider/shared_preferences.dart'; import 'package:eqmonitor/feature/debug/debug_home_view.dart'; import 'package:eqmonitor/feature/earthquake_history/page/earthquake_history.dart'; +import 'package:eqmonitor/feature/earthquake_history_details/screen/earthquake_history_details.dart'; import 'package:eqmonitor/feature/home/view/home_view.dart'; import 'package:eqmonitor/feature/setup/screen/setup_screen.dart'; import 'package:flutter/material.dart'; @@ -50,9 +51,23 @@ class EarthquakeHistoryRoute extends GoRouteData { const EarthquakeHistoryPage(); } +@TypedGoRoute( + path: '/earthquake-history/:eventId', +) +class EarthquakeHistoryDetailsRoute extends GoRouteData { + const EarthquakeHistoryDetailsRoute(this.eventId); + final int eventId; + @override + Widget build(BuildContext context, GoRouterState state) { + return EarthquakeHistoryDetailsPage( + eventId: eventId, + ); + } +} + @TypedGoRoute(path: '/home') class HomeRoute extends GoRouteData { const HomeRoute(); @override - Widget build(BuildContext context, GoRouterState state) => const HomeView(); + Widget build(BuildContext context, GoRouterState state) => HomeView(); } diff --git a/lib/feature/earthquake_history/component/earthquake_history_tile_widget.dart b/lib/feature/earthquake_history/component/earthquake_history_tile_widget.dart index 4004db442..499725c5d 100644 --- a/lib/feature/earthquake_history/component/earthquake_history_tile_widget.dart +++ b/lib/feature/earthquake_history/component/earthquake_history_tile_widget.dart @@ -12,10 +12,12 @@ import 'package:intl/intl.dart'; class EarthquakeHistoryTileWidget extends ConsumerWidget { const EarthquakeHistoryTileWidget({ required this.item, + this.onTap, super.key, }); final EarthquakeHistoryItem item; + final void Function(EarthquakeHistoryItem)? onTap; @override Widget build(BuildContext context, WidgetRef ref) { @@ -35,8 +37,6 @@ class EarthquakeHistoryTileWidget extends ConsumerWidget { (e) => e.type == TelegramType.vxse51, ) && item.telegrams.every((e) => e.type == TelegramType.vxse52); - // 長周期地震動に関する観測情報があるかどうか - final hasVxse62 = item.telegrams.any((e) => e.type == TelegramType.vxse62); // ! title final title = StringBuffer(); @@ -127,10 +127,10 @@ class EarthquakeHistoryTileWidget extends ConsumerWidget { ), ), ), - if (hasVxse62) + if (item.earthquake.lgIntensity != null) CustomChip( child: Text( - '長周期地震動 最大階級${item.earthquake.intensity!.maxLgInt}', + '長周期地震動 最大階級${item.earthquake.lgIntensity!.maxLgInt}', style: const TextStyle( fontWeight: FontWeight.bold, ), @@ -197,6 +197,9 @@ class EarthquakeHistoryTileWidget extends ConsumerWidget { ) : null, tileColor: backgroundColor?.withOpacity(0.4), + onTap: () => onTap?.call( + item, + ), ); } } diff --git a/lib/feature/earthquake_history/model/state/earthquake_history_item.dart b/lib/feature/earthquake_history/model/state/earthquake_history_item.dart index 34203f942..58926f054 100644 --- a/lib/feature/earthquake_history/model/state/earthquake_history_item.dart +++ b/lib/feature/earthquake_history/model/state/earthquake_history_item.dart @@ -29,6 +29,7 @@ class EarthquakeData with _$EarthquakeData { const factory EarthquakeData({ required Earthquake? earthquake, required Intensity? intensity, + required Intensity? lgIntensity, required CommentsOmitVar? comment, required bool isVolcano, }) = _EarthquakeData; diff --git a/lib/feature/earthquake_history/page/earthquake_history.dart b/lib/feature/earthquake_history/page/earthquake_history.dart index ab2e8a676..962c41f08 100644 --- a/lib/feature/earthquake_history/page/earthquake_history.dart +++ b/lib/feature/earthquake_history/page/earthquake_history.dart @@ -1,7 +1,9 @@ +import 'package:eqmonitor/common/router/router.dart'; import 'package:eqmonitor/feature/earthquake_history/model/state/earthquake_history_item.dart'; import 'package:eqmonitor/feature/earthquake_history/viewmodel/earthquake_history_view_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import '../component/earthquake_history_tile_widget.dart'; @@ -119,7 +121,12 @@ class EarthquakeHistoryListView extends StatelessWidget { return const _ListBottomWidget(); } final item = data[index]; - return EarthquakeHistoryTileWidget(item: item); + return EarthquakeHistoryTileWidget( + item: item, + onTap: (p0) => context.push( + EarthquakeHistoryDetailsRoute(p0.eventId).location, + ), + ); }, ), ); @@ -137,7 +144,9 @@ class _ListBottomWidget extends ConsumerWidget { if (state.isLoading || state.isRefreshing || state.isReloading) { return const Padding( padding: EdgeInsets.all(24), - child: CircularProgressIndicator.adaptive(), + child: Center( + child: CircularProgressIndicator.adaptive(), + ), ); } return const SizedBox.shrink(); @@ -146,7 +155,7 @@ class _ListBottomWidget extends ConsumerWidget { if (state.isLoading || state.isRefreshing || state.isReloading) { return const Padding( padding: EdgeInsets.all(24), - child: CircularProgressIndicator.adaptive(), + child: Center(child: CircularProgressIndicator.adaptive()), ); } return Padding( diff --git a/lib/feature/earthquake_history/viewmodel/earthquake_history_view_model.dart b/lib/feature/earthquake_history/viewmodel/earthquake_history_view_model.dart index a9467db59..93b038de8 100644 --- a/lib/feature/earthquake_history/viewmodel/earthquake_history_view_model.dart +++ b/lib/feature/earthquake_history/viewmodel/earthquake_history_view_model.dart @@ -129,9 +129,7 @@ class EarthquakeHistoryViewModel extends _$EarthquakeHistoryViewModel { earthquake = (vxse52.body as TelegramVxse52Body).earthquake; } Intensity? intensity; - if (vxse62 != null) { - intensity = (vxse62.body as TelegramVxse62Body).intensity; - } else if (vxse53 != null) { + if (vxse53 != null) { intensity = (vxse53.body as TelegramVxse53Body).intensity; } else if (vxse51 != null) { intensity = (vxse51.body as TelegramVxse51Body).intensity; @@ -166,6 +164,9 @@ class EarthquakeHistoryViewModel extends _$EarthquakeHistoryViewModel { final earthquakeData = EarthquakeData( earthquake: earthquake, intensity: intensity, + lgIntensity: vxse62 != null + ? (vxse62.body as TelegramVxse62Body).intensity + : null, comment: comment, isVolcano: isVolcano, ); diff --git a/lib/feature/earthquake_history_details/screen/earthquake_history_details.dart b/lib/feature/earthquake_history_details/screen/earthquake_history_details.dart index 8b1378917..0f045bc9c 100644 --- a/lib/feature/earthquake_history_details/screen/earthquake_history_details.dart +++ b/lib/feature/earthquake_history_details/screen/earthquake_history_details.dart @@ -1 +1,44 @@ +import 'package:collection/collection.dart'; +import 'package:eqmonitor/feature/earthquake_history/viewmodel/earthquake_history_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +class EarthquakeHistoryDetailsPage extends ConsumerWidget { + EarthquakeHistoryDetailsPage({ + required this.eventId, + super.key, + }) : super() { + mapKey = GlobalKey(debugLabel: 'eq-history-details-$eventId'); + } + + late final mapKey; + + final int eventId; + + @override + Widget build(BuildContext context, WidgetRef ref) { + // 当該データがアレばOK + final data = ref + .watch(earthquakeHistoryViewModelProvider) + .value + ?.firstWhereOrNull((e) => e.eventId == eventId); + if (data == null) { + return const Scaffold( + body: Center( + child: Text('当該データが見つかりませんでした\n再度地震の履歴を読み込んでください'), + ), + ); + } + + return Scaffold( + appBar: AppBar( + title: Text(eventId.toString()), + ), + body: SingleChildScrollView( + child: Text( + data.toString(), + ), + ), + ); + } +} diff --git a/lib/feature/home/view/home_view.dart b/lib/feature/home/view/home_view.dart index 16cbbc8b1..c0479f5bb 100644 --- a/lib/feature/home/view/home_view.dart +++ b/lib/feature/home/view/home_view.dart @@ -5,7 +5,8 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class HomeView extends HookConsumerWidget { - const HomeView({super.key}); + HomeView({super.key}); + final mapKey = GlobalKey(debugLabel: 'HomeView'); @override Widget build(BuildContext context, WidgetRef ref) { @@ -17,14 +18,31 @@ class HomeView extends HookConsumerWidget { [], ); final state = ref.watch(mapDataProvider); - if (state.isReady) { - } else { + if (state.projectedData == null) { return const Scaffold( body: Center( - child: CircularProgressIndicator(), + child: CircularProgressIndicator.adaptive(), + ), + ); + } else { + return Scaffold( + body: Stack( + children: [ + // background + Container( + color: const Color.fromARGB(255, 179, 230, 255), + ), + ClipRRect( + child: Stack( + children: [ + BaseMapWidget(mapKey: mapKey), + ], + ), + ), + MapTouchHandlerWidget(mapKey: mapKey), + ], ), ); } - throw Exception(); } }