From c03a61a77b7ef42aac1790adeb118e2a6bce93e7 Mon Sep 17 00:00:00 2001 From: Ryotaro Onoue <73390859+YumNumm@users.noreply.github.com> Date: Wed, 17 Apr 2024 22:40:40 +0900 Subject: [PATCH] =?UTF-8?q?=E6=B5=B7=E5=A4=96=E5=A4=A7=E8=A6=8F=E6=A8=A1?= =?UTF-8?q?=E5=99=B4=E7=81=AB=E3=81=AE=E6=89=B1=E3=81=84=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=20=20(#649)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: modalの角丸をMaterial3に適合 * fix: WebSocket接続中の判定ミス * add: 地震履歴一覧・詳細の大規模噴火の扱い修正 --- .../component/sheet/basic_modal_sheet.dart | 12 +-- app/lib/core/extension/earthquake_v1.dart | 19 ++++ .../data/earthquake_history_notifier.dart | 7 +- .../earthquake_history_list_tile.dart | 39 +++++--- .../earthquake_hypo_info_widget.dart | 70 ++++++++++++- .../screen/earthquake_history_details.dart | 98 +++---------------- 6 files changed, 137 insertions(+), 108 deletions(-) create mode 100644 app/lib/core/extension/earthquake_v1.dart diff --git a/app/lib/core/component/sheet/basic_modal_sheet.dart b/app/lib/core/component/sheet/basic_modal_sheet.dart index 5bd0d38d3..f8a63f05c 100644 --- a/app/lib/core/component/sheet/basic_modal_sheet.dart +++ b/app/lib/core/component/sheet/basic_modal_sheet.dart @@ -51,14 +51,12 @@ class BasicModalSheet extends HookWidget { ), child: DecoratedBox( decoration: BoxDecoration( - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16), + borderRadius: + const BorderRadius.vertical(top: Radius.circular(28)), + color: theme.colorScheme.background, + border: Border.all( + color: theme.colorScheme.onSurface.withOpacity(0.1), ), - color: theme.colorScheme.surface, - boxShadow: const [ - BoxShadow(color: Colors.black12, blurRadius: 12), - ], ), child: SafeArea( top: hasAppBar, diff --git a/app/lib/core/extension/earthquake_v1.dart b/app/lib/core/extension/earthquake_v1.dart new file mode 100644 index 000000000..b06725922 --- /dev/null +++ b/app/lib/core/extension/earthquake_v1.dart @@ -0,0 +1,19 @@ +import 'package:eqapi_types/eqapi_types.dart'; + +extension EarthquakeV1Ex on EarthquakeV1 { + bool get isVolcano => + (text?.contains('大規模な噴火が発生しました') ?? false) && + (text?.contains('実際には、規模の大きな地震は発生していない点に留意') ?? false); + + String? get volcanoName { + if (!isVolcano) { + return null; + } + + final splitted = text?.split('分頃(日本時間)に') ?? []; + if (splitted.length != 2) { + return null; + } + return splitted[1].split('で大規模な噴火が発生しました')[0]; + } +} diff --git a/app/lib/feature/earthquake_history/data/earthquake_history_notifier.dart b/app/lib/feature/earthquake_history/data/earthquake_history_notifier.dart index d15cf5508..2acb8f505 100644 --- a/app/lib/feature/earthquake_history/data/earthquake_history_notifier.dart +++ b/app/lib/feature/earthquake_history/data/earthquake_history_notifier.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:developer'; import 'package:collection/collection.dart'; import 'package:eqapi_types/eqapi_types.dart'; @@ -183,17 +184,21 @@ class EarthquakeHistoryNotifier extends _$EarthquakeHistoryNotifier { Future _refreshIfWebsocketNotConnected() async { // AsyncData以外の場合は何もしない if (state is! AsyncData) { + log('state is not AsyncData'); return; } // WebSocketが接続されている場合は何もしない final webSocketState = ref.read(websocketStatusProvider); - if (webSocketState is Connected || webSocketState is Connecting) { + if (webSocketState is Connected || webSocketState is Reconnected) { + log('WebSocket is ${webSocketState.runtimeType}'); return; } // パラメータが指定されている場合は何もしない if (parameter != const EarthquakeHistoryParameter()) { + log('parameter is not default'); return; } + log('refreshIfWebsocketNotConnected'); // リフレッシュ処理を実行 final repository = ref.read(earthquakeHistoryRepositoryProvider); diff --git a/app/lib/feature/earthquake_history/ui/components/earthquake_history_list_tile.dart b/app/lib/feature/earthquake_history/ui/components/earthquake_history_list_tile.dart index dc312d9e8..3bd3990b5 100644 --- a/app/lib/feature/earthquake_history/ui/components/earthquake_history_list_tile.dart +++ b/app/lib/feature/earthquake_history/ui/components/earthquake_history_list_tile.dart @@ -2,6 +2,7 @@ import 'package:collection/collection.dart'; import 'package:eqapi_types/eqapi_types.dart'; import 'package:eqmonitor/core/component/intenisty/intensity_icon_type.dart'; import 'package:eqmonitor/core/component/intenisty/jma_intensity_icon.dart'; +import 'package:eqmonitor/core/extension/earthquake_v1.dart'; import 'package:eqmonitor/core/provider/config/theme/intensity_color/intensity_color_provider.dart'; import 'package:eqmonitor/core/provider/config/theme/intensity_color/model/intensity_color_model.dart'; import 'package:eqmonitor/core/provider/jma_code_table_provider.dart'; @@ -64,11 +65,26 @@ class EarthquakeHistoryListTile extends HookConsumerWidget { final theme = Theme.of(context); final codeTable = ref.watch(jmaCodeTableProvider); + + /// 遠地地震かどうか + final isFarEarthquake = item.headline?.contains('海外で規模の大きな地震') ?? false; + + /// 噴火かどうか + final isVolcano = item.isVolcano; + final hypoName = useMemoized( - () => codeTable.areaEpicenter.items.firstWhereOrNull( - (e) => int.tryParse(e.code) == item.epicenterCode, - ), - [item.epicenterCode], + () { + final volcanoName = item.volcanoName; + if (volcanoName != null) { + return volcanoName; + } + return codeTable.areaEpicenter.items + .firstWhereOrNull( + (e) => int.tryParse(e.code) == item.epicenterCode, + ) + ?.name; + }, + [item], ); final hypoDetailName = useMemoized( () => codeTable.areaEpicenterDetail.items.firstWhereOrNull( @@ -77,13 +93,6 @@ class EarthquakeHistoryListTile extends HookConsumerWidget { [item.epicenterDetailCode], ); - /// 遠地地震かどうか - final isFarEarthquake = item.headline?.contains('海外で規模の大きな地震') ?? false; - - /// 噴火かどうか - final isVolcano = (item.text?.contains('大規模な噴火が発生しました') ?? false) && - (item.text?.contains('実際には、規模の大きな地震は発生していない点に留意') ?? false); - final maxIntensityRegionNames = item.maxIntensityRegionNames; final maxIntensity = item.maxIntensity; final title = switch (( @@ -93,19 +102,19 @@ class EarthquakeHistoryListTile extends HookConsumerWidget { maxIntensityRegionNames )) { ( - final AreaEpicenter_AreaEpicenterItem hypoName, + final String hypoName, final AreaEpicenterDetail_AreaEpicenterDetailItem hypoDetailName, _, _ ) => - '${hypoName.name}(${hypoDetailName.name})', + '$hypoName(${hypoDetailName.name})', ( - final AreaEpicenter_AreaEpicenterItem hypoName, + final String hypoName, _, _, _, ) => - hypoName.name, + hypoName, (_, _, final JmaIntensity intensity, final List regionNames) when regionNames.isNotEmpty && regionNames.length >= 2 => '最大震度$intensityを${regionNames.first}などで観測', diff --git a/app/lib/feature/earthquake_history_details/component/earthquake_hypo_info_widget.dart b/app/lib/feature/earthquake_history_details/component/earthquake_hypo_info_widget.dart index 77b275397..41ca5e27e 100644 --- a/app/lib/feature/earthquake_history_details/component/earthquake_hypo_info_widget.dart +++ b/app/lib/feature/earthquake_history_details/component/earthquake_hypo_info_widget.dart @@ -2,6 +2,7 @@ import 'package:collection/collection.dart'; import 'package:eqapi_types/eqapi_types.dart'; import 'package:eqmonitor/core/component/intenisty/intensity_icon_type.dart'; import 'package:eqmonitor/core/component/intenisty/jma_intensity_icon.dart'; +import 'package:eqmonitor/core/extension/earthquake_v1.dart'; import 'package:eqmonitor/core/provider/config/theme/intensity_color/intensity_color_provider.dart'; import 'package:eqmonitor/core/provider/config/theme/intensity_color/model/intensity_color_model.dart'; import 'package:eqmonitor/core/provider/jma_code_table_provider.dart'; @@ -28,10 +29,12 @@ class EarthquakeHypoInfoWidget extends HookConsumerWidget { final textTheme = theme.textTheme; final intensityColorScheme = ref.watch(intensityColorProvider); + final isVolcano = item.isVolcano; final maxIntensity = item.maxIntensity; final colorScheme = switch (maxIntensity) { final JmaIntensity intensity => intensityColorScheme.fromJmaIntensity(intensity), + _ when isVolcano => intensityColorScheme.sixUpper, _ => null, }; final codeTable = ref.watch(jmaCodeTableProvider); @@ -63,6 +66,61 @@ class EarthquakeHypoInfoWidget extends HookConsumerWidget { ], ) : null; + // 「分頃(日本時間)」の後から「火山で大規模な噴火が発生しました」 の間の文字列 + final volcanoName = item.volcanoName; + final volcanoWidget = isVolcano + ? Padding( + padding: const EdgeInsets.all(8), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.baseline, + textBaseline: TextBaseline.alphabetic, + children: [ + Flexible( + child: Text( + '火山の大規模な噴火', + style: textTheme.titleMedium!.copyWith( + color: textTheme.titleMedium!.color!.withOpacity(0.8), + ), + ), + ), + const SizedBox(width: 4), + Flexible( + child: Text( + volcanoName ?? '不明', + style: textTheme.headlineMedium!.copyWith( + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + Flexible( + child: Text.rich( + TextSpan( + children: [ + TextSpan( + text: hypoName?.name ?? '不明', + style: textTheme.titleMedium, + ), + if (hypoDetailName != null) ...[ + const TextSpan(text: ' '), + TextSpan( + text: '(${hypoDetailName.name})', + style: textTheme.titleSmall, + ), + ], + ], + ), + ), + ), + ], + ), + ) + : null; // 「MaxInt, 震源地, 規模」 final hypoWidget = Row( textBaseline: TextBaseline.ideographic, @@ -269,7 +327,9 @@ class EarthquakeHypoInfoWidget extends HookConsumerWidget { alignment: WrapAlignment.center, children: [ const Row(), - if (isEarthquakeNull) + if (volcanoWidget != null) + volcanoWidget + else if (isEarthquakeNull) earthquakeNullWidget else if (isMagnitudeAndDepthUnknown) ...[ magnitudeDepthUnknownWidget, @@ -284,8 +344,13 @@ class EarthquakeHypoInfoWidget extends HookConsumerWidget { ); final card = Card( - margin: const EdgeInsets.all(4), + margin: const EdgeInsets.symmetric( + horizontal: 8, + ).add( + const EdgeInsets.only(bottom: 4), + ), elevation: 0, + shadowColor: Colors.transparent, // 角丸にして Border shape: RoundedRectangleBorder( @@ -303,7 +368,6 @@ class EarthquakeHypoInfoWidget extends HookConsumerWidget { ), child: Column( children: [ - const SizedBox(height: 2), Row( children: [ if (maxIntensityWidget != null) maxIntensityWidget, diff --git a/app/lib/feature/earthquake_history_details/screen/earthquake_history_details.dart b/app/lib/feature/earthquake_history_details/screen/earthquake_history_details.dart index 0cf326ebf..e12f4b649 100644 --- a/app/lib/feature/earthquake_history_details/screen/earthquake_history_details.dart +++ b/app/lib/feature/earthquake_history_details/screen/earthquake_history_details.dart @@ -1,8 +1,5 @@ // ignore_for_file: deprecated_member_use -import 'dart:convert'; -import 'dart:math'; - import 'package:eqapi_types/eqapi_types.dart'; import 'package:eqmonitor/core/component/container/bordered_container.dart'; import 'package:eqmonitor/core/component/intenisty/intensity_icon_type.dart'; @@ -11,9 +8,7 @@ import 'package:eqmonitor/core/component/intenisty/jma_lg_intensity_icon.dart'; import 'package:eqmonitor/core/component/sheet/basic_modal_sheet.dart'; import 'package:eqmonitor/core/component/sheet/sheet_floating_action_buttons.dart'; import 'package:eqmonitor/core/component/widget/error_widget.dart'; -import 'package:eqmonitor/core/extension/random_select.dart'; import 'package:eqmonitor/core/provider/config/earthquake_history/earthquake_history_config_provider.dart'; -import 'package:eqmonitor/core/provider/websocket/websocket_provider.dart'; import 'package:eqmonitor/feature/earthquake_history/data/model/earthquake_v1_extended.dart'; import 'package:eqmonitor/feature/earthquake_history_details/component/earthquake_hypo_info_widget.dart'; import 'package:eqmonitor/feature/earthquake_history_details/component/earthquake_map.dart'; @@ -22,7 +17,6 @@ import 'package:eqmonitor/feature/earthquake_history_details/component/prefectur import 'package:eqmonitor/feature/earthquake_history_details/data/earthquake_history_details_notifier.dart'; import 'package:eqmonitor/feature/settings/children/config/earthquake_history/earthquake_history_config_page.dart'; import 'package:extensions/extensions.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; @@ -203,6 +197,16 @@ class EarthquakeHistoryDetailsPage extends HookConsumerWidget { left: 0, child: SafeArea( child: IconButton.filledTonal( + style: ButtonStyle( + shape: MaterialStatePropertyAll( + RoundedRectangleBorder( + side: BorderSide( + color: colorScheme.primary.withOpacity(0.2), + ), + borderRadius: BorderRadius.circular(128), + ), + ), + ), icon: const Icon(Icons.arrow_back), onPressed: () => context.pop(), color: colorScheme.primary, @@ -233,7 +237,6 @@ class _Sheet extends StatelessWidget { useColumn: true, controller: sheetController, children: [ - if (kDebugMode) _DebugInserter(item: item), EarthquakeHypoInfoWidget(item: item), const Divider(), PrefectureIntensityWidget(item: item.v1), @@ -248,80 +251,6 @@ class _Sheet extends StatelessWidget { } } -class _DebugInserter extends StatelessWidget { - const _DebugInserter({ - required this.item, - }); - - final EarthquakeV1Extended item; - - @override - Widget build(BuildContext context) { - return Consumer( - builder: (context, ref, child) => FilledButton.tonal( - child: const Text('INSERT Earthquake'), - onPressed: () async { - final payload = RealtimePostgresUpdatePayload( - commitTimestamp: DateTime.now(), - errors: [], - newData: item.v1.copyWith( - status: TelegramStatus.training.type, - depth: 150, - arrivalTime: DateTime.now(), - originTime: DateTime.now(), - latitude: (item.latitude ?? 0) - 0.5 + Random().nextDouble(), - longitude: (item.longitude ?? 0) - 0.5 + Random().nextDouble(), - magnitude: Random().nextDouble() * 10, - maxIntensity: JmaIntensity.values.randomSelect, - maxLpgmIntensity: JmaLgIntensity.values.randomSelect, - intensityCities: item.v1.intensityCities - ?.map( - (e) => e.copyWith( - intensity: JmaIntensity.values.randomSelect, - ), - ) - .toList(), - intensityPrefectures: item.v1.intensityPrefectures - ?.map( - (e) => e.copyWith( - intensity: JmaIntensity.values.randomSelect, - ), - ) - .toList(), - intensityRegions: item.v1.intensityRegions - ?.map( - (e) => e.copyWith( - intensity: JmaIntensity.values.randomSelect, - ), - ) - .toList(), - intensityStations: item.v1.intensityStations - ?.map( - (e) => e.copyWith( - intensity: JmaIntensity.values.randomSelect, - ), - ) - .toList(), - ), - old: {}, - schema: 'public', - table: 'earthquake', - ); - ref.read(websocketMessagesProvider.notifier).emit( - jsonDecode( - jsonEncode({ - ...payload.toJson((p0) => p0.toJson()), - 'eventType': - RealtimePostgresChangesListenEvent.insert.value, - }), - ) as Map, - ); - }, - ), - ); - } -} - class _EarthquakeCommentWidget extends StatelessWidget { const _EarthquakeCommentWidget({required this.item}); @@ -329,7 +258,12 @@ class _EarthquakeCommentWidget extends StatelessWidget { @override Widget build(BuildContext context) { - final comment = item.text; + final comment = switch ((item.headline, item.text)) { + (final String headline, final String text) => '$headline\n$text', + (_, final String text) => text, + (final String headline, _) => headline, + _ => null, + }; if (comment != null) { return BorderedContainer( padding: const EdgeInsets.all(8),