diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 067399278..dbe9db7ff 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -791,9 +791,6 @@ "settingsCoordinateFormatTitle": "Coordinate Format", "@settingsCoordinateFormatTitle": {}, - "mapPageTitle": "Map", - "@mapPageTitle": {}, - "statsPageTitle": "Stats", "@statsPageTitle": {}, "statsImage": "{count, plural, =1{image} other{images}}", @@ -858,14 +855,16 @@ "viewerInfoLabelAddress": "Address", "@viewerInfoLabelAddress": {}, - "viewerInfoMapStyleTitle": "Map Style", - "@viewerInfoMapStyleTitle": {}, - "viewerInfoMapStyleTooltip": "Select map style", - "@viewerInfoMapStyleTooltip": {}, - "viewerInfoMapZoomInTooltip": "Zoom in", - "@viewerInfoMapZoomInTooltip": {}, - "viewerInfoMapZoomOutTooltip": "Zoom out", - "@viewerInfoMapZoomOutTooltip": {}, + "mapStyleTitle": "Map Style", + "@mapStyleTitle": {}, + "mapStyleTooltip": "Select map style", + "@mapStyleTooltip": {}, + "mapZoomInTooltip": "Zoom in", + "@mapZoomInTooltip": {}, + "mapZoomOutTooltip": "Zoom out", + "@mapZoomOutTooltip": {}, + "mapPointNorthUpTooltip": "Point north up", + "@mapPointNorthUpTooltip": {}, "mapAttributionOsmHot": "Map data © [OpenStreetMap](https://www.openstreetmap.org/copyright) contributors • Tiles by [HOT](https://www.hotosm.org/) • Hosted by [OSM France](https://openstreetmap.fr/)", "@mapAttributionOsmHot": {}, "mapAttributionStamen": "Map data © [OpenStreetMap](https://www.openstreetmap.org/copyright) contributors • Tiles by [Stamen Design](http://stamen.com), [CC BY 3.0](http://creativecommons.org/licenses/by/3.0)", diff --git a/lib/l10n/app_ko.arb b/lib/l10n/app_ko.arb index 7030d5d9b..6ad654cc4 100644 --- a/lib/l10n/app_ko.arb +++ b/lib/l10n/app_ko.arb @@ -389,8 +389,6 @@ "settingsCoordinateFormatTile": "좌표 표현", "settingsCoordinateFormatTitle": "좌표 표현", - "mapPageTitle": "지도", - "statsPageTitle": "통계", "statsImage": "{count, plural, other{사진}}", "statsVideo": "{count, plural, other{동영상}}", @@ -419,10 +417,11 @@ "viewerInfoLabelCoordinates": "좌표", "viewerInfoLabelAddress": "주소", - "viewerInfoMapStyleTitle": "지도 유형", - "viewerInfoMapStyleTooltip": "지도 유형 선택", - "viewerInfoMapZoomInTooltip": "확대", - "viewerInfoMapZoomOutTooltip": "축소", + "mapStyleTitle": "지도 유형", + "mapStyleTooltip": "지도 유형 선택", + "mapZoomInTooltip": "확대", + "mapZoomOutTooltip": "축소", + "mapPointNorthUpTooltip": "북쪽을 위로 가리키기", "mapAttributionOsmHot": "지도 데이터 © [OpenStreetMap](https://www.openstreetmap.org/copyright) 기여자 • 타일 [HOT](https://www.hotosm.org/) • 호스팅 [OSM France](https://openstreetmap.fr/)", "mapAttributionStamen": "지도 데이터 © [OpenStreetMap](https://www.openstreetmap.org/copyright) 기여자 • 타일 [Stamen Design](http://stamen.com), [CC BY 3.0](http://creativecommons.org/licenses/by/3.0)", diff --git a/lib/widgets/common/map/buttons.dart b/lib/widgets/common/map/buttons.dart index c837bfdbc..5112810a5 100644 --- a/lib/widgets/common/map/buttons.dart +++ b/lib/widgets/common/map/buttons.dart @@ -18,6 +18,7 @@ import 'package:flutter/scheduler.dart'; import 'package:latlong2/latlong.dart'; class MapButtonPanel extends StatelessWidget { + final bool showBackButton; final ValueNotifier boundsNotifier; final Future Function(double amount)? zoomBy; final VoidCallback? resetRotation; @@ -26,6 +27,7 @@ class MapButtonPanel extends StatelessWidget { const MapButtonPanel({ Key? key, + required this.showBackButton, required this.boundsNotifier, this.zoomBy, this.resetRotation, @@ -46,34 +48,51 @@ class MapButtonPanel extends StatelessWidget { ), child: Stack( children: [ - if (resetRotation != null) - Positioned( - left: 0, - child: ValueListenableBuilder( - valueListenable: boundsNotifier, - builder: (context, bounds, child) { - final degrees = bounds.rotation; - return AnimatedOpacity( - opacity: degrees == 0 ? 0 : 1, - duration: Durations.viewerOverlayAnimation, - child: MapOverlayButton( - icon: Transform( - origin: iconSize.center(Offset.zero), - transform: Matrix4.rotationZ(degToRadian(degrees)), - child: CustomPaint( - painter: CompassPainter( - color: iconTheme.color!, + Positioned( + left: 0, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (showBackButton) + MapOverlayButton( + icon: const BackButtonIcon(), + onPressed: () => Navigator.pop(context), + tooltip: MaterialLocalizations.of(context).backButtonTooltip, + ), + if (resetRotation != null) ...[ + const SizedBox(height: padding), + ValueListenableBuilder( + valueListenable: boundsNotifier, + builder: (context, bounds, child) { + final degrees = bounds.rotation; + final opacity = degrees == 0 ? .0 : 1.0; + return IgnorePointer( + ignoring: opacity == 0, + child: AnimatedOpacity( + opacity: opacity, + duration: Durations.viewerOverlayAnimation, + child: MapOverlayButton( + icon: Transform( + origin: iconSize.center(Offset.zero), + transform: Matrix4.rotationZ(degToRadian(degrees)), + child: CustomPaint( + painter: CompassPainter( + color: iconTheme.color!, + ), + size: iconSize, + ), + ), + onPressed: () => resetRotation?.call(), + tooltip: context.l10n.mapPointNorthUpTooltip, ), - size: iconSize, ), - ), - onPressed: () => resetRotation?.call(), - tooltip: context.l10n.viewerInfoMapZoomInTooltip, - ), - ); - }, - ), + ); + }, + ), + ], + ], ), + ), Positioned( right: 0, child: Column( @@ -100,7 +119,7 @@ class MapButtonPanel extends StatelessWidget { return AvesSelectionDialog( initialValue: initialStyle, options: Map.fromEntries(availableStyles.map((v) => MapEntry(v, v.getName(context)))), - title: context.l10n.viewerInfoMapStyleTitle, + title: context.l10n.mapStyleTitle, ); }, ); @@ -110,7 +129,7 @@ class MapButtonPanel extends StatelessWidget { settings.infoMapStyle = style; } }, - tooltip: context.l10n.viewerInfoMapStyleTooltip, + tooltip: context.l10n.mapStyleTooltip, ), ], ), @@ -124,13 +143,13 @@ class MapButtonPanel extends StatelessWidget { MapOverlayButton( icon: const Icon(AIcons.zoomIn), onPressed: zoomBy != null ? () => zoomBy?.call(1) : null, - tooltip: context.l10n.viewerInfoMapZoomInTooltip, + tooltip: context.l10n.mapZoomInTooltip, ), const SizedBox(height: padding), MapOverlayButton( icon: const Icon(AIcons.zoomOut), onPressed: zoomBy != null ? () => zoomBy?.call(-1) : null, - tooltip: context.l10n.viewerInfoMapZoomOutTooltip, + tooltip: context.l10n.mapZoomOutTooltip, ), ], ), diff --git a/lib/widgets/common/map/geo_map.dart b/lib/widgets/common/map/geo_map.dart index 7c6021dc9..b6f7efd5e 100644 --- a/lib/widgets/common/map/geo_map.dart +++ b/lib/widgets/common/map/geo_map.dart @@ -27,7 +27,7 @@ import 'package:provider/provider.dart'; class GeoMap extends StatefulWidget { final AvesMapController? controller; final List entries; - final bool interactive; + final bool interactive, showBackButton; final double? mapHeight; final ValueNotifier isAnimatingNotifier; final UserZoomChangeCallback? onUserZoomChange; @@ -41,6 +41,7 @@ class GeoMap extends StatefulWidget { this.controller, required this.entries, required this.interactive, + required this.showBackButton, this.mapHeight, required this.isAnimatingNotifier, this.onUserZoomChange, @@ -65,6 +66,8 @@ class _GeoMapState extends State { bool get interactive => widget.interactive; + bool get showBackButton => widget.showBackButton; + double? get mapHeight => widget.mapHeight; @override @@ -137,6 +140,7 @@ class _GeoMapState extends State { controller: widget.controller, boundsNotifier: _boundsNotifier, interactive: interactive, + showBackButton: showBackButton, minZoom: 0, maxZoom: 20, style: mapStyle, @@ -149,6 +153,7 @@ class _GeoMapState extends State { controller: widget.controller, boundsNotifier: _boundsNotifier, interactive: interactive, + showBackButton: showBackButton, minZoom: 2, maxZoom: 16, style: mapStyle, @@ -191,6 +196,7 @@ class _GeoMapState extends State { interactive: interactive, ), MapButtonPanel( + showBackButton: showBackButton, boundsNotifier: _boundsNotifier, ), ], diff --git a/lib/widgets/common/map/google/map.dart b/lib/widgets/common/map/google/map.dart index 6a8a310f6..ec21a072c 100644 --- a/lib/widgets/common/map/google/map.dart +++ b/lib/widgets/common/map/google/map.dart @@ -18,7 +18,7 @@ import 'package:latlong2/latlong.dart' as ll; class EntryGoogleMap extends StatefulWidget { final AvesMapController? controller; final ValueNotifier boundsNotifier; - final bool interactive; + final bool interactive, showBackButton; final double? minZoom, maxZoom; final EntryMapStyle style; final MarkerClusterBuilder markerClusterBuilder; @@ -31,6 +31,7 @@ class EntryGoogleMap extends StatefulWidget { this.controller, required this.boundsNotifier, required this.interactive, + required this.showBackButton, this.minZoom, this.maxZoom, required this.style, @@ -126,6 +127,7 @@ class _EntryGoogleMapState extends State with WidgetsBindingObse child: _buildMap(), ), MapButtonPanel( + showBackButton: widget.showBackButton, boundsNotifier: boundsNotifier, zoomBy: _zoomBy, resetRotation: interactive ? _resetRotation : null, diff --git a/lib/widgets/common/map/leaflet/map.dart b/lib/widgets/common/map/leaflet/map.dart index 508ffd707..d35daed2a 100644 --- a/lib/widgets/common/map/leaflet/map.dart +++ b/lib/widgets/common/map/leaflet/map.dart @@ -19,7 +19,7 @@ import 'package:latlong2/latlong.dart'; class EntryLeafletMap extends StatefulWidget { final AvesMapController? controller; final ValueNotifier boundsNotifier; - final bool interactive; + final bool interactive, showBackButton; final double minZoom, maxZoom; final EntryMapStyle style; final MarkerClusterBuilder markerClusterBuilder; @@ -33,6 +33,7 @@ class EntryLeafletMap extends StatefulWidget { this.controller, required this.boundsNotifier, required this.interactive, + required this.showBackButton, this.minZoom = 0, this.maxZoom = 22, required this.style, @@ -107,6 +108,7 @@ class _EntryLeafletMapState extends State with TickerProviderSt child: _buildMap(), ), MapButtonPanel( + showBackButton: widget.showBackButton, boundsNotifier: boundsNotifier, zoomBy: _zoomBy, resetRotation: interactive ? _resetRotation : null, diff --git a/lib/widgets/map/map_page.dart b/lib/widgets/map/map_page.dart index 3dfb0c6d9..df11c0af0 100644 --- a/lib/widgets/map/map_page.dart +++ b/lib/widgets/map/map_page.dart @@ -3,7 +3,6 @@ import 'package:aves/model/settings/map_style.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/utils/debouncer.dart'; -import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/map/controller.dart'; import 'package:aves/widgets/common/map/geo_map.dart'; import 'package:aves/widgets/common/providers/media_query_data_provider.dart'; @@ -61,9 +60,6 @@ class _MapPageState extends State { Widget build(BuildContext context) { return MediaQueryDataProvider( child: Scaffold( - appBar: AppBar( - title: Text(context.l10n.mapPageTitle), - ), body: SafeArea( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -73,6 +69,7 @@ class _MapPageState extends State { controller: _mapController, entries: entries, interactive: true, + showBackButton: true, isAnimatingNotifier: _isAnimatingNotifier, onMarkerTap: (markerEntry, getClusterEntries) { final index = entries.indexOf(markerEntry); diff --git a/lib/widgets/viewer/info/location_section.dart b/lib/widgets/viewer/info/location_section.dart index dfbba1c64..56290a20b 100644 --- a/lib/widgets/viewer/info/location_section.dart +++ b/lib/widgets/viewer/info/location_section.dart @@ -85,6 +85,7 @@ class _LocationSectionState extends State { GeoMap( entries: [entry], interactive: false, + showBackButton: false, mapHeight: 200, isAnimatingNotifier: widget.isScrollingNotifier, onUserZoomChange: (zoom) => settings.infoMapZoom = zoom,