From 1535f6165327ad881b4b345a74576d45b6d110be Mon Sep 17 00:00:00 2001 From: ashilkn Date: Tue, 28 May 2024 14:01:12 +0530 Subject: [PATCH 1/8] [mob][photos] upgrade to flutter 3.22.0 --- .github/workflows/mobile-lint.yml | 3 +- mobile/README.md | 2 +- mobile/ios/Podfile.lock | 2 +- mobile/pubspec.lock | 54 +++++++++++++++---------------- mobile/pubspec.yaml | 13 ++++---- 5 files changed, 38 insertions(+), 36 deletions(-) diff --git a/.github/workflows/mobile-lint.yml b/.github/workflows/mobile-lint.yml index 3efebf4f2d..8f231079f3 100644 --- a/.github/workflows/mobile-lint.yml +++ b/.github/workflows/mobile-lint.yml @@ -9,7 +9,8 @@ on: - ".github/workflows/mobile-lint.yml" env: - FLUTTER_VERSION: "3.19.4" + + FLUTTER_VERSION: "3.22.0" jobs: lint: diff --git a/mobile/README.md b/mobile/README.md index 4a4579adfc..6d86ad5344 100644 --- a/mobile/README.md +++ b/mobile/README.md @@ -46,7 +46,7 @@ You can alternatively install the build from PlayStore or F-Droid. ## 🧑‍💻 Building from source -1. [Install Flutter v3.19.4](https://flutter.dev/docs/get-started/install). +1. [Install Flutter v3.22.0](https://flutter.dev/docs/get-started/install). 2. Pull in all submodules with `git submodule update --init --recursive` diff --git a/mobile/ios/Podfile.lock b/mobile/ios/Podfile.lock index 558a279108..9f74d552a7 100644 --- a/mobile/ios/Podfile.lock +++ b/mobile/ios/Podfile.lock @@ -427,7 +427,7 @@ SPEC CHECKSUMS: home_widget: 0434835a4c9a75704264feff6be17ea40e0f0d57 image_editor_common: d6f6644ae4a6de80481e89fe6d0a8c49e30b4b43 in_app_purchase_storekit: 0e4b3c2e43ba1e1281f4f46dd71b0593ce529892 - integration_test: 13825b8a9334a850581300559b8839134b124670 + integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4 libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009 local_auth_darwin: c7e464000a6a89e952235699e32b329457608d98 local_auth_ios: 5046a18c018dd973247a0564496c8898dbb5adf9 diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 1d1082bfd3..8b71025e9f 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -45,10 +45,10 @@ packages: dependency: "direct main" description: name: animated_list_plus - sha256: fe66f9c300d715254727fbdf050487844d17b013fec344fa28081d29bddbdf1a + sha256: fb3d7f1fbaf5af84907f3c739236bacda8bf32cbe1f118dd51510752883ff50c url: "https://pub.dev" source: hosted - version: "0.4.5" + version: "0.5.2" animated_stack_widget: dependency: transitive description: @@ -971,10 +971,10 @@ packages: dependency: "direct main" description: name: home_widget - sha256: "29565bfee4b32eaf9e7e8b998d504618b779a74b2b1ac62dd4dac7468e66f1a3" + sha256: "2a0fdd6267ff975bd07bedf74686bd5577200f504f5de36527ac1b56bdbe68e3" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.6.0" html: dependency: transitive description: @@ -1152,26 +1152,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" like_button: dependency: "direct main" description: @@ -1368,10 +1368,10 @@ packages: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" mgrs_dart: dependency: transitive description: @@ -2144,10 +2144,10 @@ packages: dependency: "direct main" description: name: styled_text - sha256: f72928d1ebe8cb149e3b34a689cb1ddca696b808187cf40ac3a0bd183dff379c + sha256: fd624172cf629751b4f171dd0ecf9acf02a06df3f8a81bb56c0caa4f1df706c3 url: "https://pub.dev" source: hosted - version: "7.0.0" + version: "8.1.0" sync_http: dependency: transitive description: @@ -2160,18 +2160,18 @@ packages: dependency: "direct main" description: name: syncfusion_flutter_core - sha256: "9be1bb9bbdb42823439a18da71484f1964c14dbe1c255ab1b931932b12fa96e8" + sha256: "63108a33f9b0d89f7b6b56cce908b8e519fe433dbbe0efcf41ad3e8bb2081bd9" url: "https://pub.dev" source: hosted - version: "19.4.56" + version: "25.2.5" syncfusion_flutter_sliders: dependency: "direct main" description: name: syncfusion_flutter_sliders - sha256: "1f6a63ccab4180b544074b9264a20f01ee80b553de154192fe1d7b434089d3c2" + sha256: f27310bedc0e96e84054f0a70ac593d1a3c38397c158c5226ba86027ad77b2c1 url: "https://pub.dev" source: hosted - version: "19.4.56" + version: "25.2.5" synchronized: dependency: "direct main" description: @@ -2192,26 +2192,26 @@ packages: dependency: "direct dev" description: name: test - sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f + sha256: "7ee446762c2c50b3bd4ea96fe13ffac69919352bd3b4b17bac3f3465edc58073" url: "https://pub.dev" source: hosted - version: "1.24.9" + version: "1.25.2" test_api: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" test_core: dependency: transitive description: name: test_core - sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a + sha256: "2bc4b4ecddd75309300d8096f781c0e3280ca1ef85beda558d33fcbedc2eead4" url: "https://pub.dev" source: hosted - version: "0.5.9" + version: "0.6.0" timezone: dependency: transitive description: @@ -2441,10 +2441,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.1" volume_controller: dependency: transitive description: @@ -2591,4 +2591,4 @@ packages: version: "3.1.2" sdks: dart: ">=3.3.0 <4.0.0" - flutter: ">=3.19.0" + flutter: ">=3.20.0-1.2.pre" diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 934a99a8f0..ed3bf47193 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -21,7 +21,7 @@ environment: dependencies: adaptive_theme: ^3.1.0 animate_do: ^2.0.0 - animated_list_plus: ^0.4.5 + animated_list_plus: ^0.5.2 archive: ^3.1.2 background_fetch: ^1.2.1 battery_info: ^1.1.1 @@ -93,13 +93,13 @@ dependencies: fluttertoast: ^8.0.6 freezed_annotation: ^2.4.1 google_nav_bar: ^5.0.5 - home_widget: ^0.5.0 + home_widget: ^0.6.0 html_unescape: ^2.0.0 http: ^1.1.0 image: ^4.0.17 image_editor: ^1.3.0 in_app_purchase: ^3.0.7 - intl: ^0.18.0 + intl: ^0.19.0 json_annotation: ^4.8.0 latlong2: ^0.9.0 like_button: ^2.0.5 @@ -152,9 +152,9 @@ dependencies: sqlite3_flutter_libs: ^0.5.20 sqlite_async: ^0.6.1 step_progress_indicator: ^1.0.2 - styled_text: ^7.0.0 - syncfusion_flutter_core: ^19.2.49 - syncfusion_flutter_sliders: ^19.2.49 + styled_text: ^8.1.0 + syncfusion_flutter_core: ^25.2.5 + syncfusion_flutter_sliders: ^25.2.5 synchronized: ^3.1.0 tuple: ^2.0.0 uni_links: ^0.5.1 @@ -177,6 +177,7 @@ dependency_overrides: # Remove this after removing dependency from flutter_sodium. # Newer flutter packages depends on ffi > 2.0.0 while flutter_sodium depends on ffi < 2.0.0 ffi: 2.1.0 + intl: 0.18.1 video_player: git: url: https://github.com/ente-io/packages.git From 284bca782e737c9dc39ef419243e8a5e9b0e159a Mon Sep 17 00:00:00 2001 From: ashilkn Date: Tue, 28 May 2024 14:01:58 +0530 Subject: [PATCH 2/8] [mob][photos] Update flutter version in internal release workflow --- .github/workflows/mobile-internal-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mobile-internal-release.yml b/.github/workflows/mobile-internal-release.yml index 7b0696c1bd..fac4eb1d2f 100644 --- a/.github/workflows/mobile-internal-release.yml +++ b/.github/workflows/mobile-internal-release.yml @@ -4,7 +4,7 @@ on: workflow_dispatch: # Allow manually running the action env: - FLUTTER_VERSION: "3.19.4" + FLUTTER_VERSION: "3.22.0" jobs: build: From cb0cffce3d8411c7f2d3d7ac7c193d87a57080b4 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Tue, 28 May 2024 16:28:39 +0530 Subject: [PATCH 3/8] [mob][photos] Migrating to flutter_map v6 (1) --- mobile/lib/ui/map/map_view.dart | 35 +- .../map/tile/attribution/map_attribution.dart | 544 +++++++++--------- mobile/lib/ui/map/tile/layers.dart | 58 +- 3 files changed, 311 insertions(+), 326 deletions(-) diff --git a/mobile/lib/ui/map/map_view.dart b/mobile/lib/ui/map/map_view.dart index 15b5c1d8b9..2a8c0ed4c7 100644 --- a/mobile/lib/ui/map/map_view.dart +++ b/mobile/lib/ui/map/map_view.dart @@ -60,11 +60,6 @@ class _MapViewState extends State { _markers = _buildMakers(); } - @override - void dispose() { - super.dispose(); - } - void onChange(LatLngBounds bounds) { _debouncer.run( () async { @@ -85,37 +80,31 @@ class _MapViewState extends State { widget.onTap!.call(); } : null, - center: widget.center, + initialCenter: widget.center, minZoom: widget.minZoom, maxZoom: widget.maxZoom, - enableMultiFingerGestureRace: true, - zoom: widget.initialZoom, - maxBounds: LatLngBounds( - const LatLng(-90, -180), - const LatLng(90, 180), + interactionOptions: InteractionOptions( + flags: widget.interactiveFlags, + enableMultiFingerGestureRace: true, + ), + initialZoom: widget.initialZoom, + cameraConstraint: CameraConstraint.contain( + bounds: LatLngBounds( + const LatLng(-90, -180), + const LatLng(90, 180), + ), ), onPositionChanged: (position, hasGesture) { if (position.bounds != null) { onChange(position.bounds!); } }, - interactiveFlags: widget.interactiveFlags, ), - nonRotatedChildren: [ - Padding( - padding: EdgeInsets.only( - bottom: widget.bottomSheetDraggableAreaHeight, - ), - child: OSMFranceTileAttributes( - options: widget.mapAttributionOptions, - ), - ), - ], children: [ const OSMFranceTileLayer(), MarkerClusterLayerWidget( options: MarkerClusterLayerOptions( - anchorPos: AnchorPos.align(AnchorAlign.top), + alignment: Alignment.topCenter, maxClusterRadius: 100, showPolygon: false, size: widget.markerSize, diff --git a/mobile/lib/ui/map/tile/attribution/map_attribution.dart b/mobile/lib/ui/map/tile/attribution/map_attribution.dart index e00e1a3e64..370883bf08 100644 --- a/mobile/lib/ui/map/tile/attribution/map_attribution.dart +++ b/mobile/lib/ui/map/tile/attribution/map_attribution.dart @@ -1,296 +1,296 @@ -// ignore_for_file: invalid_use_of_internal_member +// // ignore_for_file: invalid_use_of_internal_member -import "dart:async"; +// import "dart:async"; -import "package:flutter/material.dart"; -import "package:flutter_map/plugin_api.dart"; -import "package:photos/extensions/list.dart"; -import "package:photos/theme/colors.dart"; -import "package:photos/theme/ente_theme.dart"; -import "package:photos/ui/components/buttons/icon_button_widget.dart"; +// import "package:flutter/material.dart"; +// import "package:flutter_map/plugin_api.dart"; +// import "package:photos/extensions/list.dart"; +// import "package:photos/theme/colors.dart"; +// import "package:photos/theme/ente_theme.dart"; +// import "package:photos/ui/components/buttons/icon_button_widget.dart"; -// Credit: This code is based on the Rich Attribution widget from the flutter_map -class MapAttributionWidget extends StatefulWidget { - /// List of attributions to display - /// - /// [TextSourceAttribution]s are shown in a popup box (toggled by a tap/click - /// on the [openButton]/[closeButton]), unlike [LogoSourceAttribution], which - /// are visible permanently adjacent to the open/close button. - final List attributions; +// // Credit: This code is based on the Rich Attribution widget from the flutter_map +// class MapAttributionWidget extends StatefulWidget { +// /// List of attributions to display +// /// +// /// [TextSourceAttribution]s are shown in a popup box (toggled by a tap/click +// /// on the [openButton]/[closeButton]), unlike [LogoSourceAttribution], which +// /// are visible permanently adjacent to the open/close button. +// final List attributions; - /// The position in which to anchor this widget - final AttributionAlignment alignment; +// /// The position in which to anchor this widget +// final AttributionAlignment alignment; - /// The widget (usually an [IconButton]) to display when the popup box is - /// closed, that opens the popup box via the `open` callback - final Widget Function(BuildContext context, VoidCallback open)? openButton; +// /// The widget (usually an [IconButton]) to display when the popup box is +// /// closed, that opens the popup box via the `open` callback +// final Widget Function(BuildContext context, VoidCallback open)? openButton; - /// The widget (usually an [IconButton]) to display when the popup box is open, - /// that closes the popup box via the `close` callback - final Widget Function(BuildContext context, VoidCallback close)? closeButton; +// /// The widget (usually an [IconButton]) to display when the popup box is open, +// /// that closes the popup box via the `close` callback +// final Widget Function(BuildContext context, VoidCallback close)? closeButton; - /// The color to use as the popup box's background color, defaulting to the - /// [Theme]s background color - final Color? popupBackgroundColor; +// /// The color to use as the popup box's background color, defaulting to the +// /// [Theme]s background color +// final Color? popupBackgroundColor; - /// The radius of the edges of the popup box - final BorderRadius? popupBorderRadius; +// /// The radius of the edges of the popup box +// final BorderRadius? popupBorderRadius; - /// The height of the permanent row in which is found the popup menu toggle - /// button - /// - /// Also determines spacing between the items within the row. - /// - /// Also set [LogoSourceAttribution.height] to the same value, if adjusted. - final double permanentHeight; +// /// The height of the permanent row in which is found the popup menu toggle +// /// button +// /// +// /// Also determines spacing between the items within the row. +// /// +// /// Also set [LogoSourceAttribution.height] to the same value, if adjusted. +// final double permanentHeight; - /// Whether to add an additional attribution logo and text for 'flutter_map' - final bool showFlutterMapAttribution; +// /// Whether to add an additional attribution logo and text for 'flutter_map' +// final bool showFlutterMapAttribution; - /// Animation configuration, through the properties and handler/builder - /// defined by a [RichAttributionWidgetAnimation] implementation - /// - /// Can be extensivley customized by implementing a custom - /// [RichAttributionWidgetAnimation], or the prebuilt [FadeRAWA] and - /// [ScaleRAWA] animations can be used with limited customization. - final RichAttributionWidgetAnimation animationConfig; +// /// Animation configuration, through the properties and handler/builder +// /// defined by a [RichAttributionWidgetAnimation] implementation +// /// +// /// Can be extensivley customized by implementing a custom +// /// [RichAttributionWidgetAnimation], or the prebuilt [FadeRAWA] and +// /// [ScaleRAWA] animations can be used with limited customization. +// final RichAttributionWidgetAnimation animationConfig; - /// If not [Duration.zero] (default), the popup box will be open by default and - /// hidden this long after the map is initialised - /// - /// This is useful with certain sources/tile servers that make immediate - /// attribution mandatory and are not attributed with a permanently visible - /// [LogoSourceAttribution]. - final Duration popupInitialDisplayDuration; +// /// If not [Duration.zero] (default), the popup box will be open by default and +// /// hidden this long after the map is initialised +// /// +// /// This is useful with certain sources/tile servers that make immediate +// /// attribution mandatory and are not attributed with a permanently visible +// /// [LogoSourceAttribution]. +// final Duration popupInitialDisplayDuration; - /// A prebuilt dynamic attribution layer that supports both logos and text - /// through [SourceAttribution]s - /// - /// [TextSourceAttribution]s are shown in a popup box that can be visible or - /// invisible. Its state is toggled by a tri-state [openButton]/[closeButton] : - /// 1. Not hovered, not opened: faded button, invisible box - /// 2. Hovered, not opened: full opacity button, invisible box - /// 3. Opened: full opacity button, visible box - /// - /// The hover state on mobile devices is unspecified, but the behaviour is - /// usually inconsequential on mobile devices anyway, due to the fingertip - /// covering the entire button. - /// - /// [LogoSourceAttribution]s are shown adjacent to the open/close button, to - /// comply with some stricter tile server requirements (such as Mapbox). These - /// are usually supplemented with a [TextSourceAttribution]. - /// - /// The popup box also closes automatically on any interaction with the map. - /// - /// Animations are built in by default, and configured/handled through - /// [RichAttributionWidgetAnimation] - see that class and the [animationConfig] - /// property for more information. By default, a simple fade/opacity animation - /// is provided by [FadeRAWA]. [ScaleRAWA] is also available. - /// - /// Read the documentation on the individual properties for more information - /// and customizability. +// /// A prebuilt dynamic attribution layer that supports both logos and text +// /// through [SourceAttribution]s +// /// +// /// [TextSourceAttribution]s are shown in a popup box that can be visible or +// /// invisible. Its state is toggled by a tri-state [openButton]/[closeButton] : +// /// 1. Not hovered, not opened: faded button, invisible box +// /// 2. Hovered, not opened: full opacity button, invisible box +// /// 3. Opened: full opacity button, visible box +// /// +// /// The hover state on mobile devices is unspecified, but the behaviour is +// /// usually inconsequential on mobile devices anyway, due to the fingertip +// /// covering the entire button. +// /// +// /// [LogoSourceAttribution]s are shown adjacent to the open/close button, to +// /// comply with some stricter tile server requirements (such as Mapbox). These +// /// are usually supplemented with a [TextSourceAttribution]. +// /// +// /// The popup box also closes automatically on any interaction with the map. +// /// +// /// Animations are built in by default, and configured/handled through +// /// [RichAttributionWidgetAnimation] - see that class and the [animationConfig] +// /// property for more information. By default, a simple fade/opacity animation +// /// is provided by [FadeRAWA]. [ScaleRAWA] is also available. +// /// +// /// Read the documentation on the individual properties for more information +// /// and customizability. - final double iconSize; - const MapAttributionWidget({ - super.key, - required this.attributions, - this.alignment = AttributionAlignment.bottomRight, - this.openButton, - this.closeButton, - this.popupBackgroundColor, - this.popupBorderRadius, - this.permanentHeight = 24, - this.showFlutterMapAttribution = true, - this.animationConfig = const FadeRAWA(), - this.popupInitialDisplayDuration = Duration.zero, - this.iconSize = 20, - }); +// final double iconSize; +// const MapAttributionWidget({ +// super.key, +// required this.attributions, +// this.alignment = AttributionAlignment.bottomRight, +// this.openButton, +// this.closeButton, +// this.popupBackgroundColor, +// this.popupBorderRadius, +// this.permanentHeight = 24, +// this.showFlutterMapAttribution = true, +// this.animationConfig = const FadeRAWA(), +// this.popupInitialDisplayDuration = Duration.zero, +// this.iconSize = 20, +// }); - @override - State createState() => MapAttributionWidgetState(); -} +// @override +// State createState() => MapAttributionWidgetState(); +// } -class MapAttributionWidgetState extends State { - StreamSubscription? mapEventSubscription; +// class MapAttributionWidgetState extends State { +// StreamSubscription? mapEventSubscription; - final persistentAttributionKey = GlobalKey(); - Size? persistentAttributionSize; +// final persistentAttributionKey = GlobalKey(); +// Size? persistentAttributionSize; - late bool popupExpanded = widget.popupInitialDisplayDuration != Duration.zero; - bool persistentHovered = false; +// late bool popupExpanded = widget.popupInitialDisplayDuration != Duration.zero; +// bool persistentHovered = false; - @override - void initState() { - super.initState(); +// @override +// void initState() { +// super.initState(); - if (popupExpanded) { - Future.delayed( - widget.popupInitialDisplayDuration, - () => setState(() => popupExpanded = false), - ); - } +// if (popupExpanded) { +// Future.delayed( +// widget.popupInitialDisplayDuration, +// () => setState(() => popupExpanded = false), +// ); +// } - WidgetsBinding.instance.addPostFrameCallback( - (_) => WidgetsBinding.instance.addPostFrameCallback((_) { - if (mounted) { - setState( - () => persistentAttributionSize = - (persistentAttributionKey.currentContext!.findRenderObject() - as RenderBox) - .size, - ); - } - }), - ); - } +// WidgetsBinding.instance.addPostFrameCallback( +// (_) => WidgetsBinding.instance.addPostFrameCallback((_) { +// if (mounted) { +// setState( +// () => persistentAttributionSize = +// (persistentAttributionKey.currentContext!.findRenderObject() +// as RenderBox) +// .size, +// ); +// } +// }), +// ); +// } - @override - void dispose() { - mapEventSubscription?.cancel(); - super.dispose(); - } +// @override +// void dispose() { +// mapEventSubscription?.cancel(); +// super.dispose(); +// } - @override - Widget build(BuildContext context) { - final persistentAttributionItems = [ - ...List.from( - widget.attributions.whereType(), - growable: false, - ).interleave(SizedBox(width: widget.permanentHeight / 1.5)), - if (widget.showFlutterMapAttribution) - LogoSourceAttribution( - Image.asset( - 'lib/assets/flutter_map_logo.png', - package: 'flutter_map', - ), - tooltip: 'flutter_map', - height: widget.permanentHeight, - ), - SizedBox(width: widget.permanentHeight * 0.1), - AnimatedSwitcher( - switchInCurve: widget.animationConfig.buttonCurve, - switchOutCurve: widget.animationConfig.buttonCurve, - duration: widget.animationConfig.buttonDuration, - child: popupExpanded - ? (widget.closeButton ?? - (context, close) => IconButtonWidget( - size: widget.iconSize, - onTap: close, - icon: Icons.cancel_outlined, - iconButtonType: IconButtonType.primary, - iconColor: getEnteColorScheme(context).strokeBase, - ))( - context, - () => setState(() => popupExpanded = false), - ) - : (widget.openButton ?? - (context, open) => IconButtonWidget( - size: widget.iconSize, - onTap: open, - icon: Icons.info_outlined, - iconButtonType: IconButtonType.primary, - iconColor: strokeBaseLight, - ))( - context, - () { - setState(() => popupExpanded = true); - mapEventSubscription = FlutterMapState.of(context) - .mapController - .mapEventStream - .listen((e) { - setState(() => popupExpanded = false); - mapEventSubscription?.cancel(); - }); - }, - ), - ), - ]; +// @override +// Widget build(BuildContext context) { +// final persistentAttributionItems = [ +// ...List.from( +// widget.attributions.whereType(), +// growable: false, +// ).interleave(SizedBox(width: widget.permanentHeight / 1.5)), +// if (widget.showFlutterMapAttribution) +// LogoSourceAttribution( +// Image.asset( +// 'lib/assets/flutter_map_logo.png', +// package: 'flutter_map', +// ), +// tooltip: 'flutter_map', +// height: widget.permanentHeight, +// ), +// SizedBox(width: widget.permanentHeight * 0.1), +// AnimatedSwitcher( +// switchInCurve: widget.animationConfig.buttonCurve, +// switchOutCurve: widget.animationConfig.buttonCurve, +// duration: widget.animationConfig.buttonDuration, +// child: popupExpanded +// ? (widget.closeButton ?? +// (context, close) => IconButtonWidget( +// size: widget.iconSize, +// onTap: close, +// icon: Icons.cancel_outlined, +// iconButtonType: IconButtonType.primary, +// iconColor: getEnteColorScheme(context).strokeBase, +// ))( +// context, +// () => setState(() => popupExpanded = false), +// ) +// : (widget.openButton ?? +// (context, open) => IconButtonWidget( +// size: widget.iconSize, +// onTap: open, +// icon: Icons.info_outlined, +// iconButtonType: IconButtonType.primary, +// iconColor: strokeBaseLight, +// ))( +// context, +// () { +// setState(() => popupExpanded = true); +// mapEventSubscription = FlutterMapState.of(context) +// .mapController +// .mapEventStream +// .listen((e) { +// setState(() => popupExpanded = false); +// mapEventSubscription?.cancel(); +// }); +// }, +// ), +// ), +// ]; - return LayoutBuilder( - builder: (context, constraints) => Align( - alignment: widget.alignment.real, - child: Stack( - alignment: widget.alignment.real, - children: [ - if (persistentAttributionSize != null) - Padding( - padding: const EdgeInsets.all(6), - child: AnimatedScale( - scale: popupExpanded ? 1 : 0, - duration: const Duration(milliseconds: 200), - curve: popupExpanded ? Curves.easeOut : Curves.easeIn, - alignment: widget.alignment.real, - child: Container( - decoration: BoxDecoration( - color: widget.popupBackgroundColor ?? - Theme.of(context).colorScheme.background, - border: Border.all(width: 0, style: BorderStyle.none), - borderRadius: widget.popupBorderRadius ?? - BorderRadius.only( - topLeft: const Radius.circular(10), - topRight: const Radius.circular(10), - bottomLeft: widget.alignment == - AttributionAlignment.bottomLeft - ? Radius.zero - : const Radius.circular(10), - bottomRight: widget.alignment == - AttributionAlignment.bottomRight - ? Radius.zero - : const Radius.circular(10), - ), - ), - constraints: BoxConstraints( - minWidth: constraints.maxWidth < 420 - ? constraints.maxWidth - : persistentAttributionSize!.width, - ), - child: Padding( - padding: const EdgeInsets.all(8), - child: SizedBox( - height: widget.attributions.length * 32, - child: Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ...widget.attributions - .whereType(), - SizedBox( - height: (widget.permanentHeight - 24) + 32, - ), - ], - ), - ), - ), - ), - ), - ), - MouseRegion( - key: persistentAttributionKey, - onEnter: (_) => setState(() => persistentHovered = true), - onExit: (_) => setState(() => persistentHovered = false), - cursor: SystemMouseCursors.click, - child: AnimatedOpacity( - opacity: persistentHovered || popupExpanded ? 1 : 0.5, - curve: widget.animationConfig.buttonCurve, - duration: widget.animationConfig.buttonDuration, - child: Padding( - padding: const EdgeInsets.all(4), - child: FittedBox( - child: Row( - mainAxisSize: MainAxisSize.min, - children: - widget.alignment == AttributionAlignment.bottomLeft - ? persistentAttributionItems.reversed.toList() - : persistentAttributionItems, - ), - ), - ), - ), - ), - ], - ), - ), - ); - } -} +// return LayoutBuilder( +// builder: (context, constraints) => Align( +// alignment: widget.alignment.real, +// child: Stack( +// alignment: widget.alignment.real, +// children: [ +// if (persistentAttributionSize != null) +// Padding( +// padding: const EdgeInsets.all(6), +// child: AnimatedScale( +// scale: popupExpanded ? 1 : 0, +// duration: const Duration(milliseconds: 200), +// curve: popupExpanded ? Curves.easeOut : Curves.easeIn, +// alignment: widget.alignment.real, +// child: Container( +// decoration: BoxDecoration( +// color: widget.popupBackgroundColor ?? +// Theme.of(context).colorScheme.background, +// border: Border.all(width: 0, style: BorderStyle.none), +// borderRadius: widget.popupBorderRadius ?? +// BorderRadius.only( +// topLeft: const Radius.circular(10), +// topRight: const Radius.circular(10), +// bottomLeft: widget.alignment == +// AttributionAlignment.bottomLeft +// ? Radius.zero +// : const Radius.circular(10), +// bottomRight: widget.alignment == +// AttributionAlignment.bottomRight +// ? Radius.zero +// : const Radius.circular(10), +// ), +// ), +// constraints: BoxConstraints( +// minWidth: constraints.maxWidth < 420 +// ? constraints.maxWidth +// : persistentAttributionSize!.width, +// ), +// child: Padding( +// padding: const EdgeInsets.all(8), +// child: SizedBox( +// height: widget.attributions.length * 32, +// child: Column( +// mainAxisSize: MainAxisSize.max, +// mainAxisAlignment: MainAxisAlignment.spaceBetween, +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// ...widget.attributions +// .whereType(), +// SizedBox( +// height: (widget.permanentHeight - 24) + 32, +// ), +// ], +// ), +// ), +// ), +// ), +// ), +// ), +// MouseRegion( +// key: persistentAttributionKey, +// onEnter: (_) => setState(() => persistentHovered = true), +// onExit: (_) => setState(() => persistentHovered = false), +// cursor: SystemMouseCursors.click, +// child: AnimatedOpacity( +// opacity: persistentHovered || popupExpanded ? 1 : 0.5, +// curve: widget.animationConfig.buttonCurve, +// duration: widget.animationConfig.buttonDuration, +// child: Padding( +// padding: const EdgeInsets.all(4), +// child: FittedBox( +// child: Row( +// mainAxisSize: MainAxisSize.min, +// children: +// widget.alignment == AttributionAlignment.bottomLeft +// ? persistentAttributionItems.reversed.toList() +// : persistentAttributionItems, +// ), +// ), +// ), +// ), +// ), +// ], +// ), +// ), +// ); +// } +// } diff --git a/mobile/lib/ui/map/tile/layers.dart b/mobile/lib/ui/map/tile/layers.dart index 2597dddea2..c57ba2c639 100644 --- a/mobile/lib/ui/map/tile/layers.dart +++ b/mobile/lib/ui/map/tile/layers.dart @@ -1,11 +1,6 @@ import "package:flutter/material.dart"; import "package:flutter_map/flutter_map.dart"; -import "package:photos/generated/l10n.dart"; -import "package:photos/theme/ente_theme.dart"; -import "package:photos/ui/map/tile/attribution/map_attribution.dart"; import "package:photos/ui/map/tile/cache.dart"; -import "package:url_launcher/url_launcher.dart"; -import "package:url_launcher/url_launcher_string.dart"; const String _userAgent = "io.ente.photos"; @@ -62,32 +57,33 @@ class OSMFranceTileAttributes extends StatelessWidget { @override Widget build(BuildContext context) { - final textTheme = getEnteTextTheme(context).tinyBold; - return MapAttributionWidget( - alignment: AttributionAlignment.bottomLeft, - showFlutterMapAttribution: false, - permanentHeight: options.permanentHeight, - popupBackgroundColor: getEnteColorScheme(context).backgroundElevated, - popupBorderRadius: options.popupBorderRadius, - iconSize: options.iconSize, - attributions: [ - TextSourceAttribution( - S.of(context).openstreetmapContributors, - textStyle: textTheme, - onTap: () => launchUrlString('https://openstreetmap.org/copyright'), - ), - TextSourceAttribution( - 'HOT Tiles', - textStyle: textTheme, - onTap: () => launchUrl(Uri.parse('https://www.hotosm.org/')), - ), - TextSourceAttribution( - S.of(context).hostedAtOsmFrance, - textStyle: textTheme, - onTap: () => launchUrl(Uri.parse('https://www.openstreetmap.fr/')), - ), - ], - ); + // final textTheme = getEnteTextTheme(context).tinyBold; + // return MapAttributionWidget( + // alignment: AttributionAlignment.bottomLeft, + // showFlutterMapAttribution: false, + // permanentHeight: options.permanentHeight, + // popupBackgroundColor: getEnteColorScheme(context).backgroundElevated, + // popupBorderRadius: options.popupBorderRadius, + // iconSize: options.iconSize, + // attributions: [ + // TextSourceAttribution( + // S.of(context).openstreetmapContributors, + // textStyle: textTheme, + // onTap: () => launchUrlString('https://openstreetmap.org/copyright'), + // ), + // TextSourceAttribution( + // 'HOT Tiles', + // textStyle: textTheme, + // onTap: () => launchUrl(Uri.parse('https://www.hotosm.org/')), + // ), + // TextSourceAttribution( + // S.of(context).hostedAtOsmFrance, + // textStyle: textTheme, + // onTap: () => launchUrl(Uri.parse('https://www.openstreetmap.fr/')), + // ), + // ], + // ); + return const SizedBox.shrink(); } } From 6c77901396ecbde9fbecfe9a06f6f78ce397f729 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Wed, 29 May 2024 15:41:26 +0530 Subject: [PATCH 4/8] [mob][photos] Migrating to flutter_map v6 (2) --- mobile/lib/ui/map/map_marker.dart | 4 ++-- mobile/lib/ui/map/map_screen.dart | 2 +- mobile/lib/ui/map/map_view.dart | 14 ++++++-------- mobile/pubspec.lock | 20 ++++++++++++++------ mobile/pubspec.yaml | 4 ++-- 5 files changed, 25 insertions(+), 19 deletions(-) diff --git a/mobile/lib/ui/map/map_marker.dart b/mobile/lib/ui/map/map_marker.dart index 0009370be1..d4518feb6f 100644 --- a/mobile/lib/ui/map/map_marker.dart +++ b/mobile/lib/ui/map/map_marker.dart @@ -12,7 +12,7 @@ Marker mapMarker( }) { return Marker( //-6.5 is for taking in the height of the MarkerPointer - anchorPos: AnchorPos.exactly(Anchor(markerSize.height / 2, -6.5)), + alignment: Alignment(markerSize.height / 2, -6.5), key: Key(key), width: markerSize.width, height: markerSize.height, @@ -20,7 +20,7 @@ Marker mapMarker( imageMarker.latitude, imageMarker.longitude, ), - builder: (context) => MarkerImage( + child: MarkerImage( file: imageMarker.imageFile, seperator: (MapView.defaultMarkerSize.height + 10) - (MapView.defaultMarkerSize.height - markerSize.height), diff --git a/mobile/lib/ui/map/map_screen.dart b/mobile/lib/ui/map/map_screen.dart index adb2d590d9..78120bd839 100644 --- a/mobile/lib/ui/map/map_screen.dart +++ b/mobile/lib/ui/map/map_screen.dart @@ -114,7 +114,7 @@ class _MapScreenState extends State { ); Timer(Duration(milliseconds: debounceDuration), () { - calculateVisibleMarkers(mapController.bounds!); + calculateVisibleMarkers(mapController.camera.visibleBounds); setState(() { isLoading = false; }); diff --git a/mobile/lib/ui/map/map_view.dart b/mobile/lib/ui/map/map_view.dart index 2a8c0ed4c7..5286924c95 100644 --- a/mobile/lib/ui/map/map_view.dart +++ b/mobile/lib/ui/map/map_view.dart @@ -108,12 +108,10 @@ class _MapViewState extends State { maxClusterRadius: 100, showPolygon: false, size: widget.markerSize, - fitBoundsOptions: const FitBoundsOptions( - padding: EdgeInsets.all(80), - ), + padding: const EdgeInsets.all(80), markers: _markers, onClusterTap: (_) { - onChange(widget.controller.bounds!); + onChange(widget.controller.camera.visibleBounds); }, builder: (context, List markers) { final index = int.parse( @@ -164,8 +162,8 @@ class _MapViewState extends State { icon: Icons.add, onPressed: () { widget.controller.move( - widget.controller.center, - widget.controller.zoom + 1, + widget.controller.camera.center, + widget.controller.camera.zoom + 1, ); }, heroTag: 'zoom-in', @@ -174,8 +172,8 @@ class _MapViewState extends State { icon: Icons.remove, onPressed: () { widget.controller.move( - widget.controller.center, - widget.controller.zoom - 1, + widget.controller.camera.center, + widget.controller.camera.zoom - 1, ); }, heroTag: 'zoom-out', diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 8b71025e9f..97f5780063 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -764,26 +764,26 @@ packages: dependency: "direct main" description: name: flutter_map - sha256: "5286f72f87deb132daa1489442d6cc46e986fc105cb727d9ae1b602b35b1d1f3" + sha256: "87cc8349b8fa5dccda5af50018c7374b6645334a0d680931c1fe11bce88fa5bb" url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "6.2.1" flutter_map_marker_cluster: dependency: "direct main" description: name: flutter_map_marker_cluster - sha256: "14bb31b9dd3a759ab4a1ba320d19bbb554d8d7952c8812029c6f6b7bda956906" + sha256: a324f48da5ee83a3f29fd8d08b4b1e6e3114ff5c6cab910124d6a2e1f06f08cc url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.6" flutter_map_marker_popup: dependency: transitive description: name: flutter_map_marker_popup - sha256: be209c68b19d4c10d9a2f5911e45f7c579624c43a353adb9bf0f2fec0cf30b8c + sha256: ec563bcbae24a18ac16815fb75ac5ab33ccba609e14db70e252a67de19c6639c url: "https://pub.dev" source: hosted - version: "5.2.0" + version: "6.1.2" flutter_native_splash: dependency: "direct main" description: @@ -1252,6 +1252,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.10" + logger: + dependency: transitive + description: + name: logger + sha256: af05cc8714f356fd1f3888fb6741cbe9fbe25cdb6eedbab80e1a6db21047d4a4 + url: "https://pub.dev" + source: hosted + version: "2.3.0" logging: dependency: "direct main" description: diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index ed3bf47193..123ba3f2fe 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -83,8 +83,8 @@ dependencies: flutter_local_notifications: ^17.0.0 flutter_localizations: sdk: flutter - flutter_map: ^5.0.0 - flutter_map_marker_cluster: ^1.2.0 + flutter_map: ^6.2.0 + flutter_map_marker_cluster: ^1.3.6 flutter_native_splash: ^2.2.0+1 flutter_password_strength: ^0.1.6 flutter_secure_storage: ^8.0.0 From 588df2c3466913d95542b75b082fc0de5615e6b8 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Wed, 29 May 2024 17:46:30 +0530 Subject: [PATCH 5/8] [mob][photos] Migrating to flutter_map v6 (3): Fix cluster with only one image not rendering --- mobile/lib/ui/map/map_marker.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mobile/lib/ui/map/map_marker.dart b/mobile/lib/ui/map/map_marker.dart index d4518feb6f..e9de524227 100644 --- a/mobile/lib/ui/map/map_marker.dart +++ b/mobile/lib/ui/map/map_marker.dart @@ -11,8 +11,7 @@ Marker mapMarker( Size markerSize = MapView.defaultMarkerSize, }) { return Marker( - //-6.5 is for taking in the height of the MarkerPointer - alignment: Alignment(markerSize.height / 2, -6.5), + alignment: Alignment.topCenter, key: Key(key), width: markerSize.width, height: markerSize.height, From 7739be4e21365147affce9e88ec40ca8dc17898d Mon Sep 17 00:00:00 2001 From: ashilkn Date: Wed, 29 May 2024 20:42:05 +0530 Subject: [PATCH 6/8] [mob][photos] Migrating to flutter_map v6 (4): Fix attribution --- mobile/lib/ui/map/map_view.dart | 8 + .../map/tile/attribution/map_attribution.dart | 549 +++++++++--------- mobile/lib/ui/map/tile/layers.dart | 58 +- 3 files changed, 316 insertions(+), 299 deletions(-) diff --git a/mobile/lib/ui/map/map_view.dart b/mobile/lib/ui/map/map_view.dart index 5286924c95..3ff86f2ae1 100644 --- a/mobile/lib/ui/map/map_view.dart +++ b/mobile/lib/ui/map/map_view.dart @@ -135,6 +135,14 @@ class _MapViewState extends State { }, ), ), + Padding( + padding: EdgeInsets.only( + bottom: widget.bottomSheetDraggableAreaHeight, + ), + child: OSMFranceTileAttributes( + options: widget.mapAttributionOptions, + ), + ), ], ), widget.showControls diff --git a/mobile/lib/ui/map/tile/attribution/map_attribution.dart b/mobile/lib/ui/map/tile/attribution/map_attribution.dart index 370883bf08..524d8b1e5d 100644 --- a/mobile/lib/ui/map/tile/attribution/map_attribution.dart +++ b/mobile/lib/ui/map/tile/attribution/map_attribution.dart @@ -1,296 +1,301 @@ -// // ignore_for_file: invalid_use_of_internal_member +// ignore_for_file: invalid_use_of_internal_member -// import "dart:async"; +import "dart:async"; -// import "package:flutter/material.dart"; -// import "package:flutter_map/plugin_api.dart"; -// import "package:photos/extensions/list.dart"; -// import "package:photos/theme/colors.dart"; -// import "package:photos/theme/ente_theme.dart"; -// import "package:photos/ui/components/buttons/icon_button_widget.dart"; +import "package:flutter/material.dart"; +import "package:flutter_map/flutter_map.dart"; +import "package:photos/extensions/list.dart"; +import "package:photos/theme/colors.dart"; +import "package:photos/theme/ente_theme.dart"; +import "package:photos/ui/components/buttons/icon_button_widget.dart"; -// // Credit: This code is based on the Rich Attribution widget from the flutter_map -// class MapAttributionWidget extends StatefulWidget { -// /// List of attributions to display -// /// -// /// [TextSourceAttribution]s are shown in a popup box (toggled by a tap/click -// /// on the [openButton]/[closeButton]), unlike [LogoSourceAttribution], which -// /// are visible permanently adjacent to the open/close button. -// final List attributions; +// Credit: This code is based on the Rich Attribution widget from the flutter_map +class MapAttributionWidget extends StatefulWidget { + /// List of attributions to display + /// + /// [TextSourceAttribution]s are shown in a popup box (toggled by a tap/click + /// on the [openButton]/[closeButton]), unlike [LogoSourceAttribution], which + /// are visible permanently adjacent to the open/close button. + final List attributions; -// /// The position in which to anchor this widget -// final AttributionAlignment alignment; + /// The position in which to anchor this widget + final AttributionAlignment alignment; -// /// The widget (usually an [IconButton]) to display when the popup box is -// /// closed, that opens the popup box via the `open` callback -// final Widget Function(BuildContext context, VoidCallback open)? openButton; + /// The widget (usually an [IconButton]) to display when the popup box is + /// closed, that opens the popup box via the `open` callback + final Widget Function(BuildContext context, VoidCallback open)? openButton; -// /// The widget (usually an [IconButton]) to display when the popup box is open, -// /// that closes the popup box via the `close` callback -// final Widget Function(BuildContext context, VoidCallback close)? closeButton; + /// The widget (usually an [IconButton]) to display when the popup box is open, + /// that closes the popup box via the `close` callback + final Widget Function(BuildContext context, VoidCallback close)? closeButton; -// /// The color to use as the popup box's background color, defaulting to the -// /// [Theme]s background color -// final Color? popupBackgroundColor; + /// The color to use as the popup box's background color, defaulting to the + /// [Theme]s background color + final Color? popupBackgroundColor; -// /// The radius of the edges of the popup box -// final BorderRadius? popupBorderRadius; + /// The radius of the edges of the popup box + final BorderRadius? popupBorderRadius; -// /// The height of the permanent row in which is found the popup menu toggle -// /// button -// /// -// /// Also determines spacing between the items within the row. -// /// -// /// Also set [LogoSourceAttribution.height] to the same value, if adjusted. -// final double permanentHeight; + /// The height of the permanent row in which is found the popup menu toggle + /// button + /// + /// Also determines spacing between the items within the row. + /// + /// Also set [LogoSourceAttribution.height] to the same value, if adjusted. + final double permanentHeight; -// /// Whether to add an additional attribution logo and text for 'flutter_map' -// final bool showFlutterMapAttribution; + /// Whether to add an additional attribution logo and text for 'flutter_map' + final bool showFlutterMapAttribution; -// /// Animation configuration, through the properties and handler/builder -// /// defined by a [RichAttributionWidgetAnimation] implementation -// /// -// /// Can be extensivley customized by implementing a custom -// /// [RichAttributionWidgetAnimation], or the prebuilt [FadeRAWA] and -// /// [ScaleRAWA] animations can be used with limited customization. -// final RichAttributionWidgetAnimation animationConfig; + /// Animation configuration, through the properties and handler/builder + /// defined by a [RichAttributionWidgetAnimation] implementation + /// + /// Can be extensivley customized by implementing a custom + /// [RichAttributionWidgetAnimation], or the prebuilt [FadeRAWA] and + /// [ScaleRAWA] animations can be used with limited customization. + final RichAttributionWidgetAnimation animationConfig; -// /// If not [Duration.zero] (default), the popup box will be open by default and -// /// hidden this long after the map is initialised -// /// -// /// This is useful with certain sources/tile servers that make immediate -// /// attribution mandatory and are not attributed with a permanently visible -// /// [LogoSourceAttribution]. -// final Duration popupInitialDisplayDuration; + /// If not [Duration.zero] (default), the popup box will be open by default and + /// hidden this long after the map is initialised + /// + /// This is useful with certain sources/tile servers that make immediate + /// attribution mandatory and are not attributed with a permanently visible + /// [LogoSourceAttribution]. + final Duration popupInitialDisplayDuration; -// /// A prebuilt dynamic attribution layer that supports both logos and text -// /// through [SourceAttribution]s -// /// -// /// [TextSourceAttribution]s are shown in a popup box that can be visible or -// /// invisible. Its state is toggled by a tri-state [openButton]/[closeButton] : -// /// 1. Not hovered, not opened: faded button, invisible box -// /// 2. Hovered, not opened: full opacity button, invisible box -// /// 3. Opened: full opacity button, visible box -// /// -// /// The hover state on mobile devices is unspecified, but the behaviour is -// /// usually inconsequential on mobile devices anyway, due to the fingertip -// /// covering the entire button. -// /// -// /// [LogoSourceAttribution]s are shown adjacent to the open/close button, to -// /// comply with some stricter tile server requirements (such as Mapbox). These -// /// are usually supplemented with a [TextSourceAttribution]. -// /// -// /// The popup box also closes automatically on any interaction with the map. -// /// -// /// Animations are built in by default, and configured/handled through -// /// [RichAttributionWidgetAnimation] - see that class and the [animationConfig] -// /// property for more information. By default, a simple fade/opacity animation -// /// is provided by [FadeRAWA]. [ScaleRAWA] is also available. -// /// -// /// Read the documentation on the individual properties for more information -// /// and customizability. + /// A prebuilt dynamic attribution layer that supports both logos and text + /// through [SourceAttribution]s + /// + /// [TextSourceAttribution]s are shown in a popup box that can be visible or + /// invisible. Its state is toggled by a tri-state [openButton]/[closeButton] : + /// 1. Not hovered, not opened: faded button, invisible box + /// 2. Hovered, not opened: full opacity button, invisible box + /// 3. Opened: full opacity button, visible box + /// + /// The hover state on mobile devices is unspecified, but the behaviour is + /// usually inconsequential on mobile devices anyway, due to the fingertip + /// covering the entire button. + /// + /// [LogoSourceAttribution]s are shown adjacent to the open/close button, to + /// comply with some stricter tile server requirements (such as Mapbox). These + /// are usually supplemented with a [TextSourceAttribution]. + /// + /// The popup box also closes automatically on any interaction with the map. + /// + /// Animations are built in by default, and configured/handled through + /// [RichAttributionWidgetAnimation] - see that class and the [animationConfig] + /// property for more information. By default, a simple fade/opacity animation + /// is provided by [FadeRAWA]. [ScaleRAWA] is also available. + /// + /// Read the documentation on the individual properties for more information + /// and customizability. -// final double iconSize; -// const MapAttributionWidget({ -// super.key, -// required this.attributions, -// this.alignment = AttributionAlignment.bottomRight, -// this.openButton, -// this.closeButton, -// this.popupBackgroundColor, -// this.popupBorderRadius, -// this.permanentHeight = 24, -// this.showFlutterMapAttribution = true, -// this.animationConfig = const FadeRAWA(), -// this.popupInitialDisplayDuration = Duration.zero, -// this.iconSize = 20, -// }); + final double iconSize; + const MapAttributionWidget({ + super.key, + required this.attributions, + this.alignment = AttributionAlignment.bottomRight, + this.openButton, + this.closeButton, + this.popupBackgroundColor, + this.popupBorderRadius, + this.permanentHeight = 24, + this.showFlutterMapAttribution = true, + this.animationConfig = const FadeRAWA(), + this.popupInitialDisplayDuration = Duration.zero, + this.iconSize = 20, + }); -// @override -// State createState() => MapAttributionWidgetState(); -// } + @override + State createState() => MapAttributionWidgetState(); +} -// class MapAttributionWidgetState extends State { -// StreamSubscription? mapEventSubscription; +class MapAttributionWidgetState extends State { + StreamSubscription? mapEventSubscription; -// final persistentAttributionKey = GlobalKey(); -// Size? persistentAttributionSize; + final persistentAttributionKey = GlobalKey(); + Size? persistentAttributionSize; -// late bool popupExpanded = widget.popupInitialDisplayDuration != Duration.zero; -// bool persistentHovered = false; + late bool popupExpanded = widget.popupInitialDisplayDuration != Duration.zero; + bool persistentHovered = false; -// @override -// void initState() { -// super.initState(); + @override + void initState() { + super.initState(); -// if (popupExpanded) { -// Future.delayed( -// widget.popupInitialDisplayDuration, -// () => setState(() => popupExpanded = false), -// ); -// } + if (popupExpanded) { + Future.delayed( + widget.popupInitialDisplayDuration, + () => setState(() => popupExpanded = false), + ); + } -// WidgetsBinding.instance.addPostFrameCallback( -// (_) => WidgetsBinding.instance.addPostFrameCallback((_) { -// if (mounted) { -// setState( -// () => persistentAttributionSize = -// (persistentAttributionKey.currentContext!.findRenderObject() -// as RenderBox) -// .size, -// ); -// } -// }), -// ); -// } + WidgetsBinding.instance.addPostFrameCallback( + (_) => WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + setState( + () => persistentAttributionSize = + (persistentAttributionKey.currentContext!.findRenderObject() + as RenderBox) + .size, + ); + } + }), + ); + } -// @override -// void dispose() { -// mapEventSubscription?.cancel(); -// super.dispose(); -// } + @override + void dispose() { + mapEventSubscription?.cancel(); + super.dispose(); + } -// @override -// Widget build(BuildContext context) { -// final persistentAttributionItems = [ -// ...List.from( -// widget.attributions.whereType(), -// growable: false, -// ).interleave(SizedBox(width: widget.permanentHeight / 1.5)), -// if (widget.showFlutterMapAttribution) -// LogoSourceAttribution( -// Image.asset( -// 'lib/assets/flutter_map_logo.png', -// package: 'flutter_map', -// ), -// tooltip: 'flutter_map', -// height: widget.permanentHeight, -// ), -// SizedBox(width: widget.permanentHeight * 0.1), -// AnimatedSwitcher( -// switchInCurve: widget.animationConfig.buttonCurve, -// switchOutCurve: widget.animationConfig.buttonCurve, -// duration: widget.animationConfig.buttonDuration, -// child: popupExpanded -// ? (widget.closeButton ?? -// (context, close) => IconButtonWidget( -// size: widget.iconSize, -// onTap: close, -// icon: Icons.cancel_outlined, -// iconButtonType: IconButtonType.primary, -// iconColor: getEnteColorScheme(context).strokeBase, -// ))( -// context, -// () => setState(() => popupExpanded = false), -// ) -// : (widget.openButton ?? -// (context, open) => IconButtonWidget( -// size: widget.iconSize, -// onTap: open, -// icon: Icons.info_outlined, -// iconButtonType: IconButtonType.primary, -// iconColor: strokeBaseLight, -// ))( -// context, -// () { -// setState(() => popupExpanded = true); -// mapEventSubscription = FlutterMapState.of(context) -// .mapController -// .mapEventStream -// .listen((e) { -// setState(() => popupExpanded = false); -// mapEventSubscription?.cancel(); -// }); -// }, -// ), -// ), -// ]; + @override + Widget build(BuildContext context) { + final persistentAttributionItems = [ + ...List.from( + widget.attributions.whereType(), + growable: false, + ).interleave(SizedBox(width: widget.permanentHeight / 1.5)), + if (widget.showFlutterMapAttribution) + LogoSourceAttribution( + Image.asset( + 'lib/assets/flutter_map_logo.png', + package: 'flutter_map', + ), + tooltip: 'flutter_map', + height: widget.permanentHeight, + ), + SizedBox(width: widget.permanentHeight * 0.1), + AnimatedSwitcher( + switchInCurve: widget.animationConfig.buttonCurve, + switchOutCurve: widget.animationConfig.buttonCurve, + duration: widget.animationConfig.buttonDuration, + child: popupExpanded + ? (widget.closeButton ?? + (context, close) => IconButtonWidget( + size: widget.iconSize, + onTap: close, + icon: Icons.cancel_outlined, + iconButtonType: IconButtonType.primary, + iconColor: getEnteColorScheme(context).strokeBase, + ))( + context, + () => setState(() => popupExpanded = false), + ) + : (widget.openButton ?? + (context, open) => IconButtonWidget( + size: widget.iconSize, + onTap: open, + icon: Icons.info_outlined, + iconButtonType: IconButtonType.primary, + iconColor: strokeBaseLight, + ))( + context, + () { + setState(() => popupExpanded = true); + // mapEventSubscription = FlutterMapState.of(context) + // .mapController + // .mapEventStream + // .listen((e) { + // setState(() => popupExpanded = false); + // mapEventSubscription?.cancel(); + // }); + mapEventSubscription = + MapController().mapEventStream.listen((e) { + setState(() => popupExpanded = false); + mapEventSubscription?.cancel(); + }); + }, + ), + ), + ]; -// return LayoutBuilder( -// builder: (context, constraints) => Align( -// alignment: widget.alignment.real, -// child: Stack( -// alignment: widget.alignment.real, -// children: [ -// if (persistentAttributionSize != null) -// Padding( -// padding: const EdgeInsets.all(6), -// child: AnimatedScale( -// scale: popupExpanded ? 1 : 0, -// duration: const Duration(milliseconds: 200), -// curve: popupExpanded ? Curves.easeOut : Curves.easeIn, -// alignment: widget.alignment.real, -// child: Container( -// decoration: BoxDecoration( -// color: widget.popupBackgroundColor ?? -// Theme.of(context).colorScheme.background, -// border: Border.all(width: 0, style: BorderStyle.none), -// borderRadius: widget.popupBorderRadius ?? -// BorderRadius.only( -// topLeft: const Radius.circular(10), -// topRight: const Radius.circular(10), -// bottomLeft: widget.alignment == -// AttributionAlignment.bottomLeft -// ? Radius.zero -// : const Radius.circular(10), -// bottomRight: widget.alignment == -// AttributionAlignment.bottomRight -// ? Radius.zero -// : const Radius.circular(10), -// ), -// ), -// constraints: BoxConstraints( -// minWidth: constraints.maxWidth < 420 -// ? constraints.maxWidth -// : persistentAttributionSize!.width, -// ), -// child: Padding( -// padding: const EdgeInsets.all(8), -// child: SizedBox( -// height: widget.attributions.length * 32, -// child: Column( -// mainAxisSize: MainAxisSize.max, -// mainAxisAlignment: MainAxisAlignment.spaceBetween, -// crossAxisAlignment: CrossAxisAlignment.start, -// children: [ -// ...widget.attributions -// .whereType(), -// SizedBox( -// height: (widget.permanentHeight - 24) + 32, -// ), -// ], -// ), -// ), -// ), -// ), -// ), -// ), -// MouseRegion( -// key: persistentAttributionKey, -// onEnter: (_) => setState(() => persistentHovered = true), -// onExit: (_) => setState(() => persistentHovered = false), -// cursor: SystemMouseCursors.click, -// child: AnimatedOpacity( -// opacity: persistentHovered || popupExpanded ? 1 : 0.5, -// curve: widget.animationConfig.buttonCurve, -// duration: widget.animationConfig.buttonDuration, -// child: Padding( -// padding: const EdgeInsets.all(4), -// child: FittedBox( -// child: Row( -// mainAxisSize: MainAxisSize.min, -// children: -// widget.alignment == AttributionAlignment.bottomLeft -// ? persistentAttributionItems.reversed.toList() -// : persistentAttributionItems, -// ), -// ), -// ), -// ), -// ), -// ], -// ), -// ), -// ); -// } -// } + return LayoutBuilder( + builder: (context, constraints) => Align( + alignment: widget.alignment.real, + child: Stack( + alignment: widget.alignment.real, + children: [ + if (persistentAttributionSize != null) + Padding( + padding: const EdgeInsets.all(6), + child: AnimatedScale( + scale: popupExpanded ? 1 : 0, + duration: const Duration(milliseconds: 200), + curve: popupExpanded ? Curves.easeOut : Curves.easeIn, + alignment: widget.alignment.real, + child: Container( + decoration: BoxDecoration( + color: widget.popupBackgroundColor ?? + Theme.of(context).colorScheme.background, + border: Border.all(width: 0, style: BorderStyle.none), + borderRadius: widget.popupBorderRadius ?? + BorderRadius.only( + topLeft: const Radius.circular(10), + topRight: const Radius.circular(10), + bottomLeft: widget.alignment == + AttributionAlignment.bottomLeft + ? Radius.zero + : const Radius.circular(10), + bottomRight: widget.alignment == + AttributionAlignment.bottomRight + ? Radius.zero + : const Radius.circular(10), + ), + ), + constraints: BoxConstraints( + minWidth: constraints.maxWidth < 420 + ? constraints.maxWidth + : persistentAttributionSize!.width, + ), + child: Padding( + padding: const EdgeInsets.all(8), + child: SizedBox( + height: widget.attributions.length * 32, + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ...widget.attributions + .whereType(), + SizedBox( + height: (widget.permanentHeight - 24) + 32, + ), + ], + ), + ), + ), + ), + ), + ), + MouseRegion( + key: persistentAttributionKey, + onEnter: (_) => setState(() => persistentHovered = true), + onExit: (_) => setState(() => persistentHovered = false), + cursor: SystemMouseCursors.click, + child: AnimatedOpacity( + opacity: persistentHovered || popupExpanded ? 1 : 0.5, + curve: widget.animationConfig.buttonCurve, + duration: widget.animationConfig.buttonDuration, + child: Padding( + padding: const EdgeInsets.all(4), + child: FittedBox( + child: Row( + mainAxisSize: MainAxisSize.min, + children: + widget.alignment == AttributionAlignment.bottomLeft + ? persistentAttributionItems.reversed.toList() + : persistentAttributionItems, + ), + ), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/mobile/lib/ui/map/tile/layers.dart b/mobile/lib/ui/map/tile/layers.dart index c57ba2c639..2597dddea2 100644 --- a/mobile/lib/ui/map/tile/layers.dart +++ b/mobile/lib/ui/map/tile/layers.dart @@ -1,6 +1,11 @@ import "package:flutter/material.dart"; import "package:flutter_map/flutter_map.dart"; +import "package:photos/generated/l10n.dart"; +import "package:photos/theme/ente_theme.dart"; +import "package:photos/ui/map/tile/attribution/map_attribution.dart"; import "package:photos/ui/map/tile/cache.dart"; +import "package:url_launcher/url_launcher.dart"; +import "package:url_launcher/url_launcher_string.dart"; const String _userAgent = "io.ente.photos"; @@ -57,33 +62,32 @@ class OSMFranceTileAttributes extends StatelessWidget { @override Widget build(BuildContext context) { - // final textTheme = getEnteTextTheme(context).tinyBold; - // return MapAttributionWidget( - // alignment: AttributionAlignment.bottomLeft, - // showFlutterMapAttribution: false, - // permanentHeight: options.permanentHeight, - // popupBackgroundColor: getEnteColorScheme(context).backgroundElevated, - // popupBorderRadius: options.popupBorderRadius, - // iconSize: options.iconSize, - // attributions: [ - // TextSourceAttribution( - // S.of(context).openstreetmapContributors, - // textStyle: textTheme, - // onTap: () => launchUrlString('https://openstreetmap.org/copyright'), - // ), - // TextSourceAttribution( - // 'HOT Tiles', - // textStyle: textTheme, - // onTap: () => launchUrl(Uri.parse('https://www.hotosm.org/')), - // ), - // TextSourceAttribution( - // S.of(context).hostedAtOsmFrance, - // textStyle: textTheme, - // onTap: () => launchUrl(Uri.parse('https://www.openstreetmap.fr/')), - // ), - // ], - // ); - return const SizedBox.shrink(); + final textTheme = getEnteTextTheme(context).tinyBold; + return MapAttributionWidget( + alignment: AttributionAlignment.bottomLeft, + showFlutterMapAttribution: false, + permanentHeight: options.permanentHeight, + popupBackgroundColor: getEnteColorScheme(context).backgroundElevated, + popupBorderRadius: options.popupBorderRadius, + iconSize: options.iconSize, + attributions: [ + TextSourceAttribution( + S.of(context).openstreetmapContributors, + textStyle: textTheme, + onTap: () => launchUrlString('https://openstreetmap.org/copyright'), + ), + TextSourceAttribution( + 'HOT Tiles', + textStyle: textTheme, + onTap: () => launchUrl(Uri.parse('https://www.hotosm.org/')), + ), + TextSourceAttribution( + S.of(context).hostedAtOsmFrance, + textStyle: textTheme, + onTap: () => launchUrl(Uri.parse('https://www.openstreetmap.fr/')), + ), + ], + ); } } From f871255833b41c4ffb9cf89d73c70bd0d7123b65 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 30 May 2024 20:34:59 +0530 Subject: [PATCH 7/8] [mob][photos] fix clusters in map not rendering properly in profile or release mode Key.toString() is working as expected on debug mode after upgrading flutter to 3.22.0 --- mobile/lib/ui/map/map_marker.dart | 4 ++-- mobile/lib/ui/map/map_view.dart | 17 +++++++---------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/mobile/lib/ui/map/map_marker.dart b/mobile/lib/ui/map/map_marker.dart index e9de524227..3dc026697a 100644 --- a/mobile/lib/ui/map/map_marker.dart +++ b/mobile/lib/ui/map/map_marker.dart @@ -7,12 +7,12 @@ import "package:photos/ui/map/marker_image.dart"; Marker mapMarker( ImageMarker imageMarker, - String key, { + ValueKey key, { Size markerSize = MapView.defaultMarkerSize, }) { return Marker( alignment: Alignment.topCenter, - key: Key(key), + key: key, width: markerSize.width, height: markerSize.height, point: LatLng( diff --git a/mobile/lib/ui/map/map_view.dart b/mobile/lib/ui/map/map_view.dart index 3ff86f2ae1..d4de022197 100644 --- a/mobile/lib/ui/map/map_view.dart +++ b/mobile/lib/ui/map/map_view.dart @@ -4,8 +4,8 @@ import "package:flutter_map_marker_cluster/flutter_map_marker_cluster.dart"; import "package:latlong2/latlong.dart"; import "package:photos/ui/map/image_marker.dart"; import "package:photos/ui/map/map_button.dart"; -import 'package:photos/ui/map/map_gallery_tile.dart'; -import 'package:photos/ui/map/map_gallery_tile_badge.dart'; +import "package:photos/ui/map/map_gallery_tile.dart"; +import "package:photos/ui/map/map_gallery_tile_badge.dart"; import "package:photos/ui/map/map_marker.dart"; import "package:photos/ui/map/tile/layers.dart"; import "package:photos/utils/debouncer.dart"; @@ -114,13 +114,10 @@ class _MapViewState extends State { onChange(widget.controller.camera.visibleBounds); }, builder: (context, List markers) { - final index = int.parse( - markers.first.key - .toString() - .replaceAll(RegExp(r'[^0-9]'), ''), - ); - final String clusterKey = - 'map-badge-$index-len-${markers.length}'; + final valueKey = markers.first.key as ValueKey; + final index = valueKey.value as int; + + final clusterKey = 'map-badge-$index-len-${markers.length}'; return Stack( key: ValueKey(clusterKey), @@ -199,7 +196,7 @@ class _MapViewState extends State { final imageMarker = widget.imageMarkers[index]; return mapMarker( imageMarker, - index.toString(), + ValueKey(index), markerSize: widget.markerSize, ); }); From f14f973a61abcbe644c0b223dc418db809a33d15 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 30 May 2024 21:58:36 +0530 Subject: [PATCH 8/8] [mob][photos] Remove commented out code --- mobile/lib/ui/map/tile/attribution/map_attribution.dart | 7 ------- 1 file changed, 7 deletions(-) diff --git a/mobile/lib/ui/map/tile/attribution/map_attribution.dart b/mobile/lib/ui/map/tile/attribution/map_attribution.dart index 524d8b1e5d..7ae8c6bc86 100644 --- a/mobile/lib/ui/map/tile/attribution/map_attribution.dart +++ b/mobile/lib/ui/map/tile/attribution/map_attribution.dart @@ -194,13 +194,6 @@ class MapAttributionWidgetState extends State { context, () { setState(() => popupExpanded = true); - // mapEventSubscription = FlutterMapState.of(context) - // .mapController - // .mapEventStream - // .listen((e) { - // setState(() => popupExpanded = false); - // mapEventSubscription?.cancel(); - // }); mapEventSubscription = MapController().mapEventStream.listen((e) { setState(() => popupExpanded = false);