diff --git a/.config/dictionaries/project.dic b/.config/dictionaries/project.dic index 0a2d74be760f..44179a1e01dd 100644 --- a/.config/dictionaries/project.dic +++ b/.config/dictionaries/project.dic @@ -278,6 +278,7 @@ postcss Pozhylenkov Precache Precertificate +precompressed preprod proguard projectcatalyst diff --git a/.vscode/launch.recommended.json b/.vscode/launch.recommended.json index 6ab64ab47ead..30f84fabf278 100644 --- a/.vscode/launch.recommended.json +++ b/.vscode/launch.recommended.json @@ -14,6 +14,7 @@ "deviceId": "chrome", "program": "lib/configs/main_web.dart", "args": [ + "--wasm", "--dart-define", "SENTRY_DSN=REPLACE_WITH_SENTRY_DSN_URL", "--web-header", @@ -34,6 +35,7 @@ "deviceId": "chrome", "program": "lib/configs/main_web.dart", "args": [ + "--wasm", "--dart-define", "SENTRY_DSN=REPLACE_WITH_SENTRY_DSN_URL", "--web-header", @@ -52,6 +54,7 @@ "deviceId": "chrome", "program": "lib/configs/main_web.dart", "args": [ + "--wasm", "--dart-define", "SENTRY_DSN=REPLACE_WITH_SENTRY_DSN_URL", "--web-header", @@ -70,6 +73,7 @@ "deviceId": "chrome", "program": "lib/configs/main_web.dart", "args": [ + "--wasm", "--dart-define", "SENTRY_DSN=REPLACE_WITH_SENTRY_DSN_URL", "--web-header", @@ -88,6 +92,7 @@ "deviceId": "chrome", "program": "lib/configs/main_web.dart", "args": [ + "--wasm", "--dart-define", "SENTRY_DSN=REPLACE_WITH_SENTRY_DSN_URL", "--web-header", @@ -106,6 +111,7 @@ "deviceId": "chrome", "program": "lib/configs/main_web.dart", "args": [ + "--wasm", "--dart-define", "SENTRY_DSN=REPLACE_WITH_SENTRY_DSN_URL", "--web-header", @@ -124,6 +130,7 @@ "deviceId": "chrome", "program": "lib/configs/main_web.dart", "args": [ + "--wasm", "--dart-define", "SENTRY_DSN=REPLACE_WITH_SENTRY_DSN_URL", "--web-header", @@ -142,6 +149,7 @@ "deviceId": "chrome", "program": "lib/configs/main_web.dart", "args": [ + "--wasm", "--dart-define", "SENTRY_DSN=REPLACE_WITH_SENTRY_DSN_URL", "--web-header", @@ -160,6 +168,7 @@ "deviceId": "chrome", "program": "lib/configs/main_web.dart", "args": [ + "--wasm", "--dart-define", "SENTRY_DSN=REPLACE_WITH_SENTRY_DSN_URL", "--web-header", diff --git a/catalyst_voices/.gitignore b/catalyst_voices/.gitignore index a4b765cb31da..d8bcd3a4ff2f 100644 --- a/catalyst_voices/.gitignore +++ b/catalyst_voices/.gitignore @@ -3,7 +3,9 @@ devtools_options.yaml # Documentation -docs/ +docs/dartdoc +docs/licenses +docs/*.dot # Generated files from code generation tools *.g.dart @@ -169,4 +171,5 @@ coverage/ ### Tests ### # Those files are generated by melos test-report script test_reports/*.junit-report.xml -test_reports/*.tests-output.json \ No newline at end of file +test_reports/*.tests-output.json +scripts/version_web_assets/test/helper/tmp \ No newline at end of file diff --git a/catalyst_voices/Caddyfile b/catalyst_voices/Caddyfile index 75b3a4009bd0..54a521bd9d1b 100644 --- a/catalyst_voices/Caddyfile +++ b/catalyst_voices/Caddyfile @@ -1,9 +1,12 @@ { - admin :8081 - metrics + admin :8081 + metrics } http://:8080 { - root * /app + root * /app + + # Compress on-the-fly if not precompressed + encode handle /healthz { respond `{"status":"ok"}` 200 @@ -11,20 +14,29 @@ http://:8080 { handle { try_files {path} /index.html - file_server + file_server { + # default order br zstd gzip + precompressed + } } - header { - Cross-Origin-Opener-Policy "same-origin" - Cross-Origin-Embedder-Policy "require-corp" + header { + Cross-Origin-Opener-Policy "same-origin" + Cross-Origin-Embedder-Policy "require-corp" + Cross-Origin-Resource-Policy "same-origin" - / Cache-Control "public, max-age=3600, must-revalidate" - } + ?Cache-Control "public, max-age=3600, must-revalidate" + } - handle_errors { - rewrite * /50x.html - file_server - } + @index_html path /index.html + header @index_html Cache-Control "no-cache, must-revalidate" - log -} \ No newline at end of file + import /app/versioned_assets.caddy + + handle_errors { + rewrite * /50x.html + file_server + } + + log +} diff --git a/catalyst_voices/Earthfile b/catalyst_voices/Earthfile index 16d617301452..2e8e1c2699da 100644 --- a/catalyst_voices/Earthfile +++ b/catalyst_voices/Earthfile @@ -126,7 +126,14 @@ build-web: --TARGET=lib/configs/main_web.dart \ --UPLOAD_DEBUG_SYMBOLS=$UPLOAD_DEBUG_SYMBOLS \ --SENTRY_ORG='iohk-j4' \ - --SENTRY_PROJECT='catalyst-voices' + --SENTRY_PROJECT='catalyst-voices' \ + --WASM=true + + # Version web assets with content-based MD5 hashes for cache busting + RUN dart run /frontend/scripts/version_web_assets/version_web_assets.dart --build-dir=$WORKDIR/build/web --wasm=true + + DO flutter-ci+COMPRESS_WEB_FILES + SAVE ARTIFACT --keep-ts web package: diff --git a/catalyst_voices/README.md b/catalyst_voices/README.md index d397ba599912..9e2a0c05cfca 100644 --- a/catalyst_voices/README.md +++ b/catalyst_voices/README.md @@ -6,19 +6,22 @@ This repository contains the Catalyst Voices app and packages. * [Catalyst Voices](#catalyst-voices) * [Requirements](#requirements) + * [Recommended VS code plugins](#recommended-vs-code-plugins) * [Platforms](#platforms) * [Getting Started](#getting-started) * [Bootstrapping](#bootstrapping) * [Packages](#packages) * [Environment Type vs Flavor](#environment-type-vs-flavor) * [Environment types](#environment-types) + * [Stress Test](#stress-test) + * [Debug Performance Flags](#debug-performance-flags) * [Flavor types](#flavor-types) * [Environment variables](#environment-variables) * [Environment config](#environment-config) - * [Code Generation](#code-generation) - * [Running Code Generation](#running-code-generation) + * [Code Generation](#code-generation) + *[Running Code Generation](#running-code-generation) * [Basic Generation](#basic-generation) - * [Local Saving](#local-saving) + *[Local Saving](#local-saving) * [GitHub Token / PAT Setup](#github-token--pat-setup) * [Security Notes](#security-notes) * [Running Tests](#running-tests) @@ -76,6 +79,7 @@ just bootstrap | [catalyst_voices_services](./packages/internal/catalyst_voices_services/) | Services |[example](./packages/internal/catalyst_voices_services/)| | [catalyst_voices_shared](./packages/internal/catalyst_voices_shared/) | Shared code |[example](./packages/internal/catalyst_voices_shared/)| | [catalyst_voices_view_models](./packages/internal/catalyst_voices_view_models/) | ViewModels |[example](./packages/internal/catalyst_voices_view_models/)| +| [catalyst_voices_dev](./packages/internal/catalyst_voices_dev/) | Dev | [example](./packages/internal/catalyst_voices_dev/)| ### Environment Type vs Flavor @@ -122,6 +126,39 @@ flutter run --target lib/configs/main.dart --dart-define=ENV_NAME=prod -d chrome > Catalyst Voices works on the Web only. > We plan to add support for other targets later. +#### Stress Test + +Each environment can be launched in stress test. +It will use local version of Gateway and produce as many proposals as configured (defaults to 100). +Use following `--dart-define` variables to configure your setup: + +* `--dart-define=STRESS_TEST=true` to enable +* `--dart-define=STRESS_TEST_PROPOSAL_INDEX_COUNT=100` says how many proposals will be produced +* `--dart-define=STRESS_TEST_DECOMPRESSED=false` if signed documents should be compressed or not + +#### Debug Performance Flags + +Each environment can be launched with additional debug performance flags on web in `Profile` mode +There is four performance flags to choose from: + +* **debugProfileBuildsEnabled:** Adds Timeline events for every Widget built. +* **debugProfileBuildsEnabledUserWidgets:** Adds Timeline events for every user-created Widget built. +* **debugProfileLayoutsEnabled:** Adds Timeline events for every RenderObject layout. +* **debugProfilePaintsEnabled:** Adds Timeline events for every RenderObject painted. + +To use following `--dart-define` variables to configure your setup: + +* `--dart-define=DEBUG_PROFILE_BUILDS_ENABLED` to enable debugProfileBuildsEnabled +* `--dart-define=DEBUG_PROFILE_BUILDS_ENABLED_USER_WIDGETS` to enable debugProfileBuildsEnabledUserWidgets +* `--dart-define=DEBUG_PROFILE_LAYOUTS_ENABLED` to enable debugProfileLayoutsEnabled +* `--dart-define=DEBUG_PROFILE_PAINTS_ENABLED` to enable debugProfilePaintsEnabled + +There is `--dart-define` variable to enable all four performance flags at once: + +* `--dart-define=DEBUG_PROFILE_DEVELOPER_PROFILER_ENABLE_ALL` + +Remember to also use `--profile` mode when running the app. + ### Flavor types You should use flavor types instead of environment variables when running the app on mobile diff --git a/catalyst_voices/apps/voices/lib/app/view/app_precache_image_assets.dart b/catalyst_voices/apps/voices/lib/app/view/app_precache_image_assets.dart index fb07bdd78734..5ebb5c60f146 100644 --- a/catalyst_voices/apps/voices/lib/app/view/app_precache_image_assets.dart +++ b/catalyst_voices/apps/voices/lib/app/view/app_precache_image_assets.dart @@ -7,6 +7,8 @@ import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; import 'package:flutter/material.dart'; import 'package:synchronized/synchronized.dart'; +final _logger = Logger('AppPrecacheImageAssets'); + class GlobalPrecacheImages extends StatefulWidget { final Widget child; @@ -59,6 +61,22 @@ class ImagePrecacheService { } } + Future _cacheImageAsset(AssetGenImage asset, BuildContext context) async { + try { + await asset.cache(context: context); + } catch (error) { + _logger.info('Failed to cache image asset: $error'); + } + } + + Future _cacheSvgAsset(SvgGenImage svg, BuildContext context) async { + try { + await svg.cache(context: context); + } catch (error) { + _logger.info('Failed to cache SVG asset: $error'); + } + } + Future _precacheAssets( BuildContext context, { List svgs = const [], @@ -71,8 +89,8 @@ class ImagePrecacheService { _assets.addAll(assets); await Future.wait([ - ..._svgs.map((e) => e.cache(context: context)), - ..._assets.map((e) => e.cache(context: context)), + ..._svgs.map((e) => _cacheSvgAsset(e, context)), + ..._assets.map((e) => _cacheImageAsset(e, context)), ]); if (!_isInitialized.isCompleted) _isInitialized.complete(true); diff --git a/catalyst_voices/apps/voices/lib/app/view/app_session_listener.dart b/catalyst_voices/apps/voices/lib/app/view/app_session_listener.dart index 6d5a044399e5..d33c2bdf9f54 100644 --- a/catalyst_voices/apps/voices/lib/app/view/app_session_listener.dart +++ b/catalyst_voices/apps/voices/lib/app/view/app_session_listener.dart @@ -1,6 +1,6 @@ import 'package:catalyst_voices/common/signal_handler.dart'; import 'package:catalyst_voices/notification/catalyst_messenger.dart'; -import 'package:catalyst_voices/notification/specialized/account_needs_verification_banner.dart'; +import 'package:catalyst_voices/notification/specialized/banner/account_needs_verification_banner.dart'; import 'package:catalyst_voices/routes/routes.dart'; import 'package:catalyst_voices/widgets/snackbar/voices_snackbar.dart'; import 'package:catalyst_voices/widgets/snackbar/voices_snackbar_type.dart'; @@ -30,12 +30,7 @@ class _GlobalSessionListenerState extends State @override Widget build(BuildContext context) { - // TODO(damian-molinski): refactor it to use signals - return BlocListener( - listenWhen: _listenToSessionChangesWhen, - listener: _onSessionChanged, - child: widget.child, - ); + return widget.child; } @override @@ -47,28 +42,23 @@ class _GlobalSessionListenerState extends State : AccountContributorNeedsVerificationBanner(); CatalystMessenger.of(context).add(notification); case CancelAccountNeedsVerificationSignal(): - CatalystMessenger.of( - context, - ).cancelWhere((notification) => notification is AccountNeedsVerificationBanner); + CatalystMessenger.of(context).cancelWhere( + (notification) => notification is AccountNeedsVerificationBanner, + ); + case KeychainLockedSignal(): + _handleKeychainLock(); + case KeychainUnlockedSignal(): + _handleKeychainUnlock(); } } - bool _listenToSessionChangesWhen(SessionState prev, SessionState next) { - // We deliberately check if previous was guest because we don't - // want to show the snackbar after the registration is completed. - final keychainUnlocked = prev.isGuest && next.isActive; - final keychainLocked = prev.isActive && next.isGuest; - - return keychainUnlocked || keychainLocked; - } - - void _onLockedKeychain(BuildContext context) { + void _handleKeychainLock() { VoicesSnackBar( type: VoicesSnackBarType.error, behavior: SnackBarBehavior.floating, - icon: VoicesAssets.icons.lockClosed.buildIcon(), title: context.l10n.lockSnackbarTitle, message: context.l10n.lockSnackbarMessage, + icon: VoicesAssets.icons.lockClosed.buildIcon(), ).show(context); final routerContext = AppRouterFactory.rootNavigatorKey.currentContext; @@ -78,21 +68,13 @@ class _GlobalSessionListenerState extends State } } - void _onSessionChanged(BuildContext context, SessionState state) { - if (state.isActive) { - _onUnlockedKeychain(context); - } else if (state.isGuest) { - _onLockedKeychain(context); - } - } - - void _onUnlockedKeychain(BuildContext context) { + void _handleKeychainUnlock() { VoicesSnackBar( type: VoicesSnackBarType.success, behavior: SnackBarBehavior.floating, - icon: VoicesAssets.icons.lockOpen.buildIcon(), title: context.l10n.unlockSnackbarTitle, message: context.l10n.unlockSnackbarMessage, + icon: VoicesAssets.icons.lockOpen.buildIcon(), ).show(context); final routerContext = AppRouterFactory.rootNavigatorKey.currentContext; diff --git a/catalyst_voices/apps/voices/lib/app/view/app_splash_screen_manager.dart b/catalyst_voices/apps/voices/lib/app/view/app_splash_screen_manager.dart index 8ec5fe8b9467..b3d9f96ccf46 100644 --- a/catalyst_voices/apps/voices/lib/app/view/app_splash_screen_manager.dart +++ b/catalyst_voices/apps/voices/lib/app/view/app_splash_screen_manager.dart @@ -4,7 +4,9 @@ import 'package:catalyst_voices/app/view/app_precache_image_assets.dart'; import 'package:catalyst_voices/app/view/video_cache/app_video_manager.dart'; import 'package:catalyst_voices/dependency/dependencies.dart'; import 'package:catalyst_voices/pages/campaign_phase_aware/widgets/bubble_campaign_phase_aware_background.dart'; +import 'package:catalyst_voices/widgets/indicators/voices_linear_progress_indicator.dart'; import 'package:catalyst_voices/widgets/indicators/voices_loading_indicator.dart'; +import 'package:catalyst_voices/widgets/indicators/voices_progress_indicator_weight.dart'; import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; import 'package:catalyst_voices_services/catalyst_voices_services.dart'; @@ -43,6 +45,48 @@ class AppSplashScreenManager extends StatefulWidget { } } +class _AnimatedProgressSection extends StatelessWidget { + final String message; + final double progress; + final bool showProgressBar; + + const _AnimatedProgressSection({ + required this.message, + required this.progress, + required this.showProgressBar, + }); + + @override + Widget build(BuildContext context) { + return AnimatedOpacity( + opacity: showProgressBar ? 1.0 : 0.0, + duration: const Duration(milliseconds: 300), + child: Column( + mainAxisSize: MainAxisSize.min, + spacing: 16, + children: [ + Text( + message, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + SizedBox( + width: 360, + child: AnimatedVoicesLinearProgressIndicator( + value: progress, + animationDuration: const Duration(milliseconds: 800), + animationCurve: Curves.easeInOutCubic, + weight: VoicesProgressIndicatorWeight.heavy, + ), + ), + ], + ), + ); + } +} + class _AppSplashScreenManagerState extends State with SingleTickerProviderStateMixin { bool _areDocumentsSynced = false; @@ -50,6 +94,12 @@ class _AppSplashScreenManagerState extends State bool _messageShownEnoughTime = true; bool _fontsAreReady = false; + final Stopwatch _loadingStopwatch = Stopwatch(); + bool _showProgressIndicator = false; + Timer? _minimumVisibilityTimer; + + late final SyncManager _syncManager; + bool get _isReady => _areDocumentsSynced && _areImagesAndVideosCached && _messageShownEnoughTime && _fontsAreReady; @@ -59,17 +109,43 @@ class _AppSplashScreenManagerState extends State return widget.child; } - return _InAppLoading( - key: const Key('AppLoadingScreen'), - message: context.l10n.settingThingsAppInSplashScreen, - messageShownEnoughTime: _handleMessageShownEnoughTime, + // Throttle progress updates to reduce rebuilds + final throttledStream = _syncManager.progressStream.distinct((prev, curr) { + if (prev <= 0 || curr >= 1.0) return false; + + return (curr - prev).abs() < 0.008; // ~0.8% minimum change + }); + + return StreamBuilder( + stream: throttledStream, + initialData: 0, + builder: (context, snapshot) { + final progress = snapshot.data ?? 0; + final shouldShow = _handleProgressBarVisibility(progress); + + return _InAppLoading( + key: const Key('AppLoadingScreen'), + message: context.l10n.settingThingsAppInSplashScreen, + progress: progress, + showProgressBar: shouldShow, + ); + }, ); } + @override + void dispose() { + _minimumVisibilityTimer?.cancel(); + super.dispose(); + } + @override void initState() { super.initState(); + _syncManager = Dependencies.instance.get(); + _loadingStopwatch.start(); + WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { AppSplashScreenManager.hideSplashScreen(); @@ -95,10 +171,9 @@ class _AppSplashScreenManagerState extends State } Future _handleDocumentsSync() async { - final syncManager = Dependencies.instance.get(); final campaignPhaseAwareCubit = context.read(); - await syncManager.waitForSync; + await _syncManager.waitForSync; await campaignPhaseAwareCubit.awaitForInitialize; if (mounted) { @@ -111,7 +186,8 @@ class _AppSplashScreenManagerState extends State Future _handleFonts() async { try { - await GoogleFonts.pendingFonts(); + final profiler = Dependencies.instance.get(); + await profiler.awaitingFonts(body: () async => GoogleFonts.pendingFonts()); } catch (error, stackTrace) { _logger.warning('Load pending google fonts', error, stackTrace); } finally { @@ -140,35 +216,58 @@ class _AppSplashScreenManagerState extends State } } - void _handleMessageShownEnoughTime(bool value) { - if (mounted) { - setState(() { - _messageShownEnoughTime = value; - _finishStartupProfilerIfReady(); - }); + bool _handleProgressBarVisibility(double progress) { + final elapsed = _loadingStopwatch.elapsedMilliseconds; + var showProgressBar = false; + + if (progress > 0 && progress < 1.0) { + final estimatedTotal = elapsed / progress; + final estimatedTimeRemaining = estimatedTotal - elapsed; + showProgressBar = elapsed > 500 && estimatedTimeRemaining > 500; + + if (showProgressBar && !_showProgressIndicator) { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted && !_showProgressIndicator) { + setState(() { + _showProgressIndicator = true; + }); + _startMinimumVisibilityTimer(); + } + }); + } } + + return showProgressBar || _showProgressIndicator; + } + + void _startMinimumVisibilityTimer() { + _minimumVisibilityTimer?.cancel(); + + // After 2 seconds, hide progress indicator and mark message as shown long enough + _minimumVisibilityTimer = Timer(const Duration(seconds: 2), () { + if (mounted) { + setState(() { + _showProgressIndicator = false; + _messageShownEnoughTime = true; + _finishStartupProfilerIfReady(); + }); + } + }); } } -class _InAppLoading extends StatefulWidget { +class _InAppLoading extends StatelessWidget { final String message; - final ValueChanged messageShownEnoughTime; + final double progress; + final bool showProgressBar; const _InAppLoading({ super.key, required this.message, - required this.messageShownEnoughTime, + this.progress = 0, + this.showProgressBar = false, }); - @override - State<_InAppLoading> createState() => _InAppLoadingState(); -} - -class _InAppLoadingState extends State<_InAppLoading> { - bool _showMessage = false; - Timer? _messageTimer; - Timer? _minimumLoadingTimer; - @override Widget build(BuildContext context) { return Scaffold( @@ -185,16 +284,10 @@ class _InAppLoadingState extends State<_InAppLoading> { const VoicesLoadingIndicator( key: Key('PersistentLoadingIndicator'), ), - AnimatedOpacity( - opacity: _showMessage ? 1.0 : 0.0, - duration: const Duration(milliseconds: 300), - child: Text( - widget.message, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - ), - ), + _AnimatedProgressSection( + message: message, + progress: progress, + showProgressBar: showProgressBar, ), ], ), @@ -203,33 +296,4 @@ class _InAppLoadingState extends State<_InAppLoading> { ), ); } - - @override - void dispose() { - _messageTimer?.cancel(); - _messageTimer = null; - - _minimumLoadingTimer?.cancel(); - _minimumLoadingTimer = null; - super.dispose(); - } - - @override - void initState() { - super.initState(); - - _messageTimer = Timer(const Duration(seconds: 2), () { - if (mounted) { - setState(() { - _showMessage = true; - }); - - _minimumLoadingTimer = Timer(const Duration(seconds: 2), () { - if (mounted) { - widget.messageShownEnoughTime(true); - } - }); - } - }); - } } diff --git a/catalyst_voices/apps/voices/lib/app/view/app_system_status_listener.dart b/catalyst_voices/apps/voices/lib/app/view/app_system_status_listener.dart index eb97c6567813..edc89cf4afcf 100644 --- a/catalyst_voices/apps/voices/lib/app/view/app_system_status_listener.dart +++ b/catalyst_voices/apps/voices/lib/app/view/app_system_status_listener.dart @@ -1,6 +1,9 @@ +import 'dart:async'; + import 'package:catalyst_voices/common/signal_handler.dart'; import 'package:catalyst_voices/notification/catalyst_messenger.dart'; -import 'package:catalyst_voices/notification/specialized/system_status_issue_banner.dart'; +import 'package:catalyst_voices/notification/specialized/banner/new_version_available_banner.dart'; +import 'package:catalyst_voices/notification/specialized/banner/system_status_issue_banner.dart'; import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; import 'package:flutter/material.dart'; @@ -26,6 +29,8 @@ class _SystemStatusListenerState extends State @override void handleSignal(SystemStatusSignal signal) { switch (signal) { + case NewVersionAvailable(): + CatalystMessenger.of(context).add(const NewVersionAvailableBanner()); case SystemStatusIssueSignal(): CatalystMessenger.of(context).add(SystemStatusIssueBanner()); case CancelSystemStatusIssueSignal(): @@ -34,4 +39,10 @@ class _SystemStatusListenerState extends State ).cancelWhere((notification) => notification is SystemStatusIssueBanner); } } + + @override + void initState() { + super.initState(); + unawaited(context.read().checkAppVersion()); + } } diff --git a/catalyst_voices/apps/voices/lib/app/view/video_cache/app_video_manager.dart b/catalyst_voices/apps/voices/lib/app/view/video_cache/app_video_manager.dart index 702b6254693b..75aed8d07696 100644 --- a/catalyst_voices/apps/voices/lib/app/view/video_cache/app_video_manager.dart +++ b/catalyst_voices/apps/voices/lib/app/view/video_cache/app_video_manager.dart @@ -8,6 +8,8 @@ import 'package:flutter/material.dart'; import 'package:synchronized/synchronized.dart'; import 'package:video_player/video_player.dart'; +final _logger = Logger('AppVideoManager'); + /// Caches [VideoPlayerController] so it can be initialized and reused in different parts /// of app. class VideoManager extends ValueNotifier { @@ -21,8 +23,9 @@ class VideoManager extends ValueNotifier { Future get isInitialized => _isInitialized.future; Future createOrReinitializeController( - VideoCacheKey asset, - ) async { + VideoCacheKey asset, { + VideoPlaybackConfig config = const VideoPlaybackConfig(), + }) async { final key = _createKey(asset.name, asset.package); if (value.controllers.containsKey(key)) { final controller = value.controllers[key]!; @@ -31,13 +34,16 @@ class VideoManager extends ValueNotifier { // to a new VideoPlayer widget instance, even though controller state remains unchanged // it has to do with internal logic of VideoPlayer widget that is not exposed to us await controller.initialize(); - await controller.play(); + await controller.applyConfig(config); return controller; } - final controller = await _initializeController(asset.name, package: asset.package); + final controller = await _initializeController( + asset.name, + package: asset.package, + config: config, + ); final newControllers = Map.of(value.controllers)..[key] = controller; - value = value.copyWith(controllers: newControllers); return controller; @@ -91,20 +97,22 @@ class VideoManager extends ValueNotifier { Future _initializeController( String asset, { String? package, + VideoPlaybackConfig config = const VideoPlaybackConfig(), }) async { final controller = VideoPlayerController.asset( asset, package: package, ); - await controller.initialize(); - // TODO(LynxLynxx): extract this to public method so interested widget can - // control video playback - await controller.setLooping(true); - await controller.setVolume(0); - await controller.play(); - - return controller; + try { + await controller.initialize(); + await controller.applyConfig(config); + return controller; + } catch (e) { + _logger.severe('Failed to initialize video controller for $asset: $e'); + await controller.dispose(); + rethrow; + } } Future _precacheVideos( @@ -121,8 +129,12 @@ class VideoManager extends ValueNotifier { final key = _createKey(asset, videoAssets.package); if (value.controllers.containsKey(key)) return; - final controller = await _initializeController(asset, package: videoAssets.package); - newControllers[key] = controller; + try { + final controller = await _initializeController(asset, package: videoAssets.package); + newControllers[key] = controller; + } catch (e) { + _logger.info('Skipping video asset $asset due to error: $e'); + } }), ); @@ -176,3 +188,18 @@ class VideoManagerState extends Equatable { ); } } + +/// Extension methods for [VideoPlayerController] to apply playback configuration. +extension on VideoPlayerController { + /// Applies the given [VideoPlaybackConfig] to this controller. + /// + /// This sets the looping behavior, volume, and optionally starts playback + /// based on the configuration. + Future applyConfig(VideoPlaybackConfig config) async { + await setLooping(config.looping); + await setVolume(config.volume); + if (config.autoPlay) { + await play(); + } + } +} diff --git a/catalyst_voices/apps/voices/lib/common/ext/account_role_ext.dart b/catalyst_voices/apps/voices/lib/common/ext/account_role_ext.dart index 7bb9aa958729..b9b32a0e4699 100644 --- a/catalyst_voices/apps/voices/lib/common/ext/account_role_ext.dart +++ b/catalyst_voices/apps/voices/lib/common/ext/account_role_ext.dart @@ -6,9 +6,9 @@ import 'package:flutter/material.dart'; extension AccountRoleExt on AccountRole { SvgGenImage get icon { return switch (this) { - AccountRole.voter => VoicesAssets.images.roleVoter, - AccountRole.drep => VoicesAssets.images.roleDrep, - AccountRole.proposer => VoicesAssets.images.roleProposer, + AccountRole.voter => VoicesAssets.images.svg.roleVoter, + AccountRole.drep => VoicesAssets.images.svg.roleDrep, + AccountRole.proposer => VoicesAssets.images.svg.roleProposer, }; } diff --git a/catalyst_voices/apps/voices/lib/configs/bootstrap.dart b/catalyst_voices/apps/voices/lib/configs/bootstrap.dart index 27b00930a8c4..65da6dcf5d7e 100644 --- a/catalyst_voices/apps/voices/lib/configs/bootstrap.dart +++ b/catalyst_voices/apps/voices/lib/configs/bootstrap.dart @@ -19,14 +19,11 @@ import 'package:go_router/go_router.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:url_strategy/url_strategy.dart'; -const CatalystProfiler _profiler = _shouldUseSentry - ? CatalystSentryProfiler() - : CatalystNoopProfiler(); const ReportingService _reportingService = _shouldUseSentry ? SentryReportingService() : NoopReportingService(); -const _shouldUseSentry = kReleaseMode || kProfileMode; +const _shouldUseSentry = kReleaseMode; var _bootstrapInitState = const _BootstrapState(); @@ -71,9 +68,14 @@ Future bootstrap({ final config = await _getAppConfig(env: environment.type); _bootstrapInitState = _bootstrapInitState.copyWith(appConfig: Optional(config)); + if (config.stressTest.isEnabled) { + _debugPrintStressTest(); + } + final endConfigTimestamp = DateTimeExt.now(utc: true); - await _reportingService.init(config: config.sentry); + await _initReportingService(config.sentry); + await cleanUpStorages(onlyOld: true); if (!_bootstrapInitState.didInitializeCryptoUtils) { await _initCryptoUtils(); @@ -81,19 +83,24 @@ Future bootstrap({ } // profilers - final startupProfiler = CatalystStartupProfiler(_profiler) + final profiler = _createProfiler(config); + + final startupProfiler = CatalystStartupProfiler(profiler) ..start(at: bootstrapStartTimestamp) ..appConfig( fromTo: DateRange(from: startConfigTimestamp, to: endConfigTimestamp), ); + final runtimeProfiler = CatalystRuntimeProfiler(profiler)..start(at: bootstrapStartTimestamp); + await Dependencies.instance.init( config: config, environment: environment, loggingService: _loggingService, reportingService: _reportingService, - profiler: _profiler, + profiler: profiler, startupProfiler: startupProfiler, + runtimeProfiler: runtimeProfiler, ); final router = buildAppRouter(initialLocation: initialLocation); @@ -102,6 +109,10 @@ Future bootstrap({ // something Bloc.observer = AppBlocObserver(logOnChange: false); + if (config.stressTest.isEnabled && config.stressTest.clearDatabase) { + await Dependencies.instance.get().clear(); + } + Dependencies.instance.get().init(); unawaited( startupProfiler.documentsSync(body: () => Dependencies.instance.get().start()), @@ -197,6 +208,32 @@ Future registerDependencies({ ); } +/// Creates the appropriate profiler based on the current build mode. +/// +/// Returns: +/// - [CatalystDeveloperProfiler] when running in profile mode +/// - [CatalystSentryProfiler] when running in release mode +/// - [CatalystNoopProfiler] for debug mode (no overhead) +CatalystProfiler _createProfiler(AppConfig config) { + if (kProfileMode) { + return CatalystDeveloperProfiler.fromConfig(config.developerProfiler); + } + + if (_shouldUseSentry) { + return const CatalystSentryProfiler(); + } + + return const CatalystNoopProfiler(); +} + +void _debugPrintStressTest() { + if (!kProfileMode) { + debugPrint('Warning. StressTest is enabled for non profile mode'); + } else { + debugPrint('Running in StressTest environment'); + } +} + Widget _defaultBuilder(BootstrapArgs args) { return App( routerConfig: args.routerConfig, @@ -225,7 +262,8 @@ Future _doBootstrapAndRun( Future _getAppConfig({ required AppEnvironmentType env, }) async { - final api = ApiServices(env: env); + final config = ApiConfig(env: env); + final api = ApiServices(config: config); final source = ApiConfigSource(api); final service = ConfigService(ConfigRepository(source)); return service.getAppConfig(env: env); @@ -240,6 +278,18 @@ Future _initCryptoUtils() async { CatalystSignature.factory = const Bip32Ed25519XCatalystSignatureFactory(); } +Future _initReportingService(SentryConfig sentryConfig) async { + await _reportingService.init(config: sentryConfig).onError( + (error, stackTrace) { + _loggerBootstrap.info( + 'Failed to initialize ReportingService. App will continue without error reporting.', + error, + stackTrace, + ); + }, + ); +} + Future _reportBootstrapError(Object error, StackTrace stack) async { _loggerBootstrap.severe('Error while bootstrapping', error, stack); } diff --git a/catalyst_voices/apps/voices/lib/dependency/dependencies.dart b/catalyst_voices/apps/voices/lib/dependency/dependencies.dart index 65a62f3f3e91..342dd0eed4b7 100644 --- a/catalyst_voices/apps/voices/lib/dependency/dependencies.dart +++ b/catalyst_voices/apps/voices/lib/dependency/dependencies.dart @@ -42,6 +42,7 @@ final class Dependencies extends DependencyProvider { required ReportingService reportingService, CatalystProfiler? profiler, CatalystStartupProfiler? startupProfiler, + CatalystRuntimeProfiler? runtimeProfiler, }) async { DependencyProvider.instance = this; @@ -55,6 +56,9 @@ final class Dependencies extends DependencyProvider { if (startupProfiler != null) { registerSingleton(startupProfiler); } + if (runtimeProfiler != null) { + registerSingleton(runtimeProfiler); + } _registerStorages(); _registerUtils(); @@ -84,7 +88,7 @@ final class Dependencies extends DependencyProvider { () => get(), ) ..registerLazySingleton( - () => SystemStatusCubit(get()), + () => SystemStatusCubit(get()), ) ..registerLazySingleton( () { @@ -213,8 +217,16 @@ final class Dependencies extends DependencyProvider { void _registerNetwork() { registerLazySingleton( () { + final appConfig = get(); + final appEnvironment = get(); + + final config = ApiConfig( + env: appEnvironment.type, + localGateway: LocalGatewayConfig.stressTest(appConfig.stressTest), + ); + return ApiServices( - env: get().type, + config: config, authTokenProvider: get(), httpClient: () => get().buildHttpClient(), ); @@ -237,9 +249,10 @@ final class Dependencies extends DependencyProvider { return BlockchainRepository(get()); }) ..registerLazySingleton(() { - return const SignedDocumentManager( - brotli: CatalystBrotliCompressor(), - zstd: CatalystZstdCompressor(), + return SignedDocumentManager( + brotli: const CatalystBrotliCompressor(), + zstd: const CatalystZstdCompressor(), + profiler: get(), ); }) ..registerLazySingleton(() { @@ -438,6 +451,12 @@ final class Dependencies extends DependencyProvider { }, dispose: (mediator) => mediator.dispose(), ); + registerLazySingleton(() { + return SystemStatusService( + get(), + get(), + ); + }); } void _registerStorages() { diff --git a/catalyst_voices/apps/voices/lib/notification/catalyst_messenger.dart b/catalyst_voices/apps/voices/lib/notification/catalyst_messenger.dart index 7999ede989fe..d222742a0abb 100644 --- a/catalyst_voices/apps/voices/lib/notification/catalyst_messenger.dart +++ b/catalyst_voices/apps/voices/lib/notification/catalyst_messenger.dart @@ -5,6 +5,7 @@ import 'package:catalyst_voices/notification/banner_close_button.dart'; import 'package:catalyst_voices/notification/banner_content.dart'; import 'package:catalyst_voices/notification/catalyst_notification.dart'; import 'package:catalyst_voices/routes/app_router_factory.dart'; +import 'package:catalyst_voices/widgets/modals/voices_dialog.dart'; import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; import 'package:flutter/material.dart'; @@ -37,31 +38,52 @@ class CatalystMessenger extends StatefulWidget { } class CatalystMessengerState extends State { - final _pending = []; - bool _isShowing = false; - CatalystNotification? _activeNotification; + final _pendingDialogs = []; + final _pendingBanners = []; + bool _isShowingBanner = false; + bool _isShowingDialog = false; + BannerNotification? _activeBanner; + DialogNotification? _activeDialog; GoRouter? __router; + bool _routerListenerAttached = false; + bool _retryScheduled = false; - GoRouter get _router { - return __router ??= _findRouter()..routerDelegate.addListener(_handleRouterChange); + GoRouter? get _router { + if (__router != null) return __router; + + final router = _tryFindRouter(); + if (router != null && !_routerListenerAttached) { + router.routerDelegate.addListener(_handleRouterChange); + _routerListenerAttached = true; + __router = router; + } + return router; } /// Adds a notification to the queue if it is not already present. /// /// This method ensures that duplicate notifications are not added to the queue. - /// If the notification already exists in the `_pending` queue, it logs a message - /// and skips adding it. Otherwise, the notification is added to the queue in a - /// sorted order, and the queue is processed to display notifications. + /// Notifications are routed to the appropriate queue (dialogs or banners) + /// and sorted by priority within each queue. void add(CatalystNotification notification) { - if (_pending.contains(notification)) { - _logger.fine('$notification already in queue, skipping add'); - return; - } - _logger.finest('Adding $notification to queue'); - _addSorted(notification); + switch (notification) { + case DialogNotification(): + if (_pendingDialogs.any((n) => n.id == notification.id)) { + _logger.fine('$notification already in dialogs queue, skipping add'); + return; + } + _addToQueue(notification); + case BannerNotification(): + if (_pendingBanners.any((n) => n.id == notification.id)) { + _logger.fine('$notification already in banners queue, skipping add'); + return; + } + _addToQueue(notification); + } + _processQueue(); } @@ -71,10 +93,16 @@ class CatalystMessengerState extends State { } void cancelWhere(CatalystNotificationPredicate test) { - _pending.removeWhere(test); + _pendingDialogs.removeWhere(test); + _pendingBanners.removeWhere(test); - final activeNotification = _activeNotification; - if (activeNotification != null && test(activeNotification)) { + final activeDialog = _activeDialog; + if (activeDialog != null && test(activeDialog)) { + _hideCurrentDialog(); + } + + final activeBanner = _activeBanner; + if (activeBanner != null && test(activeBanner)) { _hideCurrentBanner(); } } @@ -87,42 +115,59 @@ class CatalystMessengerState extends State { super.dispose(); } - void _addSorted(CatalystNotification notification) { - _pending - ..add(notification) - ..sort(); + void _addToQueue(CatalystNotification notification) { + switch (notification) { + case BannerNotification(): + _pendingBanners + ..add(notification) + ..sort(); + break; + case DialogNotification(): + _pendingDialogs + ..add(notification) + ..sort(); + } } - GoRouter _findRouter() { + GoRouter? _tryFindRouter() { final navigatorContext = AppRouterFactory.rootNavigatorKey.currentContext; - assert(navigatorContext != null, 'Navigation context not available'); + if (navigatorContext == null) { + _logger.finest('Navigation context not yet available, deferring queue processing'); + return null; + } - return GoRouter.of(navigatorContext!); + return GoRouter.of(navigatorContext); } void _handleRouterChange() { - final activeNotification = _activeNotification; - if (activeNotification == null) { - if (_pending.isNotEmpty) { - _processQueue(); - } - return; - } + final router = _router; + if (router == null) return; // Router should always exist if this listener fires - final routerState = _router.state; + final routerState = router.state; - // if active notification is still valid for router do nothing. - if (activeNotification.routerPredicate(routerState)) { - return; + // Handle active dialog + final activeDialog = _activeDialog; + if (activeDialog != null && !activeDialog.routerPredicate(routerState)) { + _logger.finer('Hiding dialog(${activeDialog.id}). Not valid for router state'); + _addToQueue(activeDialog); + _hideCurrentDialog(); } - _logger.finer('Hiding notification(${activeNotification.id}). Not valid for router state'); + // Handle active banner + final activeBanner = _activeBanner; + if (activeBanner != null && !activeBanner.routerPredicate(routerState)) { + _logger.finer('Hiding banner(${activeBanner.id}). Not valid for router state'); + _addToQueue(activeBanner); + _hideCurrentBanner(); + } - _addSorted(activeNotification); - _hideCurrentBanner(); + // Process queue if there are pending notifications + if (_pendingDialogs.isNotEmpty || _pendingBanners.isNotEmpty) { + _processQueue(); + } } - /// Hiding current banner will trigger _onNotificationCompleted and process queue. + /// Hiding current banner will trigger _onBannerCompleted and process queue. void _hideCurrentBanner() { final messengerState = ScaffoldMessenger.maybeOf(context); if (messengerState == null) { @@ -131,45 +176,98 @@ class CatalystMessengerState extends State { messengerState.removeCurrentMaterialBanner(reason: MaterialBannerClosedReason.hide); } - void _onNotificationCompleted() { - assert(_activeNotification != null, 'Completed notification but active was null'); - final activeNotification = _activeNotification!; + /// Hiding current dialog will trigger _onDialogCompleted and process queue. + void _hideCurrentDialog() { + final router = _router; + if (router == null) return; - _logger.finer('Completed $activeNotification'); + final navigatorContext = router.routerDelegate.navigatorKey.currentContext; + if (navigatorContext == null) { + return; + } + Navigator.of(navigatorContext, rootNavigator: true).pop(); + } - _isShowing = false; - _activeNotification = null; + void _onBannerCompleted() { + assert(_activeBanner != null, 'Completed banner but active was null'); + final activeBanner = _activeBanner!; + + _logger.finer('Completed banner $activeBanner'); + + _isShowingBanner = false; + _activeBanner = null; + + _processQueue(); + } + + void _onDialogCompleted() { + assert(_activeDialog != null, 'Completed dialog but active was null'); + final activeDialog = _activeDialog!; + + _logger.finer('Completed dialog $activeDialog'); + + _isShowingDialog = false; + _activeDialog = null; _processQueue(); } void _processQueue() { - if (_isShowing) { + final router = _router; + if (router == null) { + // Navigation context not ready yet, schedule retry after next frame + final totalPending = _pendingDialogs.length + _pendingBanners.length; + if (totalPending > 0 && !_retryScheduled) { + _retryScheduled = true; + _logger.finest('Router not available, scheduling retry for $totalPending notification(s)'); + WidgetsBinding.instance.addPostFrameCallback((_) { + _retryScheduled = false; + if (mounted) { + _processQueue(); + } + }); + } return; } - final routerState = _router.state; - final allowed = _pending.where((notification) => notification.routerPredicate(routerState)); - if (allowed.isEmpty) { - if (_pending.isNotEmpty) { - _logger.finest('Found ${_pending.length} notification but none allow for router state'); + final routerState = router.state; + + // Filter notifications that are allowed for current router state + final allowedDialogs = _pendingDialogs.where((n) => n.routerPredicate(routerState)); + final allowedBanners = _pendingBanners.where((n) => n.routerPredicate(routerState)); + + if (allowedDialogs.isEmpty && allowedBanners.isEmpty) { + final totalPending = _pendingDialogs.length + _pendingBanners.length; + if (totalPending > 0) { + _logger.finest('Found $totalPending notification(s) but none allow for router state'); } return; } - final notification = allowed.first; - _pending.removeWhere((element) => element.id == notification.id); - _activeNotification = notification; + // Get next notification respecting priority across both queues + final nextDialog = allowedDialogs.firstOrNull; + final nextBanner = allowedBanners.firstOrNull; + + // Banners and dialog can be shown at the same time + if (!_isShowingDialog && nextDialog != null) { + _pendingDialogs.removeWhere((element) => element.id == nextDialog.id); + _activeDialog = nextDialog; + _isShowingDialog = true; - _isShowing = true; + _logger.finer('Showing dialog $nextDialog'); - _logger.finer('Showing $notification'); + unawaited(_showDialog(nextDialog).whenComplete(_onDialogCompleted)); + } - final future = switch (notification) { - BannerNotification() => _showBanner(notification), - }; + if (!_isShowingBanner && nextBanner != null) { + _pendingBanners.removeWhere((element) => element.id == nextBanner.id); + _activeBanner = nextBanner; + _isShowingBanner = true; - unawaited(future.whenComplete(_onNotificationCompleted)); + _logger.finer('Showing banner $nextBanner'); + + unawaited(_showBanner(nextBanner).whenComplete(_onBannerCompleted)); + } } Future _showBanner(BannerNotification notification) async { @@ -198,4 +296,43 @@ class CatalystMessengerState extends State { }, ); } + + Future _showDialog(DialogNotification notification) async { + // Wait for all pending frames to complete. Navigation (especially with + // redirects) can take multiple frames. By waiting while Flutter has + // scheduled frames, we ensure navigation has fully completed. + await WidgetsBinding.instance.endOfFrame; + + // Check if the dialog is still active after navigation + if (_activeDialog?.id != notification.id) { + return; + } + + final router = _router; + if (router == null) { + _logger.finest('Router not available when showing dialog ${notification.id}'); + return; + } + + // Verify the dialog is still allowed for the current route + if (!notification.routerPredicate(router.state)) { + _logger.finer('Dialog ${notification.id} no longer valid for current route after navigation'); + return; + } + + final navigatorContext = router.routerDelegate.navigatorKey.currentContext; + if (navigatorContext == null || !navigatorContext.mounted) { + return; + } + + final widget = notification.buildDialog(navigatorContext); + + if (navigatorContext.mounted) { + await VoicesDialog.show( + context: navigatorContext, + routeSettings: RouteSettings(name: '/dialog-${notification.id}'), + builder: (context) => widget, + ); + } + } } diff --git a/catalyst_voices/apps/voices/lib/notification/catalyst_notification.dart b/catalyst_voices/apps/voices/lib/notification/catalyst_notification.dart index 19a3bedd4dbb..6b68b4031cd4 100644 --- a/catalyst_voices/apps/voices/lib/notification/catalyst_notification.dart +++ b/catalyst_voices/apps/voices/lib/notification/catalyst_notification.dart @@ -6,6 +6,7 @@ import 'package:go_router/go_router.dart'; part 'banner_notification.dart'; part 'catalyst_notification_text.dart'; +part 'dialog_notification.dart'; bool _alwaysAllowRouterPredicate(GoRouterState state) => true; diff --git a/catalyst_voices/apps/voices/lib/notification/dialog_notification.dart b/catalyst_voices/apps/voices/lib/notification/dialog_notification.dart new file mode 100644 index 000000000000..a0310ded8bd6 --- /dev/null +++ b/catalyst_voices/apps/voices/lib/notification/dialog_notification.dart @@ -0,0 +1,12 @@ +part of 'catalyst_notification.dart'; + +abstract base class DialogNotification extends CatalystNotification { + const DialogNotification({ + required super.id, + super.priority, + super.routerPredicate, + super.type, + }); + + Widget buildDialog(BuildContext context); +} diff --git a/catalyst_voices/apps/voices/lib/notification/specialized/account_needs_verification_banner.dart b/catalyst_voices/apps/voices/lib/notification/specialized/banner/account_needs_verification_banner.dart similarity index 95% rename from catalyst_voices/apps/voices/lib/notification/specialized/account_needs_verification_banner.dart rename to catalyst_voices/apps/voices/lib/notification/specialized/banner/account_needs_verification_banner.dart index 91b1a33343a9..da71f33101c6 100644 --- a/catalyst_voices/apps/voices/lib/notification/specialized/account_needs_verification_banner.dart +++ b/catalyst_voices/apps/voices/lib/notification/specialized/banner/account_needs_verification_banner.dart @@ -39,7 +39,7 @@ final class AccountProposerNeedsVerificationBanner extends AccountNeedsVerificat @override BannerNotificationMessage message(BuildContext context) { return BannerNotificationMessage( - text: context.l10n.emailNotVerifiedBannerProposerMessage, + text: context.l10n.emailNotVerifiedBannerProposerMessage('{destination}'), placeholders: { 'destination': CatalystNotificationTextPart( text: context.l10n.myAccount, diff --git a/catalyst_voices/apps/voices/lib/notification/specialized/banner/new_version_available_banner.dart b/catalyst_voices/apps/voices/lib/notification/specialized/banner/new_version_available_banner.dart new file mode 100644 index 000000000000..cb75e19d9e76 --- /dev/null +++ b/catalyst_voices/apps/voices/lib/notification/specialized/banner/new_version_available_banner.dart @@ -0,0 +1,38 @@ +import 'package:catalyst_voices/notification/catalyst_notification.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:flutter/material.dart'; + +final class NewVersionAvailableBanner extends BannerNotification { + const NewVersionAvailableBanner() + : super( + id: 'newVersionAvailableBannerId', + priority: 1, + ); + + @override + BannerNotificationMessage message(BuildContext context) { + if (CatalystPlatform.isWeb) { + return BannerNotificationMessage( + text: context.l10n.newAppVersionAvailableDescriptionWeb, + ); + } else { + return BannerNotificationMessage( + text: '{placeholder}', + placeholders: { + 'placeholder': CatalystNotificationTextPart( + text: context.l10n.newAppVersionAvailableDescriptionWeb, + onTap: (context) { + // TODO(LynxLynxx): Implement this method for mobile + }, + ), + }, + ); + } + } + + @override + String title(BuildContext context) { + return context.l10n.newAppVersionAvailableTitle; + } +} diff --git a/catalyst_voices/apps/voices/lib/notification/specialized/system_status_issue_banner.dart b/catalyst_voices/apps/voices/lib/notification/specialized/banner/system_status_issue_banner.dart similarity index 93% rename from catalyst_voices/apps/voices/lib/notification/specialized/system_status_issue_banner.dart rename to catalyst_voices/apps/voices/lib/notification/specialized/banner/system_status_issue_banner.dart index 3b8e0b8de293..fe665ea764e8 100644 --- a/catalyst_voices/apps/voices/lib/notification/specialized/system_status_issue_banner.dart +++ b/catalyst_voices/apps/voices/lib/notification/specialized/banner/system_status_issue_banner.dart @@ -12,15 +12,10 @@ final class SystemStatusIssueBanner extends BannerNotification with LaunchUrlMix priority: 1, ); - @override - String title(BuildContext context) { - return context.l10n.systemStatusIssueBannerTitle; - } - @override BannerNotificationMessage message(BuildContext context) { return BannerNotificationMessage( - text: context.l10n.systemStatusIssueBannerMessage, + text: context.l10n.systemStatusIssueBannerMessage('{readMoreLink}'), placeholders: { 'readMoreLink': CatalystNotificationTextPart( text: context.l10n.readMore, @@ -32,4 +27,9 @@ final class SystemStatusIssueBanner extends BannerNotification with LaunchUrlMix }, ); } + + @override + String title(BuildContext context) { + return context.l10n.systemStatusIssueBannerTitle; + } } diff --git a/catalyst_voices/apps/voices/lib/notification/specialized/dialog/keychain_deleted_dialog_notification.dart b/catalyst_voices/apps/voices/lib/notification/specialized/dialog/keychain_deleted_dialog_notification.dart new file mode 100644 index 000000000000..90305059401e --- /dev/null +++ b/catalyst_voices/apps/voices/lib/notification/specialized/dialog/keychain_deleted_dialog_notification.dart @@ -0,0 +1,38 @@ +import 'package:catalyst_voices/notification/catalyst_notification.dart'; +import 'package:catalyst_voices/routes/routing/root_route.dart'; +import 'package:catalyst_voices/widgets/buttons/voices_filled_button.dart'; +import 'package:catalyst_voices/widgets/modals/voices_info_dialog.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:flutter/material.dart'; + +final class KeychainDeletedDialogNotification extends DialogNotification { + KeychainDeletedDialogNotification() + : super( + id: 'keychainDeleteDialog', + routerPredicate: (state) => RootRoute.rootRouteNameOptions.contains(state.name), + ); + + @override + Widget buildDialog(BuildContext context) { + return const _KeychainDeletedDialog(); + } +} + +class _KeychainDeletedDialog extends StatelessWidget { + const _KeychainDeletedDialog(); + + @override + Widget build(BuildContext context) { + return VoicesInfoDialog( + icon: VoicesAssets.icons.checkCircle.buildIcon(), + title: Text(context.l10n.keychainDeletedDialogTitle), + message: Text(context.l10n.keychainDeletedDialogSubtitle), + action: VoicesFilledButton( + key: const Key('KeychainDeletedDialogCloseButton'), + onTap: () => Navigator.of(context).pop(), + child: Text(context.l10n.close), + ), + ); + } +} diff --git a/catalyst_voices/apps/voices/lib/pages/account/account_page.dart b/catalyst_voices/apps/voices/lib/pages/account/account_page.dart index c842532a26a5..1b83497ecc1f 100644 --- a/catalyst_voices/apps/voices/lib/pages/account/account_page.dart +++ b/catalyst_voices/apps/voices/lib/pages/account/account_page.dart @@ -2,8 +2,9 @@ import 'dart:async'; import 'package:catalyst_voices/common/error_handler.dart'; import 'package:catalyst_voices/common/signal_handler.dart'; +import 'package:catalyst_voices/notification/catalyst_messenger.dart'; +import 'package:catalyst_voices/notification/specialized/dialog/keychain_deleted_dialog_notification.dart'; import 'package:catalyst_voices/pages/account/delete_keychain_dialog.dart'; -import 'package:catalyst_voices/pages/account/keychain_deleted_dialog.dart'; import 'package:catalyst_voices/pages/account/pending_email_change_dialog.dart'; import 'package:catalyst_voices/pages/account/verification_email_send_dialog.dart'; import 'package:catalyst_voices/pages/account/widgets/account_email_tile.dart'; @@ -16,10 +17,9 @@ import 'package:catalyst_voices/pages/account/widgets/account_username_tile.dart import 'package:catalyst_voices/pages/spaces/appbar/actions/account_settings_action.dart'; import 'package:catalyst_voices/pages/spaces/appbar/actions/session_cta_action.dart'; import 'package:catalyst_voices/pages/spaces/drawer/opportunities_drawer.dart'; -import 'package:catalyst_voices/routes/routes.dart'; +import 'package:catalyst_voices/routes/routing/root_route.dart'; import 'package:catalyst_voices/widgets/widgets.dart'; import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; -import 'package:catalyst_voices_models/catalyst_voices_models.dart'; import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; import 'package:flutter/material.dart'; @@ -115,23 +115,9 @@ class _AccountPageState extends State } if (mounted) { - unawaited(KeychainDeletedDialog.show(context)); - - // TODO(damian-molinski): refactor it. Should be inside AccountCubit and emit signals to page. - final phaseType = context.read().state.activeCampaignPhaseType; - - switch (phaseType) { - case CampaignPhaseType.communityReview: - case CampaignPhaseType.communityVoting: - const VotingRoute($extra: true).go(context); - case null: - case CampaignPhaseType.proposalSubmission: - case CampaignPhaseType.votingRegistration: - case CampaignPhaseType.reviewRegistration: - case CampaignPhaseType.votingResults: - case CampaignPhaseType.projectOnboarding: - const DiscoveryRoute($extra: true).go(context); - } + final notification = KeychainDeletedDialogNotification(); + CatalystMessenger.of(context).add(notification); + const RootRoute().go(context); } } diff --git a/catalyst_voices/apps/voices/lib/pages/account/keychain_deleted_dialog.dart b/catalyst_voices/apps/voices/lib/pages/account/keychain_deleted_dialog.dart deleted file mode 100644 index f3d99d157fc5..000000000000 --- a/catalyst_voices/apps/voices/lib/pages/account/keychain_deleted_dialog.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:catalyst_voices/widgets/widgets.dart'; -import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; -import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; -import 'package:flutter/material.dart'; - -class KeychainDeletedDialog extends StatelessWidget { - const KeychainDeletedDialog._(); - - @override - Widget build(BuildContext context) { - return VoicesInfoDialog( - icon: VoicesAssets.icons.checkCircle.buildIcon(), - title: Text(context.l10n.keychainDeletedDialogTitle), - message: Text(context.l10n.keychainDeletedDialogSubtitle), - action: VoicesFilledButton( - key: const Key('KeychainDeletedDialogCloseButton'), - onTap: () => Navigator.of(context).pop(), - child: Text(context.l10n.close), - ), - ); - } - - static Future show(BuildContext context) { - return VoicesDialog.show( - context: context, - routeSettings: const RouteSettings(name: '/deleted-keychain'), - builder: (context) => const KeychainDeletedDialog._(), - ); - } -} diff --git a/catalyst_voices/apps/voices/lib/pages/account/widgets/account_keychain_tile.dart b/catalyst_voices/apps/voices/lib/pages/account/widgets/account_keychain_tile.dart index fd4ca8514a11..5ac8c5a4e725 100644 --- a/catalyst_voices/apps/voices/lib/pages/account/widgets/account_keychain_tile.dart +++ b/catalyst_voices/apps/voices/lib/pages/account/widgets/account_keychain_tile.dart @@ -1,17 +1,11 @@ import 'dart:async'; import 'package:catalyst_voices/common/ext/build_context_ext.dart'; -import 'package:catalyst_voices/pages/account/delete_keychain_dialog.dart'; -import 'package:catalyst_voices/routes/routes.dart'; import 'package:catalyst_voices/widgets/widgets.dart'; import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; -import 'package:catalyst_voices_models/catalyst_voices_models.dart'; import 'package:flutter/material.dart'; -// TODO(damian-molinski): Remove this once KeychainDeletedDialog is migrated to CatalystMessenger. -bool showKeychainDeletedDialog = false; - class AccountKeychainTile extends StatefulWidget { final VoidCallback onRemoveTap; @@ -38,7 +32,7 @@ class _AccountKeychainTileState extends State { style: ButtonStyle( textStyle: WidgetStatePropertyAll(context.textTheme.labelSmall), ), - onTap: _removeKeychain, + onTap: widget.onRemoveTap, child: Text(context.l10n.removeKeychain), ), child: VoicesTextField( @@ -75,34 +69,4 @@ class _AccountKeychainTileState extends State { .distinct() .listen((event) => _controller.text = event); } - - Future _removeKeychain() async { - final confirmed = await DeleteKeychainDialog.show(context); - if (!confirmed) { - return; - } - - if (mounted) { - await context.read().deleteActiveKeychain(); - showKeychainDeletedDialog = true; - } - - if (mounted) { - // TODO(damian-molinski): refactor it. Should be inside AccountCubit and emit signals to page. - final phaseType = context.read().state.activeCampaignPhaseType; - - switch (phaseType) { - case CampaignPhaseType.communityReview: - case CampaignPhaseType.communityVoting: - const VotingRoute($extra: true).go(context); - case null: - case CampaignPhaseType.proposalSubmission: - case CampaignPhaseType.votingRegistration: - case CampaignPhaseType.reviewRegistration: - case CampaignPhaseType.votingResults: - case CampaignPhaseType.projectOnboarding: - const DiscoveryRoute($extra: true).go(context); - } - } - } } diff --git a/catalyst_voices/apps/voices/lib/pages/category/category_page.dart b/catalyst_voices/apps/voices/lib/pages/category/category_page.dart index e70f29b8f03d..f368ec5a8514 100644 --- a/catalyst_voices/apps/voices/lib/pages/category/category_page.dart +++ b/catalyst_voices/apps/voices/lib/pages/category/category_page.dart @@ -101,10 +101,47 @@ class _BodySmall extends StatelessWidget { } } -class _CategoryDetailErrorSelector extends StatelessWidget { +class _CategoryDetailContent extends StatelessWidget { + const _CategoryDetailContent(); + + @override + Widget build(BuildContext context) { + return BlocSelector< + CategoryDetailCubit, + CategoryDetailState, + DataVisibilityState + >( + selector: (state) { + return ( + show: state.isLoading, + data: state.category ?? CampaignCategoryDetailsViewModel.placeholder(), + ); + }, + builder: (context, state) { + final isActiveProposer = context.select( + (cubit) => cubit.state.isProposerUnlock, + ); + return ResponsiveChildBuilder( + sm: (_) => _BodySmall( + category: state.data, + isLoading: state.show, + isActiveProposer: isActiveProposer, + ), + md: (_) => _Body( + category: state.data, + isLoading: state.show, + isActiveProposer: isActiveProposer, + ), + ); + }, + ); + } +} + +class _CategoryDetailError extends StatelessWidget { final SignedDocumentRef categoryId; - const _CategoryDetailErrorSelector({required this.categoryId}); + const _CategoryDetailError({required this.categoryId}); @override Widget build(BuildContext context) { @@ -140,43 +177,6 @@ class _CategoryDetailErrorSelector extends StatelessWidget { } } -class _CategoryDetailLoadingOrDataSelector extends StatelessWidget { - const _CategoryDetailLoadingOrDataSelector(); - - @override - Widget build(BuildContext context) { - return BlocSelector< - CategoryDetailCubit, - CategoryDetailState, - DataVisibilityState - >( - selector: (state) { - return ( - show: state.isLoading, - data: state.category ?? CampaignCategoryDetailsViewModel.dummy(), - ); - }, - builder: (context, state) { - final isActiveProposer = context.select( - (cubit) => cubit.state.isProposerUnlock, - ); - return ResponsiveChildBuilder( - sm: (_) => _BodySmall( - category: state.data, - isLoading: state.show, - isActiveProposer: isActiveProposer, - ), - md: (_) => _Body( - category: state.data, - isLoading: state.show, - isActiveProposer: isActiveProposer, - ), - ); - }, - ); - } -} - class _CategoryPageState extends State { StreamSubscription? _categoryRefSub; @@ -185,8 +185,8 @@ class _CategoryPageState extends State { return ProposalSubmissionPhaseAware( activeChild: Stack( children: [ - const _CategoryDetailLoadingOrDataSelector(), - _CategoryDetailErrorSelector( + const _CategoryDetailContent(), + _CategoryDetailError( categoryId: widget.categoryId, ), ].constrainedDelegate(), diff --git a/catalyst_voices/apps/voices/lib/pages/discovery/discovery_page.dart b/catalyst_voices/apps/voices/lib/pages/discovery/discovery_page.dart index 6331e9ebe483..c84b7e1c0083 100644 --- a/catalyst_voices/apps/voices/lib/pages/discovery/discovery_page.dart +++ b/catalyst_voices/apps/voices/lib/pages/discovery/discovery_page.dart @@ -1,27 +1,19 @@ import 'dart:async'; import 'package:catalyst_voices/common/error_handler.dart'; -import 'package:catalyst_voices/pages/account/keychain_deleted_dialog.dart'; -import 'package:catalyst_voices/pages/account/widgets/account_keychain_tile.dart'; import 'package:catalyst_voices/pages/campaign_phase_aware/proposal_submission_phase_aware.dart'; +import 'package:catalyst_voices/pages/discovery/sections/campaign_details/campaign_details.dart'; import 'package:catalyst_voices/pages/discovery/sections/campaign_hero.dart'; import 'package:catalyst_voices/pages/discovery/sections/how_it_works.dart'; -import 'package:catalyst_voices/pages/discovery/sections/stay_involved.dart'; -import 'package:catalyst_voices/pages/discovery/state_selectors/campaign_categories_state_selector.dart'; -import 'package:catalyst_voices/pages/discovery/state_selectors/current_campaign_selector.dart'; -import 'package:catalyst_voices/pages/discovery/state_selectors/most_recent_proposals_selector.dart'; +import 'package:catalyst_voices/pages/discovery/sections/most_recent_proposals/most_recent_proposals.dart'; +import 'package:catalyst_voices/pages/discovery/sections/stay_involved/stay_involved.dart'; import 'package:catalyst_voices/widgets/common/infrastructure/voices_wide_screen_constrained.dart'; import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; import 'package:flutter/material.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; class DiscoveryPage extends StatefulWidget { - final bool keychainDeleted; - - const DiscoveryPage({ - super.key, - this.keychainDeleted = false, - }); + const DiscoveryPage({super.key}); @override State createState() => _DiscoveryPageState(); @@ -39,14 +31,13 @@ class _Body extends StatelessWidget { [ const CampaignHeroSection(), const HowItWorks(), - const CurrentCampaignSelector(), - const CampaignCategoriesStateSelector(), + const CampaignDetails(), const StayInvolved(), - const MostRecentProposalsSelector(), + const MostRecentProposals(), ].constrainedDelegate( excludePredicate: (widget) => widget is CampaignHeroSection || - widget is MostRecentProposalsSelector || + widget is MostRecentProposals || widget is HowItWorks, ), ), @@ -71,30 +62,11 @@ class _DiscoveryPageState extends State ); } - @override - void didUpdateWidget(DiscoveryPage oldWidget) { - super.didUpdateWidget(oldWidget); - - if (showKeychainDeletedDialog) { - showKeychainDeletedDialog = false; - WidgetsBinding.instance.addPostFrameCallback((_) async { - await _showKeychainDeletedDialog(context); - }); - } - } - @override void initState() { super.initState(); unawaited(_loadData()); - - if (showKeychainDeletedDialog) { - showKeychainDeletedDialog = false; - WidgetsBinding.instance.addPostFrameCallback((_) async { - await _showKeychainDeletedDialog(context); - }); - } } Future _loadData() async { @@ -104,8 +76,4 @@ class _DiscoveryPageState extends State if (mounted) unawaited(SentryDisplayWidget.of(context).reportFullyDisplayed()); } } - - Future _showKeychainDeletedDialog(BuildContext context) async { - await KeychainDeletedDialog.show(context); - } } diff --git a/catalyst_voices/apps/voices/lib/pages/discovery/sections/campaign_details/campaign_details.dart b/catalyst_voices/apps/voices/lib/pages/discovery/sections/campaign_details/campaign_details.dart new file mode 100644 index 000000000000..49cc1a512178 --- /dev/null +++ b/catalyst_voices/apps/voices/lib/pages/discovery/sections/campaign_details/campaign_details.dart @@ -0,0 +1,191 @@ +import 'package:catalyst_voices/common/ext/active_fund_number_selector_ext.dart'; +import 'package:catalyst_voices/common/ext/build_context_ext.dart'; +import 'package:catalyst_voices/pages/discovery/sections/campaign_details/widgets/campaign_categories.dart'; +import 'package:catalyst_voices/pages/discovery/sections/campaign_details/widgets/current_campaign.dart'; +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; +import 'package:flutter/material.dart'; + +class CampaignDetails extends StatelessWidget { + const CampaignDetails({super.key}); + + @override + Widget build(BuildContext context) { + return BlocSelector( + selector: (state) => state.campaign, + builder: (context, campaign) { + return _CampaignDetails(campaign); + }, + ); + } +} + +class _CampaignCategoriesContent extends StatelessWidget { + final List categories; + final bool isLoading; + + const _CampaignCategoriesContent({ + required this.categories, + required this.isLoading, + }); + + @override + Widget build(BuildContext context) { + final categoriesToShow = categories.isEmpty + ? List.filled(4, CampaignCategoryDetailsViewModel.placeholder()) + : categories; + + return CampaignCategories( + key: const Key('CampaignCategoriesData'), + categoriesToShow, + isLoading: isLoading, + ); + } +} + +class _CampaignData extends StatelessWidget { + final DiscoveryCampaignState campaignState; + + const _CampaignData({required this.campaignState}); + + @override + Widget build(BuildContext context) { + return Column( + key: const Key('CampaignDetailsRoot'), + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Padding( + padding: EdgeInsets.only(left: 120, top: 64, right: 120), + child: _CurrentCampaignHeader(), + ), + _CurrentCampaignContent( + currentCampaign: campaignState.currentCampaign ?? NullCurrentCampaignInfoViewModel(), + isLoading: campaignState.isLoading, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 120), + child: Text( + key: const Key('CampaignCategoriesTitle'), + context.l10n.campaignCategories, + style: context.textTheme.titleLarge, + ), + ), + const SizedBox(height: 24), + _CampaignCategoriesContent( + categories: campaignState.categories, + isLoading: campaignState.isLoading, + ), + ], + ); + } +} + +class _CampaignDetails extends StatelessWidget { + final DiscoveryCampaignState campaign; + + const _CampaignDetails(this.campaign); + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Offstage( + offstage: campaign.showError, + child: _CampaignData(campaignState: campaign), + ), + Offstage( + offstage: !campaign.showError, + child: _CampaignError( + error: campaign.error, + onRetry: () async { + await context.read().getCurrentCampaign(); + }, + ), + ), + ], + ); + } +} + +class _CampaignError extends StatelessWidget { + final LocalizedException? error; + final VoidCallback onRetry; + + const _CampaignError({ + required this.error, + required this.onRetry, + }); + + @override + Widget build(BuildContext context) { + final errorMessage = error?.message(context); + return Padding( + key: const Key('CampaignError'), + padding: const EdgeInsets.all(32), + child: Center( + child: VoicesErrorIndicator( + message: errorMessage ?? context.l10n.somethingWentWrong, + onRetry: onRetry, + ), + ), + ); + } +} + +class _CurrentCampaignContent extends StatelessWidget { + final CurrentCampaignInfoViewModel currentCampaign; + final bool isLoading; + + const _CurrentCampaignContent({ + required this.currentCampaign, + required this.isLoading, + }); + + @override + Widget build(BuildContext context) { + return CurrentCampaign( + key: const Key('CurrentCampaignData'), + currentCampaignInfo: currentCampaign, + isLoading: isLoading, + ); + } +} + +class _CurrentCampaignHeader extends StatelessWidget { + const _CurrentCampaignHeader(); + + @override + Widget build(BuildContext context) { + return ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 568), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + key: const Key('CurrentCampaignTitle'), + context.l10n.currentCampaign, + style: context.textTheme.titleSmall, + ), + const SizedBox(height: 4), + Text( + key: const Key('Subtitle'), + context.l10n.catalystFundNo(context.activeCampaignFundNumber), + style: context.textTheme.displayMedium?.copyWith( + color: context.colorScheme.primary, + ), + ), + const SizedBox(height: 16), + Text( + key: const Key('Description'), + context.l10n.currentCampaignDescription, + style: context.textTheme.bodyLarge, + ), + ], + ), + ); + } +} diff --git a/catalyst_voices/apps/voices/lib/pages/discovery/sections/campaign_categories.dart b/catalyst_voices/apps/voices/lib/pages/discovery/sections/campaign_details/widgets/campaign_categories.dart similarity index 94% rename from catalyst_voices/apps/voices/lib/pages/discovery/sections/campaign_categories.dart rename to catalyst_voices/apps/voices/lib/pages/discovery/sections/campaign_details/widgets/campaign_categories.dart index b947d88c289d..efefedfa1f88 100644 --- a/catalyst_voices/apps/voices/lib/pages/discovery/sections/campaign_categories.dart +++ b/catalyst_voices/apps/voices/lib/pages/discovery/sections/campaign_details/widgets/campaign_categories.dart @@ -32,7 +32,7 @@ class CampaignCategories extends StatelessWidget { .map( (e) => Skeletonizer( enabled: isLoading, - child: CampaignCategoryCard(category: e), + child: CampaignCategoryCard(key: ValueKey(e.id), category: e), ), ) .toList(), diff --git a/catalyst_voices/apps/voices/lib/pages/discovery/sections/current_campaign.dart b/catalyst_voices/apps/voices/lib/pages/discovery/sections/campaign_details/widgets/current_campaign.dart similarity index 100% rename from catalyst_voices/apps/voices/lib/pages/discovery/sections/current_campaign.dart rename to catalyst_voices/apps/voices/lib/pages/discovery/sections/campaign_details/widgets/current_campaign.dart diff --git a/catalyst_voices/apps/voices/lib/pages/discovery/sections/campaign_hero.dart b/catalyst_voices/apps/voices/lib/pages/discovery/sections/campaign_hero.dart index 274cbaee0685..58ad2e5f656a 100644 --- a/catalyst_voices/apps/voices/lib/pages/discovery/sections/campaign_hero.dart +++ b/catalyst_voices/apps/voices/lib/pages/discovery/sections/campaign_hero.dart @@ -2,7 +2,7 @@ import 'package:catalyst_voices/routes/routing/spaces_route.dart'; import 'package:catalyst_voices/widgets/buttons/voices_filled_button.dart'; import 'package:catalyst_voices/widgets/buttons/voices_outlined_button.dart'; import 'package:catalyst_voices/widgets/heroes/section_hero.dart'; -import 'package:catalyst_voices_assets/generated/assets.gen.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; @@ -20,6 +20,10 @@ class CampaignHeroSection extends StatelessWidget { constraints: const BoxConstraints(minHeight: 450), child: HeroSection( asset: VideoCacheKey(name: VoicesAssets.videos.heroDesktop), + errorBuilder: (context, error, stackTrace) => CatalystImage.asset( + VoicesAssets.images.discoveryFallback.path, + fit: BoxFit.cover, + ), child: ResponsivePadding( xs: const EdgeInsets.only(left: 20, bottom: 16, top: 8, right: 20), sm: const EdgeInsets.only(left: 80, bottom: 32, top: 18), diff --git a/catalyst_voices/apps/voices/lib/pages/discovery/sections/most_recent_proposals/most_recent_proposals.dart b/catalyst_voices/apps/voices/lib/pages/discovery/sections/most_recent_proposals/most_recent_proposals.dart new file mode 100644 index 000000000000..0565d3f25a9e --- /dev/null +++ b/catalyst_voices/apps/voices/lib/pages/discovery/sections/most_recent_proposals/most_recent_proposals.dart @@ -0,0 +1,99 @@ +import 'package:catalyst_voices/pages/discovery/sections/most_recent_proposals/recent_proposals.dart'; +import 'package:catalyst_voices/widgets/indicators/voices_error_indicator.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:flutter/material.dart'; + +class MostRecentProposals extends StatelessWidget { + const MostRecentProposals({super.key}); + + @override + Widget build(BuildContext context) { + return BlocSelector( + selector: (state) => state.proposals, + builder: (context, state) { + return _MostRecentProposals(data: state); + }, + ); + } +} + +class _MostRecentProposals extends StatelessWidget { + final DiscoveryMostRecentProposalsState data; + const _MostRecentProposals({ + required this.data, + }); + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + _MostRecentProposalsError(data), + _ViewAllProposals( + offstage: data.showError || !data.hasMinProposalsToShow, + ), + _MostRecentProposalsData( + data, + minProposalsToShow: data.hasMinProposalsToShow, + ), + ], + ); + } +} + +class _MostRecentProposalsData extends StatelessWidget { + final DiscoveryMostRecentProposalsState state; + final bool minProposalsToShow; + + const _MostRecentProposalsData(this.state, {this.minProposalsToShow = false}); + + @override + Widget build(BuildContext context) { + return Offstage( + key: const Key('MostRecentProposalsData'), + offstage: state.showError || !minProposalsToShow, + child: RecentProposals(proposals: state.proposals), + ); + } +} + +class _MostRecentProposalsError extends StatelessWidget { + final DiscoveryMostRecentProposalsState state; + + const _MostRecentProposalsError(this.state); + + @override + Widget build(BuildContext context) { + final errorMessage = state.error?.message(context); + return Offstage( + key: const Key('MostRecentError'), + offstage: !state.showError, + child: Padding( + padding: const EdgeInsets.all(16), + child: Center( + child: VoicesErrorIndicator( + message: errorMessage ?? context.l10n.somethingWentWrong, + onRetry: () async { + await context.read().getMostRecentProposals(); + }, + ), + ), + ), + ); + } +} + +class _ViewAllProposals extends StatelessWidget { + final bool offstage; + + const _ViewAllProposals({this.offstage = true}); + + @override + Widget build(BuildContext context) { + return Offstage( + key: const Key('MostRecentProposalsData'), + offstage: !offstage, + child: const ViewAllProposals(), + ); + } +} diff --git a/catalyst_voices/apps/voices/lib/pages/discovery/sections/most_recent_proposals.dart b/catalyst_voices/apps/voices/lib/pages/discovery/sections/most_recent_proposals/recent_proposals.dart similarity index 50% rename from catalyst_voices/apps/voices/lib/pages/discovery/sections/most_recent_proposals.dart rename to catalyst_voices/apps/voices/lib/pages/discovery/sections/most_recent_proposals/recent_proposals.dart index 86e0badfce56..a89da9f9bcbd 100644 --- a/catalyst_voices/apps/voices/lib/pages/discovery/sections/most_recent_proposals.dart +++ b/catalyst_voices/apps/voices/lib/pages/discovery/sections/most_recent_proposals/recent_proposals.dart @@ -8,23 +8,45 @@ import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; import 'package:flutter/material.dart'; -import 'package:skeletonizer/skeletonizer.dart'; -class MostRecentProposals extends StatefulWidget { +class RecentProposals extends StatelessWidget { final List proposals; - final bool isLoading; - const MostRecentProposals({ + const RecentProposals({ super.key, required this.proposals, - this.isLoading = false, }); @override - State createState() => _LatestProposalsState(); + Widget build(BuildContext context) { + return _Background( + constraints: const BoxConstraints.tightFor( + height: 800, + width: double.infinity, + ), + child: ResponsivePadding( + xs: const EdgeInsets.symmetric(horizontal: 48), + sm: const EdgeInsets.symmetric(horizontal: 48), + md: const EdgeInsets.symmetric(horizontal: 100), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 72), + const _ProposalsTitle(), + const SizedBox(height: 48), + _ProposalsScrollableList(proposals: proposals), + const SizedBox(height: 16), + const _ViewAllProposalsButton(), + const SizedBox(height: 72), + ], + ), + ), + ); + } } class ViewAllProposals extends StatelessWidget { @@ -55,7 +77,7 @@ class _Background extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - key: const Key('MostRecentProposals'), + key: const Key('RecentProposals'), constraints: constraints, decoration: BoxDecoration( image: DecorationImage( @@ -70,107 +92,118 @@ class _Background extends StatelessWidget { } } -class _LatestProposalsState extends State { - late final ScrollController _scrollController; - late double _scrollPercentage; +class _ProposalsList extends StatelessWidget { + final ScrollController scrollController; + final List proposals; + + const _ProposalsList({ + required this.scrollController, + required this.proposals, + }); @override Widget build(BuildContext context) { - return Container( - key: const Key('MostRecentProposals'), - constraints: const BoxConstraints.tightFor( - height: 800, - width: double.infinity, + return ListView.builder( + controller: scrollController, + physics: const ClampingScrollPhysics(), + scrollDirection: Axis.horizontal, + itemCount: proposals.length, + itemBuilder: (context, index) { + final proposal = proposals[index]; + final ref = proposal.selfRef; + return Padding( + key: Key('PendingProposalCard_$ref'), + padding: EdgeInsets.only(right: index < proposals.length - 1 ? 12 : 0), + child: ProposalBriefCard( + proposal: proposal, + onTap: () => _onCardTap(context, ref), + onFavoriteChanged: (value) => _onCardFavoriteChanged(context, ref, value), + ), + ); + }, + prototypeItem: Padding( + padding: const EdgeInsets.only(right: 12), + child: ProposalBriefCard(proposal: ProposalBrief.prototype()), ), - decoration: BoxDecoration( - image: DecorationImage( - image: CatalystImage.asset( - VoicesAssets.images.campaignHero.path, - ).image, - fit: BoxFit.cover, - ), - ), - child: ResponsivePadding( - xs: const EdgeInsets.symmetric(horizontal: 48), - sm: const EdgeInsets.symmetric(horizontal: 48), - md: const EdgeInsets.symmetric(horizontal: 100), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox(height: 72), - Text( - key: const Key('MostRecentProposalsTitle'), - context.l10n.mostRecent, - style: context.textTheme.headlineLarge?.copyWith( - color: ThemeBuilder.buildTheme().colors.textOnPrimaryWhite, - ), - ), - const SizedBox(height: 48), - VoicesGestureDetector( - onHorizontalDragUpdate: _onHorizontalDrag, - child: SizedBox( - height: 440, - width: 1200, - child: Center( - child: ListView.separated( - controller: _scrollController, - shrinkWrap: true, - physics: const ClampingScrollPhysics(), - scrollDirection: Axis.horizontal, - itemCount: widget.proposals.length, - itemBuilder: (context, index) { - final proposal = widget.proposals[index]; - final ref = proposal.selfRef; - return Skeletonizer( - enabled: widget.isLoading, - child: ProposalBriefCard( - key: Key('PendingProposalCard_$ref'), - proposal: proposal, - onTap: () { - unawaited( - ProposalRoute( - proposalId: ref.id, - version: ref.version, - ).push(context), - ); - }, - onFavoriteChanged: (value) async { - final bloc = context.read(); - if (value) { - await bloc.addFavorite(ref); - } else { - await bloc.removeFavorite(ref); - } - }, - ), - ); - }, - separatorBuilder: (context, index) => const SizedBox(width: 24), - ), - ), + ); + } + + Future _onCardFavoriteChanged( + BuildContext context, + DocumentRef ref, + bool isFavorite, + ) async { + final bloc = context.read(); + if (isFavorite) { + await bloc.addFavorite(ref); + } else { + await bloc.removeFavorite(ref); + } + } + + void _onCardTap(BuildContext context, DocumentRef ref) { + unawaited( + ProposalRoute( + proposalId: ref.id, + version: ref.version, + ).push(context), + ); + } +} + +class _ProposalsScrollableList extends StatefulWidget { + final List proposals; + + const _ProposalsScrollableList({required this.proposals}); + + @override + State<_ProposalsScrollableList> createState() => _ProposalsScrollableListState(); +} + +class _ProposalsScrollableListState extends State<_ProposalsScrollableList> { + late final ScrollController _scrollController; + final ValueNotifier _scrollPercentageNotifier = ValueNotifier(0); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + VoicesGestureDetector( + onHorizontalDragUpdate: _onHorizontalDrag, + child: SizedBox( + height: 440, + width: 1200, + child: Center( + child: _ProposalsList( + scrollController: _scrollController, + proposals: widget.proposals, ), ), - const SizedBox(height: 16), - ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 360), - child: VoicesSlider( + ), + ), + const SizedBox(height: 16), + ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 360), + child: ValueListenableBuilder( + valueListenable: _scrollPercentageNotifier, + builder: (context, value, child) { + return VoicesSlider( key: const Key('MostRecentProposalsSlider'), - value: _scrollPercentage, + value: value, onChanged: _onSliderChanged, - ), - ), - const SizedBox(height: 16), - const _ViewAllProposalsButton(), - const SizedBox(height: 72), - ], + ); + }, + ), ), - ), + ], ); } @override void dispose() { _scrollController.dispose(); + _scrollPercentageNotifier.dispose(); super.dispose(); } @@ -179,32 +212,50 @@ class _LatestProposalsState extends State { super.initState(); _scrollController = ScrollController(); _scrollController.addListener(_onScroll); - _scrollPercentage = 0.0; } void _onHorizontalDrag(DragUpdateDetails details) { final offset = _scrollController.offset - details.delta.dx; final overMax = offset > _scrollController.position.maxScrollExtent; - if (offset < 0 || overMax) { - return; - } - _scrollController.jumpTo( - _scrollController.offset - details.delta.dx, - ); + if (offset < 0 || overMax) return; + + _scrollController.jumpTo(offset); } void _onScroll() { final scrollPosition = _scrollController.position.pixels; final maxScroll = _scrollController.position.maxScrollExtent; - setState(() { - _scrollPercentage = scrollPosition / maxScroll; - }); + + if (maxScroll > 0) { + _scrollPercentageNotifier.value = scrollPosition / maxScroll; + } } void _onSliderChanged(double value) { final maxScroll = _scrollController.position.maxScrollExtent; - _scrollController.jumpTo(maxScroll * value); + unawaited( + _scrollController.animateTo( + maxScroll * value, + duration: const Duration(milliseconds: 200), + curve: Curves.easeOut, + ), + ); + } +} + +class _ProposalsTitle extends StatelessWidget { + const _ProposalsTitle(); + + @override + Widget build(BuildContext context) { + return Text( + key: const Key('MostRecentProposalsTitle'), + context.l10n.mostRecent, + style: context.textTheme.headlineLarge?.copyWith( + color: ThemeBuilder.buildTheme().colors.textOnPrimaryWhite, + ), + ); } } diff --git a/catalyst_voices/apps/voices/lib/pages/discovery/sections/stay_involved.dart b/catalyst_voices/apps/voices/lib/pages/discovery/sections/stay_involved.dart deleted file mode 100644 index a11a5f89d8d9..000000000000 --- a/catalyst_voices/apps/voices/lib/pages/discovery/sections/stay_involved.dart +++ /dev/null @@ -1,346 +0,0 @@ -import 'dart:async'; - -import 'package:catalyst_voices/common/constants/constants.dart'; -import 'package:catalyst_voices/common/ext/active_fund_number_selector_ext.dart'; -import 'package:catalyst_voices/common/ext/build_context_ext.dart'; -import 'package:catalyst_voices/pages/discovery/sections/session_account_catalyst_id.dart'; -import 'package:catalyst_voices/share/share_manager.dart'; -import 'package:catalyst_voices/widgets/text/campaign_stage_time_text.dart'; -import 'package:catalyst_voices/widgets/widgets.dart'; -import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; -import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; -import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; -import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; -import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; -import 'package:flutter/material.dart'; -import 'package:url_launcher/url_launcher.dart'; - -class StayInvolved extends StatelessWidget { - const StayInvolved({super.key}); - - @override - Widget build(BuildContext context) { - return ResponsivePadding( - xs: const EdgeInsets.symmetric(horizontal: 20, vertical: 20), - sm: const EdgeInsets.symmetric(horizontal: 48, vertical: 48), - md: const EdgeInsets.symmetric(horizontal: 120, vertical: 72), - child: const Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - _Header(), - SizedBox(height: 24), - Wrap( - alignment: WrapAlignment.center, - spacing: 16, - runSpacing: 16, - children: [ - _VoterCard(), - _ReviewerCard(), - ], - ), - ], - ), - ); - } -} - -class _CopyCatalystIdTipText extends StatelessWidget { - const _CopyCatalystIdTipText(); - - @override - Widget build(BuildContext context) { - return BlocSelector( - selector: (state) { - return state.isActive; - }, - builder: (context, isActive) { - return Offstage( - offstage: !isActive, - child: Padding( - padding: const EdgeInsets.only(top: 20), - child: TipText( - context.l10n.tipCopyCatalystIdForReviewTool( - ShareManager.of(context).becomeReviewer().decoded(), - ), - style: context.textTheme.bodyMedium?.copyWith( - color: context.colors.textOnPrimaryLevel1, - ), - ), - ), - ); - }, - ); - } -} - -class _DatetimeRangeTimeline extends StatelessWidget { - final DateRange? dateRange; - final String title; - - const _DatetimeRangeTimeline({ - this.dateRange, - required this.title, - }); - - @override - Widget build(BuildContext context) { - return dateRange == null - ? const SizedBox.shrink() - : Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Text( - title, - style: context.textTheme.titleSmall, - ), - CampaignStageTimeText( - dateRange: dateRange!, - ), - ], - ); - } -} - -class _Header extends StatelessWidget { - const _Header(); - - @override - Widget build(BuildContext context) { - return Text( - context.l10n.stayInvolved, - style: context.textTheme.headlineMedium, - ); - } -} - -class _RangeTimelineCard extends StatelessWidget { - final List children; - - const _RangeTimelineCard({required this.children}); - - @override - Widget build(BuildContext context) { - return Container( - width: double.infinity, - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: Theme.of(context).colors.elevationsOnSurfaceNeutralLv0, - borderRadius: BorderRadius.circular(12), - ), - child: Wrap( - alignment: WrapAlignment.spaceBetween, - spacing: 8, - runSpacing: 8, - children: children, - ), - ); - } -} - -class _ReviewerCard extends StatelessWidget { - const _ReviewerCard(); - - @override - Widget build(BuildContext context) { - return _StayInvolvedCard( - icon: VoicesAssets.icons.clipboardCheck, - title: context.l10n.becomeReviewer, - description: context.l10n.stayInvolvedReviewerDescription, - actions: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 16), - _RangeTimelineCard( - children: [ - BlocSelector( - selector: (state) { - return state.campaign.reviewRegistrationStartsAt; - }, - builder: (context, date) { - return _DatetimeRangeTimeline( - dateRange: date, - title: context.l10n.reviewRegistration, - ); - }, - ), - BlocSelector( - selector: (state) { - return state.campaign.reviewStartsAt; - }, - builder: (context, date) { - return _DatetimeRangeTimeline( - dateRange: date, - title: context.l10n.reviewTimelineHeader, - ); - }, - ), - ], - ), - const _CopyCatalystIdTipText(), - const SessionAccountCatalystId( - padding: EdgeInsets.only(top: 20), - ), - _StayInvolvedActionButton( - title: context.l10n.becomeReviewer, - onTap: () { - final shareManager = ShareManager.of(context); - final uri = shareManager.becomeReviewer(); - unawaited(launchUrl(uri)); - }, - trailing: VoicesAssets.icons.externalLink.buildIcon(), - ), - ], - ), - ); - } -} - -class _StayInvolvedActionButton extends StatelessWidget with LaunchUrlMixin { - final String title; - final Widget? trailing; - final VoidCallback? onTap; - - const _StayInvolvedActionButton({ - required this.title, - this.trailing, - this.onTap, - }); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(top: 20), - child: VoicesFilledButton( - onTap: onTap, - trailing: trailing, - child: Text(title), - ), - ); - } -} - -class _StayInvolvedCard extends StatelessWidget { - final SvgGenImage icon; - final String title; - final String description; - final Widget actions; - - const _StayInvolvedCard({ - required this.icon, - required this.title, - required this.description, - required this.actions, - }); - - @override - Widget build(BuildContext context) { - return Container( - constraints: const BoxConstraints( - minHeight: 565, - maxWidth: 588, - ), - padding: const EdgeInsets.symmetric(horizontal: 32), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: _getGradientColors(context), - ), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox(height: 20), - icon.buildIcon(size: 53), - const SizedBox(height: 22), - Text( - title, - style: context.textTheme.headlineLarge?.copyWith( - color: context.colorScheme.primary, - ), - ), - const SizedBox(height: 12), - Text( - description, - style: context.textTheme.bodyMedium?.copyWith( - color: context.colors.textOnPrimaryLevel1, - ), - ), - actions, - const SizedBox(height: 20), - ], - ), - ); - } - - List _getGradientColors(BuildContext context) { - return Theme.of(context).brightness == Brightness.light - ? [ - const Color(0xFFD1EAFF), - const Color(0xFFCAD6FE), - ] - : [ - const Color(0xFF2D3953), - const Color(0xFF242C42), - ]; - } -} - -class _VoterCard extends StatelessWidget { - const _VoterCard(); - - @override - Widget build(BuildContext context) { - return _StayInvolvedCard( - icon: VoicesAssets.icons.vote, - title: context.l10n.registerToVoteFund(context.activeCampaignFundNumber), - description: context.l10n.stayInvolvedContributorDescription, - actions: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox(height: 16), - _RangeTimelineCard( - children: [ - BlocSelector( - selector: (state) { - return state.campaign.votingRegistrationStartsAt; - }, - builder: (context, date) { - return _DatetimeRangeTimeline( - dateRange: date, - title: context.l10n.votingRegistrationTimelineHeader, - ); - }, - ), - const SizedBox(height: 16), - BlocSelector( - selector: (state) { - return state.campaign.votingStartsAt; - }, - builder: (context, date) { - return _DatetimeRangeTimeline( - dateRange: date, - title: context.l10n.votingTimelineHeader, - ); - }, - ), - ], - ), - _StayInvolvedActionButton( - title: context.l10n.becomeVoter, - onTap: () { - final uri = Uri.parse(VoicesConstants.afterSubmissionUrl); - unawaited(launchUrl(uri)); - }, - ), - ], - ), - ); - } -} diff --git a/catalyst_voices/apps/voices/lib/pages/discovery/sections/stay_involved/stay_involved.dart b/catalyst_voices/apps/voices/lib/pages/discovery/sections/stay_involved/stay_involved.dart new file mode 100644 index 000000000000..891eb71ce2b5 --- /dev/null +++ b/catalyst_voices/apps/voices/lib/pages/discovery/sections/stay_involved/stay_involved.dart @@ -0,0 +1,48 @@ +import 'package:catalyst_voices/common/ext/build_context_ext.dart'; +import 'package:catalyst_voices/pages/discovery/sections/stay_involved/widgets/reviewer_card.dart'; +import 'package:catalyst_voices/pages/discovery/sections/stay_involved/widgets/voter_card.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:flutter/material.dart'; + +class StayInvolved extends StatelessWidget { + const StayInvolved({super.key}); + + @override + Widget build(BuildContext context) { + return ResponsivePadding( + xs: const EdgeInsets.symmetric(horizontal: 20, vertical: 20), + sm: const EdgeInsets.symmetric(horizontal: 48, vertical: 48), + md: const EdgeInsets.symmetric(horizontal: 120, vertical: 72), + child: const Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _Header(), + SizedBox(height: 24), + Wrap( + alignment: WrapAlignment.center, + spacing: 16, + runSpacing: 16, + children: [ + VoterCard(), + ReviewerCard(), + ], + ), + ], + ), + ); + } +} + +class _Header extends StatelessWidget { + const _Header(); + + @override + Widget build(BuildContext context) { + return Text( + context.l10n.stayInvolved, + style: context.textTheme.headlineMedium, + ); + } +} diff --git a/catalyst_voices/apps/voices/lib/pages/discovery/sections/stay_involved/widgets/copy_catalyst_id_tip.dart b/catalyst_voices/apps/voices/lib/pages/discovery/sections/stay_involved/widgets/copy_catalyst_id_tip.dart new file mode 100644 index 000000000000..fe172e401a89 --- /dev/null +++ b/catalyst_voices/apps/voices/lib/pages/discovery/sections/stay_involved/widgets/copy_catalyst_id_tip.dart @@ -0,0 +1,34 @@ +import 'package:catalyst_voices/common/ext/build_context_ext.dart'; +import 'package:catalyst_voices/share/share_manager.dart'; +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:flutter/material.dart'; + +class CopyCatalystIdTip extends StatelessWidget { + const CopyCatalystIdTip({super.key}); + + @override + Widget build(BuildContext context) { + return BlocSelector( + selector: (state) => state.isActive, + builder: (context, isActive) { + return Offstage( + offstage: !isActive, + child: Padding( + padding: const EdgeInsets.only(top: 20), + child: TipText( + context.l10n.tipCopyCatalystIdForReviewTool( + ShareManager.of(context).becomeReviewer().decoded(), + ), + style: context.textTheme.bodyMedium?.copyWith( + color: context.colors.textOnPrimaryLevel1, + ), + ), + ), + ); + }, + ); + } +} diff --git a/catalyst_voices/apps/voices/lib/pages/discovery/sections/stay_involved/widgets/reviewer_card.dart b/catalyst_voices/apps/voices/lib/pages/discovery/sections/stay_involved/widgets/reviewer_card.dart new file mode 100644 index 000000000000..97a84162152d --- /dev/null +++ b/catalyst_voices/apps/voices/lib/pages/discovery/sections/stay_involved/widgets/reviewer_card.dart @@ -0,0 +1,66 @@ +import 'dart:async'; + +import 'package:catalyst_voices/pages/discovery/sections/session_account_catalyst_id.dart'; +import 'package:catalyst_voices/pages/discovery/sections/stay_involved/widgets/copy_catalyst_id_tip.dart'; +import 'package:catalyst_voices/pages/discovery/sections/stay_involved/widgets/stay_involved_action_button.dart'; +import 'package:catalyst_voices/pages/discovery/sections/stay_involved/widgets/stay_involved_card.dart'; +import 'package:catalyst_voices/pages/discovery/sections/stay_involved/widgets/timeline_card.dart'; +import 'package:catalyst_voices/share/share_manager.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class ReviewerCard extends StatelessWidget { + const ReviewerCard({super.key}); + + @override + Widget build(BuildContext context) { + return StayInvolvedCard( + icon: VoicesAssets.icons.clipboardCheck, + title: context.l10n.becomeReviewer, + description: context.l10n.stayInvolvedReviewerDescription, + actions: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 16), + const _ReviewTimeline(), + const CopyCatalystIdTip(), + const SessionAccountCatalystId( + padding: EdgeInsets.only(top: 20), + ), + StayInvolvedActionButton( + title: context.l10n.becomeReviewer, + onTap: () { + final shareManager = ShareManager.of(context); + final uri = shareManager.becomeReviewer(); + unawaited(launchUrl(uri)); + }, + trailing: VoicesAssets.icons.externalLink.buildIcon(), + ), + ], + ), + ); + } +} + +class _ReviewTimeline extends StatelessWidget { + const _ReviewTimeline(); + + @override + Widget build(BuildContext context) { + return BlocSelector( + selector: (state) => state.campaign.datesEvents, + builder: (context, campaignDates) { + final timelineItems = campaignDates.reviewTimelineItems; + if (timelineItems.isEmpty) { + return const SizedBox.shrink(); + } + + return TimelineCard(timelineItems: timelineItems); + }, + ); + } +} diff --git a/catalyst_voices/apps/voices/lib/pages/discovery/sections/stay_involved/widgets/stay_involved_action_button.dart b/catalyst_voices/apps/voices/lib/pages/discovery/sections/stay_involved/widgets/stay_involved_action_button.dart new file mode 100644 index 000000000000..ca19f488799c --- /dev/null +++ b/catalyst_voices/apps/voices/lib/pages/discovery/sections/stay_involved/widgets/stay_involved_action_button.dart @@ -0,0 +1,27 @@ +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:flutter/material.dart'; + +class StayInvolvedActionButton extends StatelessWidget { + final String title; + final Widget? trailing; + final VoidCallback? onTap; + + const StayInvolvedActionButton({ + super.key, + required this.title, + this.trailing, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(top: 20), + child: VoicesFilledButton( + onTap: onTap, + trailing: trailing, + child: Text(title), + ), + ); + } +} diff --git a/catalyst_voices/apps/voices/lib/pages/discovery/sections/stay_involved/widgets/stay_involved_card.dart b/catalyst_voices/apps/voices/lib/pages/discovery/sections/stay_involved/widgets/stay_involved_card.dart new file mode 100644 index 000000000000..8b63e7f357c8 --- /dev/null +++ b/catalyst_voices/apps/voices/lib/pages/discovery/sections/stay_involved/widgets/stay_involved_card.dart @@ -0,0 +1,73 @@ +import 'package:catalyst_voices/common/ext/build_context_ext.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:flutter/material.dart'; + +class StayInvolvedCard extends StatelessWidget { + final SvgGenImage icon; + final String title; + final String description; + final Widget actions; + + const StayInvolvedCard({ + super.key, + required this.icon, + required this.title, + required this.description, + required this.actions, + }); + + @override + Widget build(BuildContext context) { + return Container( + constraints: const BoxConstraints( + minHeight: 565, + maxWidth: 588, + ), + padding: const EdgeInsets.symmetric(horizontal: 32), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: _getGradientColors(context), + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 20), + icon.buildIcon(size: 53), + const SizedBox(height: 22), + Text( + title, + style: context.textTheme.headlineLarge?.copyWith( + color: context.colorScheme.primary, + ), + ), + const SizedBox(height: 12), + Text( + description, + style: context.textTheme.bodyMedium?.copyWith( + color: context.colors.textOnPrimaryLevel1, + ), + ), + actions, + const SizedBox(height: 20), + ], + ), + ); + } + + List _getGradientColors(BuildContext context) { + return Theme.of(context).brightness == Brightness.light + ? [ + const Color(0xFFD1EAFF), + const Color(0xFFCAD6FE), + ] + : [ + const Color(0xFF2D3953), + const Color(0xFF242C42), + ]; + } +} diff --git a/catalyst_voices/apps/voices/lib/pages/discovery/sections/stay_involved/widgets/timeline_card.dart b/catalyst_voices/apps/voices/lib/pages/discovery/sections/stay_involved/widgets/timeline_card.dart new file mode 100644 index 000000000000..a3ad74be7898 --- /dev/null +++ b/catalyst_voices/apps/voices/lib/pages/discovery/sections/stay_involved/widgets/timeline_card.dart @@ -0,0 +1,68 @@ +import 'package:catalyst_voices/common/ext/build_context_ext.dart'; +import 'package:catalyst_voices/widgets/text/campaign_stage_time_text.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; +import 'package:flutter/material.dart'; + +class TimelineCard extends StatelessWidget { + final List timelineItems; + + const TimelineCard({ + super.key, + required this.timelineItems, + }); + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Theme.of(context).colors.elevationsOnSurfaceNeutralLv0, + borderRadius: BorderRadius.circular(12), + ), + child: Wrap( + alignment: WrapAlignment.spaceBetween, + spacing: 8, + runSpacing: 8, + children: timelineItems + .map( + (item) => _TimelineItem( + dateRange: item.dateRange, + title: item.localizedEventTitle(context.l10n), + ), + ) + .toList(), + ), + ); + } +} + +class _TimelineItem extends StatelessWidget { + final DateRange dateRange; + final String title; + + const _TimelineItem({ + required this.dateRange, + required this.title, + }); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + title, + style: context.textTheme.titleSmall, + ), + CampaignStageTimeText( + dateRange: dateRange, + ), + ], + ); + } +} diff --git a/catalyst_voices/apps/voices/lib/pages/discovery/sections/stay_involved/widgets/voter_card.dart b/catalyst_voices/apps/voices/lib/pages/discovery/sections/stay_involved/widgets/voter_card.dart new file mode 100644 index 000000000000..43b1d5c8a92f --- /dev/null +++ b/catalyst_voices/apps/voices/lib/pages/discovery/sections/stay_involved/widgets/voter_card.dart @@ -0,0 +1,59 @@ +import 'dart:async'; + +import 'package:catalyst_voices/common/constants/constants.dart'; +import 'package:catalyst_voices/common/ext/active_fund_number_selector_ext.dart'; +import 'package:catalyst_voices/pages/discovery/sections/stay_involved/widgets/stay_involved_action_button.dart'; +import 'package:catalyst_voices/pages/discovery/sections/stay_involved/widgets/stay_involved_card.dart'; +import 'package:catalyst_voices/pages/discovery/sections/stay_involved/widgets/timeline_card.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class VoterCard extends StatelessWidget { + const VoterCard({super.key}); + + @override + Widget build(BuildContext context) { + return StayInvolvedCard( + icon: VoicesAssets.icons.vote, + title: context.l10n.registerToVoteFund(context.activeCampaignFundNumber), + description: context.l10n.stayInvolvedContributorDescription, + actions: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 16), + const _VotingTimeline(), + StayInvolvedActionButton( + title: context.l10n.becomeVoter, + onTap: () { + final uri = Uri.parse(VoicesConstants.afterSubmissionUrl); + unawaited(launchUrl(uri)); + }, + ), + ], + ), + ); + } +} + +class _VotingTimeline extends StatelessWidget { + const _VotingTimeline(); + + @override + Widget build(BuildContext context) { + return BlocSelector( + selector: (state) => state.campaign.datesEvents, + builder: (context, campaignDates) { + final timelineItems = campaignDates.votingTimelineItems; + if (timelineItems.isEmpty) { + return const SizedBox.shrink(); + } + + return TimelineCard(timelineItems: timelineItems); + }, + ); + } +} diff --git a/catalyst_voices/apps/voices/lib/pages/discovery/state_selectors/campaign_categories_state_selector.dart b/catalyst_voices/apps/voices/lib/pages/discovery/state_selectors/campaign_categories_state_selector.dart deleted file mode 100644 index f050fce7a1fe..000000000000 --- a/catalyst_voices/apps/voices/lib/pages/discovery/state_selectors/campaign_categories_state_selector.dart +++ /dev/null @@ -1,117 +0,0 @@ -import 'package:catalyst_voices/common/ext/build_context_ext.dart'; -import 'package:catalyst_voices/common/typedefs.dart'; -import 'package:catalyst_voices/pages/discovery/sections/campaign_categories.dart'; -import 'package:catalyst_voices/widgets/widgets.dart'; -import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; -import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; -import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; -import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; -import 'package:flutter/material.dart'; - -typedef _ListItems = List; - -class CampaignCategoriesStateSelector extends StatelessWidget { - const CampaignCategoriesStateSelector({super.key}); - - @override - Widget build(BuildContext context) { - return Column( - key: const Key('CampaignCategoriesStateSelector'), - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ResponsivePadding( - xs: const EdgeInsets.symmetric(horizontal: 20), - sm: const EdgeInsets.symmetric(horizontal: 48), - md: const EdgeInsets.symmetric(horizontal: 120), - child: Text( - key: const Key('CampaignCategoriesTitle'), - context.l10n.campaignCategories, - style: context.textTheme.titleLarge, - ), - ), - const SizedBox(height: 24), - const Stack( - children: [ - _CampaignCategoriesLoading(), - _CampaignCategoriesData(), - _CampaignCategoriesError(), - ], - ), - ], - ); - } -} - -class _CampaignCategoriesData extends StatelessWidget { - const _CampaignCategoriesData(); - - @override - Widget build(BuildContext context) { - return BlocSelector( - selector: (state) { - return state.categories.categories; - }, - builder: (context, state) { - return Offstage( - offstage: state.isEmpty, - child: CampaignCategories( - state, - ), - ); - }, - ); - } -} - -class _CampaignCategoriesError extends StatelessWidget { - const _CampaignCategoriesError(); - - @override - Widget build(BuildContext context) { - return BlocSelector( - selector: (state) => (show: state.categories.showError, data: state.categories.error), - builder: (context, state) { - final errorMessage = state.data?.message(context); - return Offstage( - key: const Key('CampaignCategoriesError'), - offstage: !state.show, - child: Padding( - padding: const EdgeInsets.all(16), - child: Center( - child: VoicesErrorIndicator( - message: errorMessage ?? context.l10n.somethingWentWrong, - onRetry: () async { - await context.read().getCurrentCampaign(); - }, - ), - ), - ), - ); - }, - ); - } -} - -class _CampaignCategoriesLoading extends StatelessWidget { - const _CampaignCategoriesLoading(); - - @override - Widget build(BuildContext context) { - return BlocSelector( - selector: (state) { - return state.categories.isLoading; - }, - builder: (context, isLoading) { - final dummyCategories = List.filled( - 6, - CampaignCategoryDetailsViewModel.dummy(), - ); - return Offstage( - offstage: !isLoading, - child: CampaignCategories(dummyCategories, isLoading: isLoading), - ); - }, - ); - } -} diff --git a/catalyst_voices/apps/voices/lib/pages/discovery/state_selectors/current_campaign_selector.dart b/catalyst_voices/apps/voices/lib/pages/discovery/state_selectors/current_campaign_selector.dart deleted file mode 100644 index 99d250106edf..000000000000 --- a/catalyst_voices/apps/voices/lib/pages/discovery/state_selectors/current_campaign_selector.dart +++ /dev/null @@ -1,152 +0,0 @@ -import 'package:catalyst_voices/common/ext/active_fund_number_selector_ext.dart'; -import 'package:catalyst_voices/common/ext/build_context_ext.dart'; -import 'package:catalyst_voices/common/typedefs.dart'; -import 'package:catalyst_voices/pages/discovery/sections/current_campaign.dart'; -import 'package:catalyst_voices/widgets/widgets.dart'; -import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; -import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; -import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; -import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; -import 'package:flutter/material.dart'; - -class CurrentCampaignData extends StatelessWidget { - const CurrentCampaignData({super.key}); - - @override - Widget build(BuildContext context) { - return BlocSelector( - key: const Key('CurrentCampaignData'), - selector: (state) { - return state.campaign.currentCampaign; - }, - builder: (context, state) { - return Offstage( - offstage: state is NullCurrentCampaignInfoViewModel, - child: CurrentCampaign( - currentCampaignInfo: state, - ), - ); - }, - ); - } -} - -class CurrentCampaignError extends StatelessWidget { - const CurrentCampaignError({super.key}); - - @override - Widget build(BuildContext context) { - return BlocSelector( - selector: (state) { - return ( - show: state.campaign.showError, - data: state.campaign.error, - ); - }, - builder: (context, state) { - final errorMessage = state.data?.message(context); - return Offstage( - key: const Key('CurrentCampaignError'), - offstage: !state.show, - child: Padding( - padding: const EdgeInsets.all(32), - child: Center( - child: VoicesErrorIndicator( - message: errorMessage ?? context.l10n.somethingWentWrong, - onRetry: () async { - await context.read().getCurrentCampaign(); - }, - ), - ), - ), - ); - }, - ); - } -} - -class CurrentCampaignLoading extends StatelessWidget { - const CurrentCampaignLoading({super.key}); - - @override - Widget build(BuildContext context) { - return BlocSelector( - selector: (state) { - return state.campaign.isLoading; - }, - builder: (context, state) { - return Offstage( - offstage: !state, - child: CurrentCampaign( - currentCampaignInfo: NullCurrentCampaignInfoViewModel(), - isLoading: state, - ), - ); - }, - ); - } -} - -class CurrentCampaignSelector extends StatelessWidget { - const CurrentCampaignSelector({super.key}); - - @override - Widget build(BuildContext context) { - return Column( - key: const Key('CurrentCampaignRoot'), - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ResponsivePadding( - xs: const EdgeInsets.only(left: 20, top: 64, right: 20), - sm: const EdgeInsets.only(left: 42, top: 64, right: 42), - md: const EdgeInsets.only(left: 120, top: 64, right: 120), - child: const _Header(), - ), - const Stack( - children: [ - CurrentCampaignLoading(), - CurrentCampaignData(), - CurrentCampaignError(), - ], - ), - ], - ); - } -} - -class _Header extends StatelessWidget { - const _Header(); - - @override - Widget build(BuildContext context) { - return ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 568), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Text( - key: const Key('CurrentCampaignTitle'), - context.l10n.currentCampaign, - style: context.textTheme.titleSmall, - ), - const SizedBox(height: 4), - Text( - key: const Key('Subtitle'), - context.l10n.catalystFundNo(context.activeCampaignFundNumber), - style: context.textTheme.displayMedium?.copyWith( - color: context.colorScheme.primary, - ), - ), - const SizedBox(height: 16), - Text( - key: const Key('Description'), - context.l10n.currentCampaignDescription, - style: context.textTheme.bodyLarge, - ), - ], - ), - ); - } -} diff --git a/catalyst_voices/apps/voices/lib/pages/discovery/state_selectors/most_recent_proposals_selector.dart b/catalyst_voices/apps/voices/lib/pages/discovery/state_selectors/most_recent_proposals_selector.dart deleted file mode 100644 index 4a6cbc44174e..000000000000 --- a/catalyst_voices/apps/voices/lib/pages/discovery/state_selectors/most_recent_proposals_selector.dart +++ /dev/null @@ -1,93 +0,0 @@ -import 'package:catalyst_voices/common/typedefs.dart'; -import 'package:catalyst_voices/pages/discovery/sections/most_recent_proposals.dart'; -import 'package:catalyst_voices/widgets/indicators/voices_error_indicator.dart'; -import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; -import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; -import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; -import 'package:flutter/material.dart'; - -typedef _ListItems = List; - -class MostRecentProposalsSelector extends StatelessWidget { - const MostRecentProposalsSelector({super.key}); - - @override - Widget build(BuildContext context) { - return const Stack( - children: [ - _MostRecentProposalsLoading(), - _MostRecentProposalsError(), - _MostRecentProposalsData(), - ], - ); - } -} - -class _MostRecentProposalsData extends StatelessWidget { - static const _minProposalsToShowRecent = 6; - - const _MostRecentProposalsData(); - - @override - Widget build(BuildContext context) { - return BlocSelector( - selector: (state) => state.proposals.proposals, - builder: (context, state) { - if (state.length < _minProposalsToShowRecent) { - return const ViewAllProposals(); - } - return MostRecentProposals(proposals: state); - }, - ); - } -} - -class _MostRecentProposalsError extends StatelessWidget { - const _MostRecentProposalsError(); - - @override - Widget build(BuildContext context) { - return BlocSelector( - selector: (state) { - return ( - show: state.proposals.showError, - data: state.proposals.error, - ); - }, - builder: (context, state) { - final errorMessage = state.data?.message(context); - return Offstage( - key: const Key('MostRecentError'), - offstage: !state.show, - child: Padding( - padding: const EdgeInsets.all(16), - child: Center( - child: VoicesErrorIndicator( - message: errorMessage ?? context.l10n.somethingWentWrong, - onRetry: () async { - await context.read().getMostRecentProposals(); - }, - ), - ), - ), - ); - }, - ); - } -} - -class _MostRecentProposalsLoading extends StatelessWidget { - const _MostRecentProposalsLoading(); - - @override - Widget build(BuildContext context) { - return BlocSelector( - selector: (state) { - return state.proposals.isLoading; - }, - builder: (context, state) { - return const Offstage(); - }, - ); - } -} diff --git a/catalyst_voices/apps/voices/lib/pages/discovery/toggle_state_text.dart b/catalyst_voices/apps/voices/lib/pages/discovery/toggle_state_text.dart deleted file mode 100644 index 82241ad920f9..000000000000 --- a/catalyst_voices/apps/voices/lib/pages/discovery/toggle_state_text.dart +++ /dev/null @@ -1,95 +0,0 @@ -import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; -import 'package:catalyst_voices_models/catalyst_voices_models.dart'; -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; - -// Note. This widget will be removed so its not localized -class ToggleStateText extends StatefulWidget { - const ToggleStateText({ - super.key, - }); - - @override - State createState() => _ToggleStateTextState(); -} - -class _ToggleStateTextState extends State { - final _tapVisitor = TapGestureRecognizer(); - final _tapGuest = TapGestureRecognizer(); - final _tapActiveUser = TapGestureRecognizer(); - - @override - Widget build(BuildContext context) { - return Row( - children: [ - GestureDetector( - key: const Key('VisitorShortcut'), - onTap: _tapVisitor.onTap, - child: const Text( - 'No key (visitor)', - style: TextStyle( - decoration: TextDecoration.underline, - color: Colors.blue, - ), - ), - ), - const Text(', '), - GestureDetector( - key: const Key('GuestShortcut'), - onTap: _tapGuest.onTap, - child: const Text( - 'Key found(Guest/locked)', - style: TextStyle( - decoration: TextDecoration.underline, - color: Colors.blue, - ), - ), - ), - const Text(', '), - GestureDetector( - key: const Key('UserShortcut'), - onTap: _tapActiveUser.onTap, - child: const Text( - 'Key found (Active user/unlocked)', - style: TextStyle( - decoration: TextDecoration.underline, - color: Colors.blue, - ), - ), - ), - ], - ); - } - - @override - void dispose() { - _tapVisitor.dispose(); - _tapGuest.dispose(); - _tapActiveUser.dispose(); - super.dispose(); - } - - @override - void initState() { - super.initState(); - _tapVisitor.onTap = () async { - await context.read().removeKeychain(); - }; - _tapGuest.onTap = () async { - final sessionBloc = context.read(); - - if (sessionBloc.state.isActive) { - await sessionBloc.lock(); - } else if (sessionBloc.state.isVisitor) { - await sessionBloc.switchToDummyAccount().then((_) => sessionBloc.lock()); - } - }; - _tapActiveUser.onTap = () async { - final sessionBloc = context.read(); - - await sessionBloc.switchToDummyAccount().then( - (_) => sessionBloc.unlock(Account.dummyUnlockFactor), - ); - }; - } -} diff --git a/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/discovery_overview.dart b/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/discovery_overview.dart index cda00852aa40..b16478db7b61 100644 --- a/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/discovery_overview.dart +++ b/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/discovery_overview.dart @@ -1,6 +1,6 @@ import 'package:catalyst_voices/pages/overall_spaces/space/space_overview_header.dart'; import 'package:catalyst_voices/pages/overall_spaces/space/space_overview_nav_tile.dart'; -import 'package:catalyst_voices/pages/overall_spaces/space/user_proposal_selectors/user_proposal_selectors.dart'; +import 'package:catalyst_voices/pages/overall_spaces/space/user_proposal_overview/discovery_overview_proposal.dart'; import 'package:catalyst_voices/pages/overall_spaces/space_overview_container.dart'; import 'package:catalyst_voices/routes/routes.dart'; import 'package:catalyst_voices/widgets/widgets.dart'; @@ -26,7 +26,7 @@ class DiscoveryOverview extends StatelessWidget { VoicesDivider(indent: 0, endIndent: 0, height: 16), Expanded( child: SingleChildScrollView( - child: _PublishedProposalSelector(), + child: _PublishedProposal(), ), ), ], @@ -67,8 +67,8 @@ class _FeedbackTile extends StatelessWidget { } } -class _PublishedProposalSelector extends StatelessWidget { - const _PublishedProposalSelector(); +class _PublishedProposal extends StatelessWidget { + const _PublishedProposal(); @override Widget build(BuildContext context) { @@ -79,7 +79,7 @@ class _PublishedProposalSelector extends StatelessWidget { builder: (context, state) { return Offstage( offstage: state, - child: const DiscoveryOverviewProposalSelector(), + child: const DiscoveryOverviewProposal(), ); }, ); diff --git a/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_overview/discovery_overview_proposal.dart b/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_overview/discovery_overview_proposal.dart new file mode 100644 index 000000000000..e340be1054e3 --- /dev/null +++ b/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_overview/discovery_overview_proposal.dart @@ -0,0 +1,63 @@ +import 'package:catalyst_voices/common/typedefs.dart'; +import 'package:catalyst_voices/pages/overall_spaces/space/user_proposal_overview/widgets/error_user_proposal_overview.dart'; +import 'package:catalyst_voices/pages/overall_spaces/space/user_proposal_overview/widgets/loading_user_proposal_overview.dart'; +import 'package:catalyst_voices/pages/overall_spaces/space/user_proposal_overview/widgets/user_proposals_overview_header.dart'; +import 'package:catalyst_voices/pages/overall_spaces/space/user_proposal_overview/widgets/user_proposals_overview_list.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; +import 'package:flutter/material.dart'; + +class DiscoveryOverviewProposal extends StatelessWidget { + const DiscoveryOverviewProposal({super.key}); + + @override + Widget build(BuildContext context) { + return const Stack( + children: [ + LoadingProposalOverview(), + ErrorProposalOverview(), + _DiscoveryOverviewProposalData(), + ], + ); + } +} + +class _DiscoveryOverviewProposalData extends StatelessWidget { + const _DiscoveryOverviewProposalData(); + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: + BlocSelector< + WorkspaceBloc, + WorkspaceState, + DataVisibilityState> + >( + selector: (state) { + return (data: state.userProposals.published.items, show: state.showProposals); + }, + builder: (context, state) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + UserProposalsOverviewHeader( + title: context.l10n.publishedProposals, + ), + Offstage( + offstage: !state.show, + child: UserProposalsOverviewList( + proposals: state.data, + emptyMessage: context.l10n.noPublishedProposals, + ), + ), + const SizedBox(height: 12), + ], + ); + }, + ), + ); + } +} diff --git a/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_overview/widgets/error_user_proposal_overview.dart b/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_overview/widgets/error_user_proposal_overview.dart new file mode 100644 index 000000000000..ff730c0c3452 --- /dev/null +++ b/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_overview/widgets/error_user_proposal_overview.dart @@ -0,0 +1,40 @@ +import 'package:catalyst_voices/widgets/indicators/voices_error_indicator.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; +import 'package:flutter/material.dart'; + +class ErrorProposalOverview extends StatelessWidget { + const ErrorProposalOverview({super.key}); + + @override + Widget build(BuildContext context) { + return BlocSelector( + selector: (state) => state.error, + builder: (context, error) { + return Offstage( + offstage: error == null, + child: _Error(error: error), + ); + }, + ); + } +} + +class _Error extends StatelessWidget { + final LocalizedException? error; + + const _Error({ + this.error, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(top: 60), + child: VoicesErrorIndicator( + message: error?.message(context) ?? const LocalizedUnknownException().message(context), + onRetry: () => context.read().add(const WatchUserProposalsEvent()), + ), + ); + } +} diff --git a/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_overview/widgets/loading_user_proposal_overview.dart b/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_overview/widgets/loading_user_proposal_overview.dart new file mode 100644 index 000000000000..49a2fa800e87 --- /dev/null +++ b/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_overview/widgets/loading_user_proposal_overview.dart @@ -0,0 +1,43 @@ +import 'package:catalyst_voices/widgets/indicators/voices_circular_progress_indicator.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:flutter/material.dart'; + +class LoadingProposalOverview extends StatelessWidget { + const LoadingProposalOverview({super.key}); + + @override + Widget build(BuildContext context) { + return BlocSelector( + selector: (state) { + return state.isLoading; + }, + builder: (context, isLoading) => Offstage( + offstage: !isLoading, + child: _Loading( + isLoading: isLoading, + ), + ), + ); + } +} + +class _Loading extends StatelessWidget { + final bool isLoading; + + const _Loading({ + required this.isLoading, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(top: 60), + child: Center( + child: TickerMode( + enabled: isLoading, + child: const VoicesCircularProgressIndicator(), + ), + ), + ); + } +} diff --git a/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_overview/widgets/user_proposals_overview_header.dart b/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_overview/widgets/user_proposals_overview_header.dart new file mode 100644 index 000000000000..fdc214ab590b --- /dev/null +++ b/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_overview/widgets/user_proposals_overview_header.dart @@ -0,0 +1,28 @@ +import 'package:catalyst_voices/common/ext/build_context_ext.dart'; +import 'package:flutter/material.dart'; + +class UserProposalsOverviewHeader extends StatelessWidget { + final String title; + + const UserProposalsOverviewHeader({ + super.key, + required this.title, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only( + left: 20, + top: 18, + bottom: 18, + ), + child: Text( + title, + style: context.textTheme.titleMedium?.copyWith( + color: context.colors.textOnPrimaryLevel1, + ), + ), + ); + } +} diff --git a/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_overview/widgets/user_proposals_overview_list.dart b/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_overview/widgets/user_proposals_overview_list.dart new file mode 100644 index 000000000000..0ff0cdea18d6 --- /dev/null +++ b/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_overview/widgets/user_proposals_overview_list.dart @@ -0,0 +1,43 @@ +import 'package:catalyst_voices/common/ext/build_context_ext.dart'; +import 'package:catalyst_voices/widgets/cards/proposal/small_proposal_card.dart'; +import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; +import 'package:flutter/material.dart'; + +class UserProposalsOverviewList extends StatelessWidget { + final List proposals; + final String emptyMessage; + final bool showLatestLocal; + + const UserProposalsOverviewList({ + super.key, + required this.proposals, + required this.emptyMessage, + this.showLatestLocal = false, + }); + + @override + Widget build(BuildContext context) { + if (proposals.isEmpty) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Text( + emptyMessage, + style: context.textTheme.bodyMedium?.copyWith( + color: context.colors.textOnPrimaryLevel1, + ), + ), + ); + } + return Column( + spacing: 12, + children: proposals + .map( + (e) => SmallProposalCard( + proposal: e, + showLatestLocal: showLatestLocal, + ), + ) + .toList(), + ); + } +} diff --git a/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_overview/workspace_overview_proposal.dart b/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_overview/workspace_overview_proposal.dart new file mode 100644 index 000000000000..d1b7a6210e9e --- /dev/null +++ b/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_overview/workspace_overview_proposal.dart @@ -0,0 +1,57 @@ +import 'package:catalyst_voices/common/typedefs.dart'; +import 'package:catalyst_voices/pages/overall_spaces/space/user_proposal_overview/widgets/error_user_proposal_overview.dart'; +import 'package:catalyst_voices/pages/overall_spaces/space/user_proposal_overview/widgets/loading_user_proposal_overview.dart'; +import 'package:catalyst_voices/pages/overall_spaces/space/user_proposal_overview/widgets/user_proposals_overview_header.dart'; +import 'package:catalyst_voices/pages/overall_spaces/space/user_proposal_overview/widgets/user_proposals_overview_list.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; +import 'package:flutter/material.dart'; + +class WorkspaceOverviewProposal extends StatelessWidget { + const WorkspaceOverviewProposal({super.key}); + + @override + Widget build(BuildContext context) { + return const Stack( + children: [ + LoadingProposalOverview(), + ErrorProposalOverview(), + _WorkspaceDataProposalOverview(), + ], + ); + } +} + +class _WorkspaceDataProposalOverview extends StatelessWidget { + const _WorkspaceDataProposalOverview(); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + UserProposalsOverviewHeader(title: context.l10n.notPublishedProposals), + BlocSelector>( + selector: (state) { + return ( + data: state.userProposals.notPublished, + show: state.showProposals && !state.isLoading, + ); + }, + builder: (context, state) { + return Offstage( + offstage: !state.show, + child: UserProposalsOverviewList( + proposals: state.data.items, + emptyMessage: context.l10n.noProposalsToPublish, + showLatestLocal: true, + ), + ); + }, + ), + ], + ); + } +} diff --git a/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_selectors/discovery_overview_proposal_selector.dart b/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_selectors/discovery_overview_proposal_selector.dart deleted file mode 100644 index 7e990fca0adc..000000000000 --- a/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_selectors/discovery_overview_proposal_selector.dart +++ /dev/null @@ -1,55 +0,0 @@ -part of 'user_proposal_selectors.dart'; - -class DiscoveryOverviewProposalSelector extends StatelessWidget { - const DiscoveryOverviewProposalSelector({super.key}); - - @override - Widget build(BuildContext context) { - return const Stack( - children: [ - _LoadingProposalSelector(), - _ErrorProposalSelector(), - _DataProposalSelector(), - ], - ); - } -} - -class _DataProposalSelector extends StatelessWidget { - const _DataProposalSelector(); - - @override - Widget build(BuildContext context) { - return SingleChildScrollView( - child: - BlocSelector< - WorkspaceBloc, - WorkspaceState, - DataVisibilityState> - >( - selector: (state) { - return (data: state.published, show: state.showProposals); - }, - builder: (context, state) { - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _Header( - title: context.l10n.publishedProposals, - ), - Offstage( - offstage: !state.show, - child: _DataProposalWidget( - proposals: state.data, - emptyMessage: context.l10n.noPublishedProposals, - ), - ), - const SizedBox(height: 12), - ], - ); - }, - ), - ); - } -} diff --git a/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_selectors/user_proposal_selectors.dart b/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_selectors/user_proposal_selectors.dart deleted file mode 100644 index b78638c3674b..000000000000 --- a/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_selectors/user_proposal_selectors.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:catalyst_voices/common/ext/build_context_ext.dart'; -import 'package:catalyst_voices/common/typedefs.dart'; -import 'package:catalyst_voices/widgets/cards/proposal/small_proposal_card.dart'; -import 'package:catalyst_voices/widgets/indicators/voices_circular_progress_indicator.dart'; -import 'package:catalyst_voices/widgets/indicators/voices_error_indicator.dart'; -import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; -import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; -import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; -import 'package:flutter/material.dart'; - -// As widget across two overviews are almost similar and are not reusable -// anywhere else it useful to use as part -part 'discovery_overview_proposal_selector.dart'; -part 'user_proposals_selector_widgets.dart'; -part 'workspace_overview_proposal_selector.dart'; diff --git a/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_selectors/user_proposals_selector_widgets.dart b/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_selectors/user_proposals_selector_widgets.dart deleted file mode 100644 index dbae1ff28fc9..000000000000 --- a/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_selectors/user_proposals_selector_widgets.dart +++ /dev/null @@ -1,116 +0,0 @@ -part of 'user_proposal_selectors.dart'; - -class _DataProposalWidget extends StatelessWidget { - final List proposals; - final String emptyMessage; - final bool showLatestLocal; - - const _DataProposalWidget({ - required this.proposals, - required this.emptyMessage, - this.showLatestLocal = false, - }); - - @override - Widget build(BuildContext context) { - if (proposals.isEmpty) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: Text( - emptyMessage, - style: context.textTheme.bodyMedium?.copyWith( - color: context.colors.textOnPrimaryLevel1, - ), - ), - ); - } - return Column( - spacing: 12, - children: proposals - .map( - (e) => SmallProposalCard( - proposal: e, - showLatestLocal: showLatestLocal, - ), - ) - .toList(), - ); - } -} - -class _ErrorProposalSelector extends StatelessWidget { - const _ErrorProposalSelector(); - - @override - Widget build(BuildContext context) { - return BlocSelector( - selector: (state) { - return (data: state.error, show: state.showError); - }, - builder: (context, state) { - return Offstage( - offstage: !state.show, - child: Padding( - padding: const EdgeInsets.only(top: 60), - child: VoicesErrorIndicator( - message: - state.data?.message(context) ?? - const LocalizedUnknownException().message(context), - onRetry: () => context.read().add(const WatchUserProposalsEvent()), - ), - ), - ); - }, - ); - } -} - -class _Header extends StatelessWidget { - final String title; - - const _Header({ - required this.title, - }); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only( - left: 20, - top: 18, - bottom: 18, - ), - child: Text( - title, - style: context.textTheme.titleMedium?.copyWith( - color: context.colors.textOnPrimaryLevel1, - ), - ), - ); - } -} - -class _LoadingProposalSelector extends StatelessWidget { - const _LoadingProposalSelector(); - - @override - Widget build(BuildContext context) { - return BlocSelector( - selector: (state) { - return state.isLoading; - }, - builder: (context, isLoading) => Offstage( - offstage: !isLoading, - child: Padding( - padding: const EdgeInsets.only(top: 60), - child: Center( - child: TickerMode( - enabled: isLoading, - child: const VoicesCircularProgressIndicator(), - ), - ), - ), - ), - ); - } -} diff --git a/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_selectors/workspace_overview_proposal_selector.dart b/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_selectors/workspace_overview_proposal_selector.dart deleted file mode 100644 index b8c14eecf4d1..000000000000 --- a/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/user_proposal_selectors/workspace_overview_proposal_selector.dart +++ /dev/null @@ -1,50 +0,0 @@ -part of 'user_proposal_selectors.dart'; - -class WorkspaceOverviewProposalSelector extends StatelessWidget { - const WorkspaceOverviewProposalSelector({super.key}); - - @override - Widget build(BuildContext context) { - return const Stack( - children: [ - _LoadingProposalSelector(), - _ErrorProposalSelector(), - _WorkspaceDataProposalSelector(), - ], - ); - } -} - -class _WorkspaceDataProposalSelector extends StatelessWidget { - const _WorkspaceDataProposalSelector(); - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _Header(title: context.l10n.notPublishedProposals), - BlocSelector< - WorkspaceBloc, - WorkspaceState, - DataVisibilityState> - >( - selector: (state) { - return (data: state.notPublished, show: state.showProposals && !state.isLoading); - }, - builder: (context, state) { - return Offstage( - offstage: !state.show, - child: _DataProposalWidget( - proposals: state.data, - emptyMessage: context.l10n.noProposalsToPublish, - showLatestLocal: true, - ), - ); - }, - ), - ], - ); - } -} diff --git a/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/workspace_overview.dart b/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/workspace_overview.dart index 96877a397e37..9fea74d69725 100644 --- a/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/workspace_overview.dart +++ b/catalyst_voices/apps/voices/lib/pages/overall_spaces/space/workspace_overview.dart @@ -1,6 +1,6 @@ import 'package:catalyst_voices/pages/overall_spaces/space/space_overview_header.dart'; import 'package:catalyst_voices/pages/overall_spaces/space/space_overview_nav_tile.dart'; -import 'package:catalyst_voices/pages/overall_spaces/space/user_proposal_selectors/user_proposal_selectors.dart'; +import 'package:catalyst_voices/pages/overall_spaces/space/user_proposal_overview/workspace_overview_proposal.dart'; import 'package:catalyst_voices/pages/overall_spaces/space_overview_container.dart'; import 'package:catalyst_voices/routes/routing/spaces_route.dart'; import 'package:catalyst_voices/widgets/widgets.dart'; @@ -25,7 +25,7 @@ class WorkspaceOverview extends StatelessWidget { VoicesDivider(indent: 0, endIndent: 0, height: 16), Expanded( child: SingleChildScrollView( - child: _NotPublishedProposalSelector(), + child: _NotPublishedProposalOverview(), ), ), ], @@ -50,8 +50,8 @@ class _BrowseMyProposals extends StatelessWidget { } } -class _NotPublishedProposalSelector extends StatelessWidget { - const _NotPublishedProposalSelector(); +class _NotPublishedProposalOverview extends StatelessWidget { + const _NotPublishedProposalOverview(); @override Widget build(BuildContext context) { @@ -62,7 +62,7 @@ class _NotPublishedProposalSelector extends StatelessWidget { builder: (context, state) { return Offstage( offstage: state, - child: const WorkspaceOverviewProposalSelector(), + child: const WorkspaceOverviewProposal(), ); }, ); diff --git a/catalyst_voices/apps/voices/lib/pages/registration/pictures/base_profile_picture.dart b/catalyst_voices/apps/voices/lib/pages/registration/pictures/base_profile_picture.dart index c8d4a7029507..6a555c3d0834 100644 --- a/catalyst_voices/apps/voices/lib/pages/registration/pictures/base_profile_picture.dart +++ b/catalyst_voices/apps/voices/lib/pages/registration/pictures/base_profile_picture.dart @@ -15,7 +15,7 @@ class BaseProfilePicture extends StatelessWidget { return TaskPicture( child: TaskPictureIconBox( type: type, - child: VoicesAssets.images.createBaseProfile.buildIcon( + child: VoicesAssets.images.svg.createBaseProfile.buildIcon( allowSize: false, ), ), diff --git a/catalyst_voices/apps/voices/lib/pages/registration/pictures/keychain_picture.dart b/catalyst_voices/apps/voices/lib/pages/registration/pictures/keychain_picture.dart index 3bb53ce34bc8..3f7ba3d34db3 100644 --- a/catalyst_voices/apps/voices/lib/pages/registration/pictures/keychain_picture.dart +++ b/catalyst_voices/apps/voices/lib/pages/registration/pictures/keychain_picture.dart @@ -15,7 +15,7 @@ class KeychainPicture extends StatelessWidget { return TaskPicture( child: TaskPictureIconBox( type: type, - child: VoicesAssets.images.keychain.buildIcon(allowSize: false), + child: VoicesAssets.images.svg.keychain.buildIcon(allowSize: false), ), ); } diff --git a/catalyst_voices/apps/voices/lib/pages/registration/pictures/keychain_with_password_picture.dart b/catalyst_voices/apps/voices/lib/pages/registration/pictures/keychain_with_password_picture.dart index 1d7fc8a4b659..2f83b678f515 100644 --- a/catalyst_voices/apps/voices/lib/pages/registration/pictures/keychain_with_password_picture.dart +++ b/catalyst_voices/apps/voices/lib/pages/registration/pictures/keychain_with_password_picture.dart @@ -13,7 +13,7 @@ class KeychainWithPasswordPicture extends StatelessWidget { child: Stack( alignment: Alignment.center, children: [ - VoicesAssets.images.keychain.buildIcon(allowSize: false), + VoicesAssets.images.svg.keychain.buildIcon(allowSize: false), Align( alignment: const Alignment(0.52, -0.4), child: VoicesAssets.icons.lockClosed.buildIcon(), diff --git a/catalyst_voices/apps/voices/lib/pages/voting/voting_page.dart b/catalyst_voices/apps/voices/lib/pages/voting/voting_page.dart index 079317469a4b..a37c88d7753f 100644 --- a/catalyst_voices/apps/voices/lib/pages/voting/voting_page.dart +++ b/catalyst_voices/apps/voices/lib/pages/voting/voting_page.dart @@ -2,8 +2,6 @@ import 'dart:async'; import 'package:catalyst_voices/common/error_handler.dart'; import 'package:catalyst_voices/common/signal_handler.dart'; -import 'package:catalyst_voices/pages/account/keychain_deleted_dialog.dart'; -import 'package:catalyst_voices/pages/account/widgets/account_keychain_tile.dart'; import 'package:catalyst_voices/pages/campaign_phase_aware/campaign_phase_aware.dart'; import 'package:catalyst_voices/pages/voting/widgets/content/pre_voting_content.dart'; import 'package:catalyst_voices/pages/voting/widgets/content/voting_background.dart'; @@ -23,13 +21,11 @@ import 'package:rxdart/rxdart.dart'; class VotingPage extends StatefulWidget { final SignedDocumentRef? categoryId; final VotingPageTab? tab; - final bool keychainDeleted; const VotingPage({ super.key, this.categoryId, this.tab, - this.keychainDeleted = false, }); @override @@ -93,13 +89,6 @@ class _VotingPageState extends State if (widget.tab != oldWidget.tab) { _tabController.animateToTab(tab); } - - if (showKeychainDeletedDialog) { - showKeychainDeletedDialog = false; - WidgetsBinding.instance.addPostFrameCallback((_) async { - await _showKeychainDeletedDialog(context); - }); - } } @override @@ -165,15 +154,6 @@ class _VotingPageState extends State _pagingController ..addPageRequestListener(_handleProposalsPageRequest) ..notifyPageRequestListeners(0); - - // TODO(damian-molinski): same behavior already exists in DiscoveryPage because - // of way confirmation dialog is shown. Refactor it. - if (showKeychainDeletedDialog) { - showKeychainDeletedDialog = false; - WidgetsBinding.instance.addPostFrameCallback((_) async { - await _showKeychainDeletedDialog(context); - }); - } } VotingPageTab _determineTab( @@ -207,10 +187,6 @@ class _VotingPageState extends State await context.read().getProposals(request); } - Future _showKeychainDeletedDialog(BuildContext context) async { - await KeychainDeletedDialog.show(context); - } - void _updateRoute({ Optional? categoryId, VotingPageTab? tab, diff --git a/catalyst_voices/apps/voices/lib/pages/voting/widgets/voting_list/voting_list_ballot.dart b/catalyst_voices/apps/voices/lib/pages/voting/widgets/voting_list/voting_list_ballot.dart index a841aee89fcc..ca623fc572d5 100644 --- a/catalyst_voices/apps/voices/lib/pages/voting/widgets/voting_list/voting_list_ballot.dart +++ b/catalyst_voices/apps/voices/lib/pages/voting/widgets/voting_list/voting_list_ballot.dart @@ -76,7 +76,7 @@ class _VotingListBallotEmptyState extends StatelessWidget { return Align( alignment: const Alignment(0, -0.3), child: EmptyState( - image: VoicesAssets.images.noVotes.buildPicture(), + image: VoicesAssets.images.svg.noVotes.buildPicture(), title: Text(_buildTitleParts(context).join('\n')), constraints: const BoxConstraints(maxWidth: 236), ), diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/header/workspace_timeline.dart b/catalyst_voices/apps/voices/lib/pages/workspace/header/workspace_timeline.dart index 383b79924253..aa8d30f6ef68 100644 --- a/catalyst_voices/apps/voices/lib/pages/workspace/header/workspace_timeline.dart +++ b/catalyst_voices/apps/voices/lib/pages/workspace/header/workspace_timeline.dart @@ -144,7 +144,7 @@ class _ViewComments extends StatelessWidget { @override Widget build(BuildContext context) { return BlocSelector( - selector: (state) => state.hasComments, + selector: (state) => state.userProposals.hasComments, builder: (context, hasComments) { return hasComments ? Padding( diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/page/workspace_error.dart b/catalyst_voices/apps/voices/lib/pages/workspace/page/workspace_error.dart index cbf8bcbb3a81..982b44540d00 100644 --- a/catalyst_voices/apps/voices/lib/pages/workspace/page/workspace_error.dart +++ b/catalyst_voices/apps/voices/lib/pages/workspace/page/workspace_error.dart @@ -1,21 +1,21 @@ -import 'package:catalyst_voices/common/typedefs.dart'; import 'package:catalyst_voices/widgets/widgets.dart'; import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; import 'package:flutter/material.dart'; -class WorkspaceErrorSelector extends StatelessWidget { - const WorkspaceErrorSelector({super.key}); +class WorkspaceError extends StatelessWidget { + const WorkspaceError({super.key}); @override Widget build(BuildContext context) { - return BlocSelector( - selector: (state) => (show: state.showError, data: state.error), - builder: (context, state) { - final errorMessage = state.data?.message(context); + return BlocSelector( + selector: (state) => state.error, + builder: (context, error) { + final errorMessage = error?.message(context); return Offstage( - offstage: !state.show, + offstage: error == null, child: _WorkspaceError( message: errorMessage ?? context.l10n.somethingWentWrong, ), diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/page/workspace_loading.dart b/catalyst_voices/apps/voices/lib/pages/workspace/page/workspace_loading.dart index baf3c646877d..cffcfbcce12a 100644 --- a/catalyst_voices/apps/voices/lib/pages/workspace/page/workspace_loading.dart +++ b/catalyst_voices/apps/voices/lib/pages/workspace/page/workspace_loading.dart @@ -2,10 +2,10 @@ import 'package:catalyst_voices/widgets/indicators/voices_loading_overlay.dart'; import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; import 'package:flutter/material.dart'; -class WorkspaceLoadingSelector extends StatelessWidget { +class WorkspaceLoading extends StatelessWidget { final Widget child; - const WorkspaceLoadingSelector({ + const WorkspaceLoading({ super.key, required this.child, }); @@ -15,13 +15,31 @@ class WorkspaceLoadingSelector extends StatelessWidget { return BlocSelector( selector: (state) => state.isLoading, builder: (context, isLoading) { - return Stack( - children: [ - child, - VoicesLoadingOverlay(show: isLoading), - ], + return _LoadingStack( + isLoading: isLoading, + child: child, ); }, ); } } + +class _LoadingStack extends StatelessWidget { + final bool isLoading; + final Widget child; + + const _LoadingStack({ + required this.isLoading, + required this.child, + }); + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + child, + VoicesLoadingOverlay(show: isLoading), + ], + ); + } +} diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/page/workspace_page.dart b/catalyst_voices/apps/voices/lib/pages/workspace/page/workspace_page.dart index 5e2b69b30bbf..c9d2390ea095 100644 --- a/catalyst_voices/apps/voices/lib/pages/workspace/page/workspace_page.dart +++ b/catalyst_voices/apps/voices/lib/pages/workspace/page/workspace_page.dart @@ -32,21 +32,23 @@ class _WorkspacePageState extends State Widget build(BuildContext context) { return const ProposalSubmissionPhaseAware( activeChild: Scaffold( - body: WorkspaceLoadingSelector( - child: SingleChildScrollView( - child: Column( - children: [ - SizedBox(height: 10), - WorkspaceHeader(), - Stack( - children: [ - WorkspaceErrorSelector(), - WorkspaceUserProposalsSelector(), - ], - ), - SizedBox(height: 50), - ], - ), + body: WorkspaceLoading( + child: CustomScrollView( + slivers: [ + SliverToBoxAdapter( + child: SizedBox(height: 10), + ), + SliverToBoxAdapter( + child: WorkspaceHeader(), + ), + SliverToBoxAdapter( + child: WorkspaceError(), + ), + WorkspaceUserProposals(), + SliverToBoxAdapter( + child: SizedBox(height: 50), + ), + ], ), ), ), diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/page/workspace_user_proposals.dart b/catalyst_voices/apps/voices/lib/pages/workspace/page/workspace_user_proposals.dart index ca43b0fc1129..db3a893f1168 100644 --- a/catalyst_voices/apps/voices/lib/pages/workspace/page/workspace_user_proposals.dart +++ b/catalyst_voices/apps/voices/lib/pages/workspace/page/workspace_user_proposals.dart @@ -1,27 +1,19 @@ import 'package:catalyst_voices/pages/workspace/user_proposals/user_proposals.dart'; import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; -import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; import 'package:flutter/material.dart'; -typedef UserProposalsSelectorState = ({bool show, List proposals}); - -class WorkspaceUserProposalsSelector extends StatelessWidget { - const WorkspaceUserProposalsSelector({super.key}); +class WorkspaceUserProposals extends StatelessWidget { + const WorkspaceUserProposals({super.key}); @override Widget build(BuildContext context) { - return BlocSelector( - selector: (state) => ( - proposals: state.userProposals, - show: state.showProposals, - ), - builder: (context, state) { - return Offstage( - offstage: !state.show, - child: UserProposals( - items: state.proposals, - ), - ); + return BlocSelector( + selector: (state) => state.showProposals, + builder: (context, show) { + if (!show) { + return const SliverToBoxAdapter(child: SizedBox.shrink()); + } + return const UserProposals(); }, ); } diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/user_proposals/user_proposal_section.dart b/catalyst_voices/apps/voices/lib/pages/workspace/user_proposals/user_proposal_section.dart index ffeb13fa330e..9951bce71a5d 100644 --- a/catalyst_voices/apps/voices/lib/pages/workspace/user_proposals/user_proposal_section.dart +++ b/catalyst_voices/apps/voices/lib/pages/workspace/user_proposals/user_proposal_section.dart @@ -36,22 +36,25 @@ class _ListOfProposals extends StatelessWidget { @override Widget build(BuildContext context) { if (items.isEmpty) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 22, vertical: 22), - child: Text(emptyTextMessage), - ); - } else { - return Column( - children: items - .map( - (e) => WorkspaceProposalCard( - key: ValueKey(e.selfRef), - proposal: e, - ), - ) - .toList(), + return SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 22, vertical: 22), + child: Text(emptyTextMessage), + ), ); } + + return SliverList.builder( + itemCount: items.length, + itemBuilder: (context, index) { + final item = items[index]; + + return WorkspaceProposalCard( + key: ValueKey(item.selfRef), + proposal: item, + ); + }, + ); } } @@ -68,22 +71,13 @@ class _ProposalVisibility extends StatelessWidget { @override Widget build(BuildContext context) { - return AnimatedSwitcher( - duration: const Duration(milliseconds: 200), - reverseDuration: const Duration(milliseconds: 200), - transitionBuilder: (child, animation) { - return FadeTransition( - opacity: animation, - child: child, - ); - }, - child: Offstage( - offstage: !isExpanded, - child: _ListOfProposals( - items: items, - emptyTextMessage: emptyTextMessage, - ), - ), + if (!isExpanded) { + return const SliverToBoxAdapter(child: SizedBox.shrink()); + } + + return _ListOfProposals( + items: items, + emptyTextMessage: emptyTextMessage, ); } } @@ -93,16 +87,16 @@ class _UserProposalSectionState extends State { @override Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SectionLearnMoreHeader( - title: widget.title, - info: widget.info, - learnMoreUrl: widget.learnMoreUrl, - isExpanded: _isExpanded, - onExpandedChanged: _onExpandedChanged, + return SliverMainAxisGroup( + slivers: [ + SliverToBoxAdapter( + child: SectionLearnMoreHeader( + title: widget.title, + info: widget.info, + learnMoreUrl: widget.learnMoreUrl, + isExpanded: _isExpanded, + onExpandedChanged: _onExpandedChanged, + ), ), _ProposalVisibility( isExpanded: _isExpanded, diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/user_proposals/user_proposals.dart b/catalyst_voices/apps/voices/lib/pages/workspace/user_proposals/user_proposals.dart index 53518f093c00..aeaf798e8ae7 100644 --- a/catalyst_voices/apps/voices/lib/pages/workspace/user_proposals/user_proposals.dart +++ b/catalyst_voices/apps/voices/lib/pages/workspace/user_proposals/user_proposals.dart @@ -2,18 +2,52 @@ import 'package:catalyst_voices/common/constants/constants.dart'; import 'package:catalyst_voices/common/ext/build_context_ext.dart'; import 'package:catalyst_voices/pages/workspace/user_proposals/user_proposal_section.dart'; import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; import 'package:catalyst_voices_models/catalyst_voices_models.dart'; import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; import 'package:flutter/widgets.dart'; -class UserProposals extends StatefulWidget { - final List items; +class UserProposals extends StatelessWidget { + const UserProposals({super.key}); - const UserProposals({super.key, required this.items}); + @override + Widget build(BuildContext context) { + return const SliverPadding( + padding: EdgeInsets.symmetric(horizontal: 32), + sliver: SliverMainAxisGroup( + slivers: [ + SliverToBoxAdapter( + child: _Header(), + ), + SliverToBoxAdapter( + child: _Divider(), + ), + SliverToBoxAdapter( + child: SizedBox(height: 20), + ), + _UserSubmittedProposals(), + _UserDraftProposals(), + _UserLocalProposals(), + _UserInactiveProposals(), + ], + ), + ); + } +} + +class _Divider extends StatelessWidget { + const _Divider(); @override - State createState() => _UserProposalsState(); + Widget build(BuildContext context) { + return VoicesDivider( + indent: 0, + endIndent: 0, + height: 24, + color: context.colorScheme.primary, + ); + } } class _Header extends StatelessWidget { @@ -28,67 +62,95 @@ class _Header extends StatelessWidget { } } -class _UserProposalsState extends State { - Iterable get _active => widget.items.where((e) => e.fromActiveCampaign); +class _UserDraftProposals extends StatelessWidget { + const _UserDraftProposals(); + + @override + Widget build(BuildContext context) { + return BlocSelector( + selector: (state) { + return state.userProposals.draftProposals; + }, + builder: (context, proposals) { + return UserProposalSection( + items: proposals.items, + emptyTextMessage: context.l10n.noDraftUserProposals, + title: context.l10n.sharedForPublicInProgress, + info: context.l10n.sharedForPublicInfoMarkdown, + learnMoreUrl: VoicesConstants.proposalPublishingDocsUrl, + ); + }, + ); + } +} + +class _UserInactiveProposals extends StatelessWidget { + const _UserInactiveProposals(); - List get _draft => _active.where((e) => e.publish.isDraft).toList(); + @override + Widget build(BuildContext context) { + return BlocSelector( + selector: (state) { + return state.userProposals.inactiveProposals; + }, + builder: (context, proposals) { + if (proposals.items.isEmpty) { + return const SliverToBoxAdapter(); + } + return UserProposalSection( + items: proposals.items, + emptyTextMessage: '', + title: context.l10n.notActiveCampaign, + info: context.l10n.notActiveCampaignInfoMarkdown, + ); + }, + ); + } +} - Iterable get _inactive => widget.items.where((e) => !e.fromActiveCampaign); +class _UserLocalProposals extends StatelessWidget { + const _UserLocalProposals(); - List get _local => _active.where((e) => e.publish.isLocal).toList(); + @override + Widget build(BuildContext context) { + return BlocSelector( + selector: (state) { + return state.userProposals.localProposals; + }, + builder: (context, proposals) { + return UserProposalSection( + items: proposals.items, + emptyTextMessage: context.l10n.noLocalUserProposals, + title: context.l10n.notPublished, + info: context.l10n.notPublishedInfoMarkdown, + learnMoreUrl: VoicesConstants.proposalPublishingDocsUrl, + ); + }, + ); + } +} - List get _submitted => - _active.where((e) => e.publish.isPublished).toList(); +class _UserSubmittedProposals extends StatelessWidget { + const _UserSubmittedProposals(); @override Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 32), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const _Header(), - VoicesDivider( - indent: 0, - endIndent: 0, - height: 24, - color: context.colorScheme.primary, - ), - const SizedBox(height: 20), - UserProposalSection( - items: _submitted, - emptyTextMessage: context.l10n.noFinalUserProposals, - title: context.l10n.submittedForReview( - _submitted.length, - ProposalDocument.maxSubmittedProposalsPerUser, - ), - info: context.l10n.submittedForReviewInfoMarkdown, - learnMoreUrl: VoicesConstants.proposalPublishingDocsUrl, + return BlocSelector( + selector: (state) { + return state.userProposals.finalProposals; + }, + builder: (context, proposals) { + return UserProposalSection( + items: proposals.items, + emptyTextMessage: context.l10n.noFinalUserProposals, + title: context.l10n.submittedForReview( + proposals.items.length, + ProposalDocument.maxSubmittedProposalsPerUser, ), - UserProposalSection( - items: _draft, - emptyTextMessage: context.l10n.noDraftUserProposals, - title: context.l10n.sharedForPublicInProgress, - info: context.l10n.sharedForPublicInfoMarkdown, - learnMoreUrl: VoicesConstants.proposalPublishingDocsUrl, - ), - UserProposalSection( - items: _local, - emptyTextMessage: context.l10n.noLocalUserProposals, - title: context.l10n.notPublished, - info: context.l10n.notPublishedInfoMarkdown, - learnMoreUrl: VoicesConstants.proposalPublishingDocsUrl, - ), - if (_inactive.isNotEmpty) - UserProposalSection( - items: _inactive.toList(), - emptyTextMessage: '', - title: context.l10n.notActiveCampaign, - info: context.l10n.notActiveCampaignInfoMarkdown, - ), - ], - ), + info: context.l10n.submittedForReviewInfoMarkdown, + learnMoreUrl: VoicesConstants.proposalPublishingDocsUrl, + ); + }, ); } } diff --git a/catalyst_voices/apps/voices/lib/routes/routing/root_route.dart b/catalyst_voices/apps/voices/lib/routes/routing/root_route.dart index c14f7824e33c..2d62a9557bfc 100644 --- a/catalyst_voices/apps/voices/lib/routes/routing/root_route.dart +++ b/catalyst_voices/apps/voices/lib/routes/routing/root_route.dart @@ -10,6 +10,11 @@ part 'root_route.g.dart'; @TypedGoRoute(path: '/') final class RootRoute extends GoRouteData { + static List rootRouteNameOptions = [ + DiscoveryRoute.name, + VotingRoute.name, + ]; + const RootRoute(); @override diff --git a/catalyst_voices/apps/voices/lib/routes/routing/spaces_route.dart b/catalyst_voices/apps/voices/lib/routes/routing/spaces_route.dart index d0ceacbe0e4e..890e49a53837 100644 --- a/catalyst_voices/apps/voices/lib/routes/routing/spaces_route.dart +++ b/catalyst_voices/apps/voices/lib/routes/routing/spaces_route.dart @@ -41,13 +41,11 @@ final class CategoryDetailRoute extends GoRouteData with FadePageTransitionMixin final class DiscoveryRoute extends GoRouteData with FadePageTransitionMixin { static const name = 'discovery'; - final bool? $extra; - - const DiscoveryRoute({this.$extra}); + const DiscoveryRoute(); @override Widget build(BuildContext context, GoRouterState state) { - return SentryDisplayWidget(child: DiscoveryPage(keychainDeleted: $extra ?? false)); + return const SentryDisplayWidget(child: DiscoveryPage()); } } @@ -120,7 +118,7 @@ final class ProposalsRoute extends GoRouteData with FadePageTransitionMixin { ), TypedGoRoute( path: '/voting', - name: 'voting', + name: VotingRoute.name, ), TypedGoRoute( path: '/funded_projects', @@ -183,14 +181,14 @@ final class TreasuryRoute extends GoRouteData } final class VotingRoute extends GoRouteData with FadePageTransitionMixin { + static const name = 'voting'; + final String? categoryId; final String? tab; - final bool? $extra; const VotingRoute({ this.categoryId, this.tab, - this.$extra, }); @override @@ -203,7 +201,6 @@ final class VotingRoute extends GoRouteData with FadePageTransitionMixin { return VotingPage( categoryId: categoryRef, tab: tab, - keychainDeleted: $extra ?? false, ); } } diff --git a/catalyst_voices/apps/voices/lib/widgets/common/resizable_box_parent.dart b/catalyst_voices/apps/voices/lib/widgets/common/resizable_box_parent.dart index e473fbc6221b..df1b7c003ee7 100644 --- a/catalyst_voices/apps/voices/lib/widgets/common/resizable_box_parent.dart +++ b/catalyst_voices/apps/voices/lib/widgets/common/resizable_box_parent.dart @@ -103,7 +103,7 @@ class _ResizableBoxState extends State<_ResizableBox> { } }); }, - child: VoicesAssets.images.dragger.buildIcon(size: 15), + child: VoicesAssets.images.svg.dragger.buildIcon(size: 15), ), ), ], diff --git a/catalyst_voices/apps/voices/lib/widgets/empty_state/specialized/proposals_pagination_empty_state.dart b/catalyst_voices/apps/voices/lib/widgets/empty_state/specialized/proposals_pagination_empty_state.dart index b4a55e9a6f42..cea141942583 100644 --- a/catalyst_voices/apps/voices/lib/widgets/empty_state/specialized/proposals_pagination_empty_state.dart +++ b/catalyst_voices/apps/voices/lib/widgets/empty_state/specialized/proposals_pagination_empty_state.dart @@ -33,7 +33,7 @@ class ProposalsPaginationEmptyState extends StatelessWidget { ? Text(context.l10n.tryDifferentSearch) : Text(context.l10n.discoverySpaceEmptyProposals), image: VoicesImagesScheme( - image: VoicesAssets.images.noProposalForeground.buildPicture(), + image: VoicesAssets.images.svg.noProposalForeground.buildPicture(), background: Container( height: 180, decoration: BoxDecoration( diff --git a/catalyst_voices/apps/voices/lib/widgets/footers/standard_links_page_footer.dart b/catalyst_voices/apps/voices/lib/widgets/footers/standard_links_page_footer.dart index 1221857aabb8..71dfb82e392d 100644 --- a/catalyst_voices/apps/voices/lib/widgets/footers/standard_links_page_footer.dart +++ b/catalyst_voices/apps/voices/lib/widgets/footers/standard_links_page_footer.dart @@ -41,19 +41,19 @@ class StandardLinksPageFooter extends StatelessWidget { ], lowerChildren: [ VoicesIconButton( - child: VoicesAssets.images.facebookMono.buildIcon(), + child: VoicesAssets.images.svg.facebookMono.buildIcon(), onTap: () { ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Facebook'))); }, ), VoicesIconButton( - child: VoicesAssets.images.linkedinMono.buildIcon(), + child: VoicesAssets.images.svg.linkedinMono.buildIcon(), onTap: () { ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('LinkedIn'))); }, ), VoicesIconButton( - child: VoicesAssets.images.xMono.buildIcon(), + child: VoicesAssets.images.svg.xMono.buildIcon(), onTap: () { ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('X'))); }, diff --git a/catalyst_voices/apps/voices/lib/widgets/heroes/section_hero.dart b/catalyst_voices/apps/voices/lib/widgets/heroes/section_hero.dart index 0c74d5f85a4c..9d3637f27493 100644 --- a/catalyst_voices/apps/voices/lib/widgets/heroes/section_hero.dart +++ b/catalyst_voices/apps/voices/lib/widgets/heroes/section_hero.dart @@ -6,7 +6,8 @@ class HeroSection extends StatelessWidget { final AlignmentGeometry alignment; final VideoCacheKey asset; final BoxConstraints constraints; - + final VoicesVideoErrorBuilder? errorBuilder; + final VideoPlaybackConfig playbackConfig; final Widget child; const HeroSection({ @@ -14,6 +15,8 @@ class HeroSection extends StatelessWidget { this.alignment = Alignment.bottomLeft, required this.asset, this.constraints = const BoxConstraints.tightFor(height: 650), + this.errorBuilder, + this.playbackConfig = const VideoPlaybackConfig(), required this.child, }); @@ -26,6 +29,8 @@ class HeroSection extends StatelessWidget { _Background( asset: asset, constraints: constraints, + errorBuilder: errorBuilder, + playbackConfig: playbackConfig, ), Align( alignment: alignment, @@ -39,10 +44,14 @@ class HeroSection extends StatelessWidget { class _Background extends StatelessWidget { final VideoCacheKey asset; final BoxConstraints constraints; + final VideoPlaybackConfig playbackConfig; + final VoicesVideoErrorBuilder? errorBuilder; const _Background({ required this.asset, required this.constraints, + required this.playbackConfig, + this.errorBuilder, }); @override @@ -52,6 +61,8 @@ class _Background extends StatelessWidget { child: VoicesVideoPlayer( key: const Key('HeroBackgroundVideo'), asset: asset, + errorBuilder: errorBuilder, + playbackConfig: playbackConfig, ), ); } diff --git a/catalyst_voices/apps/voices/lib/widgets/indicators/voices_linear_progress_indicator.dart b/catalyst_voices/apps/voices/lib/widgets/indicators/voices_linear_progress_indicator.dart index d5212b85b53b..2a66eecd557d 100644 --- a/catalyst_voices/apps/voices/lib/widgets/indicators/voices_linear_progress_indicator.dart +++ b/catalyst_voices/apps/voices/lib/widgets/indicators/voices_linear_progress_indicator.dart @@ -12,19 +12,27 @@ class AnimatedVoicesLinearProgressIndicator extends StatelessWidget { /// The weight of the progress indicator. final VoicesProgressIndicatorWeight weight; + /// The duration of the animation when the progress value changes. + final Duration animationDuration; + + /// The curve of the animation when the progress value changes. + final Curve animationCurve; + const AnimatedVoicesLinearProgressIndicator({ super.key, required this.value, this.showTrack = true, this.weight = VoicesProgressIndicatorWeight.medium, + this.animationDuration = const Duration(milliseconds: 200), + this.animationCurve = Curves.easeInOut, }); @override Widget build(BuildContext context) { return TweenAnimationBuilder( tween: Tween(begin: 0, end: value), - duration: const Duration(milliseconds: 200), - curve: Curves.easeInOut, + duration: animationDuration, + curve: animationCurve, builder: (context, value, _) { return VoicesLinearProgressIndicator( value: value, diff --git a/catalyst_voices/apps/voices/lib/widgets/rich_text/insert_image_error.dart b/catalyst_voices/apps/voices/lib/widgets/rich_text/insert_image_error.dart index dff4270f92b0..2349c91ef708 100644 --- a/catalyst_voices/apps/voices/lib/widgets/rich_text/insert_image_error.dart +++ b/catalyst_voices/apps/voices/lib/widgets/rich_text/insert_image_error.dart @@ -19,7 +19,7 @@ class InsertImageError extends StatelessWidget { SizedBox( width: 32, height: 32, - child: VoicesAssets.images.insertImageError.buildIcon(), + child: VoicesAssets.images.svg.insertImageError.buildIcon(), ), const SizedBox(height: 12), Text( diff --git a/catalyst_voices/apps/voices/lib/widgets/rich_text/insert_new_image_dialog_header.dart b/catalyst_voices/apps/voices/lib/widgets/rich_text/insert_new_image_dialog_header.dart index 13d0e4eeac82..650963ce93ca 100644 --- a/catalyst_voices/apps/voices/lib/widgets/rich_text/insert_new_image_dialog_header.dart +++ b/catalyst_voices/apps/voices/lib/widgets/rich_text/insert_new_image_dialog_header.dart @@ -23,7 +23,7 @@ class InsertNewImageDialogHeader extends StatelessWidget { SizedBox( width: 32, height: 32, - child: images.insertImage.buildIcon(), + child: images.svg.insertImage.buildIcon(), ), const SizedBox(width: 12), Text( diff --git a/catalyst_voices/apps/voices/lib/widgets/video/video_player.dart b/catalyst_voices/apps/voices/lib/widgets/video/video_player.dart index 0750bfc1ccb4..55d4d5db93c6 100644 --- a/catalyst_voices/apps/voices/lib/widgets/video/video_player.dart +++ b/catalyst_voices/apps/voices/lib/widgets/video/video_player.dart @@ -3,16 +3,23 @@ import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; import 'package:flutter/material.dart'; import 'package:video_player/video_player.dart'; +typedef VoicesVideoErrorBuilder = + Widget Function(BuildContext context, Object? error, StackTrace? stackTrace); + class VoicesVideoPlayer extends StatefulWidget { final VideoCacheKey asset; final BoxFit fit; final Clip clipBehavior; + final VoicesVideoErrorBuilder? errorBuilder; + final VideoPlaybackConfig playbackConfig; const VoicesVideoPlayer({ super.key, required this.asset, this.fit = BoxFit.cover, this.clipBehavior = Clip.hardEdge, + this.errorBuilder, + this.playbackConfig = const VideoPlaybackConfig(), }); @override @@ -33,8 +40,9 @@ class _VoicesVideoPlayerState extends State with AutomaticKee future: _future, builder: (context, snapshot) { final controller = snapshot.data; - if (controller == null) { - return const SizedBox.expand(); + if (snapshot.hasError || controller == null) { + return widget.errorBuilder?.call(context, snapshot.error, snapshot.stackTrace) ?? + const SizedBox.expand(); } return FittedBox( @@ -58,6 +66,9 @@ class _VoicesVideoPlayerState extends State with AutomaticKee } Future _getController() { - return VideoManagerScope.of(context).createOrReinitializeController(widget.asset); + return VideoManagerScope.of(context).createOrReinitializeController( + widget.asset, + config: widget.playbackConfig, + ); } } diff --git a/catalyst_voices/apps/voices/pubspec.yaml b/catalyst_voices/apps/voices/pubspec.yaml index a24704b7552e..e22be7af88f9 100644 --- a/catalyst_voices/apps/voices/pubspec.yaml +++ b/catalyst_voices/apps/voices/pubspec.yaml @@ -89,6 +89,8 @@ dependencies: dev_dependencies: build_runner: ^2.5.4 catalyst_analysis: ^3.0.0 + catalyst_voices_dev: + path: ../../packages/internal/catalyst_voices_dev flutter_driver: sdk: flutter flutter_test: diff --git a/catalyst_voices/apps/voices/test/widgets/cards/small_proposal_card_test.dart b/catalyst_voices/apps/voices/test/widgets/cards/small_proposal_card_test.dart index 6c20099eaa85..c2c6314e37e0 100644 --- a/catalyst_voices/apps/voices/test/widgets/cards/small_proposal_card_test.dart +++ b/catalyst_voices/apps/voices/test/widgets/cards/small_proposal_card_test.dart @@ -1,12 +1,12 @@ import 'package:catalyst_voices/widgets/cards/proposal/proposal_card_widgets.dart' show DraftProposalChip, FinalProposalChip, PrivateProposalChip; import 'package:catalyst_voices/widgets/cards/proposal/small_proposal_card.dart'; +import 'package:catalyst_voices_dev/catalyst_voices_dev.dart'; import 'package:catalyst_voices_models/catalyst_voices_models.dart'; import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart' show ProposalVersionViewModel, UsersProposalOverview; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:uuid_plus/uuid_plus.dart'; import '../../helpers/helpers.dart'; @@ -19,15 +19,10 @@ void main() { late String draftVersion; setUpAll(() async { - // TODO(LynxLynxx): When we create dev test package use DocumentFactoryRef here - // Extracting DocumentFactoryRef to Shared not possible due to need of importing classes from - // repository package - proposalId = const Uuid().v7(); - draftVersion = const Uuid().v7(); - await Future.delayed(const Duration(milliseconds: 10), () {}); - latestVersion = const Uuid().v7(); - await Future.delayed(const Duration(milliseconds: 10), () {}); - localVersion = const Uuid().v7(); + proposalId = DocumentRefFactory.randomUuidV7(); + draftVersion = DocumentRefFactory.randomUuidV7(); + latestVersion = DocumentRefFactory.randomUuidV7(); + localVersion = DocumentRefFactory.randomUuidV7(); mockProposal = UsersProposalOverview( selfRef: SignedDocumentRef(id: proposalId, version: latestVersion), title: 'Test Proposal', diff --git a/catalyst_voices/apps/voices/web/drift_worker.js b/catalyst_voices/apps/voices/web/drift_worker.v1.js similarity index 100% rename from catalyst_voices/apps/voices/web/drift_worker.js rename to catalyst_voices/apps/voices/web/drift_worker.v1.js diff --git a/catalyst_voices/apps/voices/web/index.html b/catalyst_voices/apps/voices/web/index.html index 10c8d73051a2..4804520da5c1 100644 --- a/catalyst_voices/apps/voices/web/index.html +++ b/catalyst_voices/apps/voices/web/index.html @@ -20,7 +20,6 @@ Catalyst - + + + + + + + + + + + + + + + + + + diff --git a/catalyst_voices/scripts/version_web_assets/test/helper/build_wasm/main.dart.js_1.part.js b/catalyst_voices/scripts/version_web_assets/test/helper/build_wasm/main.dart.js_1.part.js new file mode 100644 index 000000000000..5eb19881851f --- /dev/null +++ b/catalyst_voices/scripts/version_web_assets/test/helper/build_wasm/main.dart.js_1.part.js @@ -0,0 +1,2218 @@ +((a,b)=>{a[b]=a[b]||{}})(self,"$__dart_deferred_initializers__") +$__dart_deferred_initializers__.current=function(a,b,c,$){var J,B,D,C={wO:function wO(){}, +dR2(){return new C.T2(B.dN("en"))}, +T2:function T2(d){this.a=d}, +bw(d,e,f){return new C.aCp(f,d,e)}, +aCp:function aCp(d,e,f){this.a=d +this.b=e +this.c=f}, +dSb(d,e,f,g,h,i,j){return new C.aoM(h,d,i,f,j,g,e)}, +d8k(d,e,f,g,h){var w,v,u,t,s,r,q,p,o=$.dxM().h7(e) +if(o!=null){w=o.b +v=w[1] +v.toString +u=w[3] +u.toString +t=$.dxL() +s=!t.b.test(e)?B.cd(Math.pow(10,f-w[2].length+1)):1 +r=u +q=v +p=!1}else{p=e.length!==0&&!D.c.p(e,"0") +s=p?B.cd(Math.pow(10,f)):1 +q="" +r=""}return new C.aoM(e,s,q,!h?d.r+q:q,r,r,p)}, +do7(d){return d.Q}, +do5(a6,a7,a8){var w,v,u,t,s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,a0,a1,a2,a3,a4,a5={} +a5.a=a8 +w=B.qf(a8,B.dsr(),null) +w.toString +a5.a=w +v=$.d3p().i(0,w) +u=v.e +t=$.btO() +s=v.ay +r=s +q=C.do7(v) +w=$.dAm().i(0,w) +w.toString +p=B.F(x.e,x.B) +switch(a7.a){case 0:o=w.a +break +case 1:o=w.b +if(o==null)o=w.a +break +case 2:o=w.c +break +default:throw B.i(B.CR("formatType"))}o.aF(0,new C.cBc(a5,!1,v,p)) +w=a5.a +n=B.dj7(v,q,!1,r,s,null) +m=n.b +l=n.a +k=n.d +j=n.c +i=n.e +h=D.h.au(Math.log(i)/$.dbN()) +g=n.ax +f=n.f +e=n.r +d=n.w +a0=n.x +a1=n.y +a2=n.z +a3=n.Q +a4=n.at +t=new C.cBa(p,!1,l,m,j,k,a2,a3,n.as,a4,g,!1,e,d,a0,a1,f,i,h,q,w,v,r,n.ay,new B.cQ(""),u.charCodeAt(0)-t) +t.sc3E(3) +t.sc3q(null) +t.cx=!1 +t.f=t.e=0 +return t}, +do8(d,e,f,g){var w,v,u +if(D.c.p(d,";")){w=d.split(";") +v=D.b.gV(w) +u=D.b.gN(w) +return new C.b7I(C.d8k(g,v,e,!1,D.c.p(v,g.f)),C.d8k(g,u,e,!1,!0))}else return C.d8k(g,d,e,!1,!1)}, +do6(d,e){return d/e}, +Ch:function Ch(){}, +b7J:function b7J(d){this.b=d +this.c=null +this.d=$}, +b7I:function b7I(d,e){this.a=d +this.b=e}, +aoM:function aoM(d,e,f,g,h,i,j){var _=this +_.a=d +_.b=e +_.c=f +_.d=g +_.e=h +_.f=i +_.r=j}, +cB9:function cB9(d,e){this.a=d +this.b=e}, +cBa:function cBa(d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,a0,a1,a2,a3,a4,a5){var _=this +_.ok=d +_.p1=e +_.p2=null +_.a=f +_.b=g +_.c=h +_.d=i +_.e=j +_.f=k +_.r=l +_.w=m +_.x=n +_.y=o +_.z=p +_.Q=q +_.at=r +_.ax=!1 +_.ay=s +_.ch=t +_.CW=null +_.cx=!1 +_.cy=null +_.db=!1 +_.dx=u +_.dy=v +_.fr=w +_.fx=a0 +_.fy=a1 +_.id=a2 +_.k1=a3 +_.k2=a4 +_.k4=a5}, +cBc:function cBc(d,e,f,g){var _=this +_.a=d +_.b=e +_.c=f +_.d=g}, +cBb:function cBb(d,e,f){this.a=d +this.b=e +this.c=f}, +cBd:function cBd(d,e){this.a=d +this.b=e}},A +J=c[1] +B=c[0] +D=c[2] +C=a.updateHolder(c[3],C) +A=c[4] +C.wO.prototype={} +C.T2.prototype={ +gbKw(){return"Account"}, +gbKx(){return"Create a new \nCatalyst Keychain"}, +gaFk(){return"Welcome to Catalyst"}, +gbKy(){return"If you already have a Catalyst keychain you can restore it on this device, or you can create a new Catalyst Keychain."}, +gbKz(){return"What do you want to do?"}, +gbKA(){return"On this device"}, +gbKB(){return"Recover your\nCatalyst Keychain"}, +gbKC(){return"Catalyst Keychain is your ticket to participate in innovation on the global stage. \n\nThese next steps will create your Catalyst keychain so you can enter new spaces, discover awesome ideas, and share your feedback to help improve ideas."}, +gbKD(){return"Create your Keychain now"}, +gaFl(){return"Create your Catalyst Keychain"}, +gbKE(){return"Please provide an email for proposer and reviewer roles."}, +gbKF(){return"Please verify email above in your email. Email Title: Catalyst verification"}, +gbKG(){return"On the next screen, you're going to see 12 words. \nThis is called your \"Catalyst seed phrase\".\n\nIt's like a super secure password that only you know, \nthat allows you to prove ownership of your keychain.\n\nUse your Catalyst seed phrase to login and recover your account on different devices, so be sure to put it somewhere safe!\n\nIt is a super secure password that only you know, so best is to write it down with pen and paper, so get this ready."}, +gbKH(){return"Great! Your Catalyst Keychain \nhas been created."}, +gbKI(){return"Continue Role setup"}, +bKJ(d){return d+" role summary"}, +gbKK(){return"Learn about Catalyst Roles"}, +gbKL(){return"Leaving before submitting means no change."}, +gbKM(){return"Continue to update"}, +gbKN(){return"Account update incomplete!"}, +gbKO(){return"I acknowledge that I have to re-submit my draft proposal as Final to be eligible for funding."}, +gbKT(d){return"Active"}, +gbL0(){return"Actor"}, +gbLa(){return"Add comment"}, +gbLn(){return"Add role"}, +gbLs(){return"Add URL"}, +gbLK(){return"I Agree"}, +gbLL(){return"I understand that after I choose a category, the proposal\u2019s category is locked and switching categories requires creating a new proposal."}, +gbLM(){return"I understand the category brief and will make sure my proposal fits the criteria."}, +gbLN(d){return"Alert"}, +gbLX(){return"Anonymous"}, +gbM_(){return"Internal server error"}, +gbM0(){return"Not found"}, +gbM1(){return"Service Unavailable"}, +gbM2(){return"Too many requests, please try again in 10 minutes."}, +gbM3(){return"Unknown error response"}, +gaf2(d){return"Back"}, +gbMS(){return"Back to Campaign"}, +gbMV(){return"Balance"}, +gaf5(){return"Become a Reviewer!"}, +gbMW(){return"Become a Voter"}, +gbN6(){return"best practices FAQ"}, +gbNm(){return"browse"}, +bNn(d){return"Browse Fund"+d+" proposals"}, +bNo(d){return"Please browse other Catalyst Fund"+d+" proposals on ProjectCatalyst.io"}, +gbNQ(){return"Campaign Categories"}, +gbNR(){return"Campaign Dates"}, +gaGK(){return"Campaign End"}, +bNS(d,e){return"Voting for Fund"+d+" opens "+e}, +gbNT(){return"After Campaign"}, +gaGL(){return"Before Campaign"}, +gbNU(){return"During Campaign"}, +gbNV(){return"Events"}, +gbNW(){return"Campaign Preview"}, +gbNX(){return"Views"}, +gaGM(){return"Campaign Start"}, +gbNY(){return"Campaign Total Ask"}, +gbNZ(){return"Funds requested for all final proposals"}, +gbO_(){return"Campaign Treasury"}, +gbO0(){return"Total budget, including ecosystem incentives"}, +ga01(){return"Cancel anyway"}, +gpc(){return"Cancel"}, +gbO2(){return"Cancel"}, +gbO6(){return"Can't edit this proposal"}, +gbO7(){return"We can't find your wallet"}, +gbOm(){return"Catalyst"}, +gbOn(){return"The Catalyst app is closed."}, +aGT(d,e,f,g){return"Catalyst App closes in "+d+"d, "+e+"h, "+f+"m, "+g+"s."}, +gbOo(){return"Project Catalyst Terms and Conditions"}, +Pp(d){return"Catalyst Fund"+d}, +gbOp(){return"Catalyst ID"}, +gAK(){return"Catalyst Keychain"}, +gbOq(){return"Catalyst knowledge base"}, +gbOr(){return"Project Catalyst Platform Privacy Policy"}, +gbOs(){return"Project Catalyst Platform Terms of Use"}, +gbOt(){return"Categories"}, +gaGU(){return"Category"}, +gbOu(){return"Category Budget"}, +gbOv(){return"Category Details"}, +gbOw(){return"Category Proposals"}, +gbOx(){return"Choose Wisely! Review the category and its rules carefully as this can\u2019t be changed after you make a selection. To switch, you\u2019ll need to start a new proposal."}, +gbOy(){return"Caution:"}, +ga05(){return"Change"}, +bOP(d){var w=null +return"Submit no more than "+d+" "+B.jq(d,w,this.a,w,"proposal","proposals",w,w)+" as Final."}, +gbOQ(){return"Check on Cardano scan"}, +gbOV(){return"Choose Cardano Wallet"}, +gbOW(){return"Choose other wallet"}, +gbOZ(d){return"Clear"}, +gcu(d){return"Close"}, +gbPp(){return"Coming Soon"}, +gbPv(){return"Hide replies"}, +gafX(){return"Set Display Name"}, +gbPw(){return"Enter Name"}, +gaHk(){return"Pick a display name to join the conversation and leave a comment."}, +bPC(d){return""+d+" "+B.jq(d,null,this.a,null,"reply","replies",null,"replies")}, +afY(d){var w="Comments" +return B.jq(d,null,this.a,null,"Comment",w,null,w)}, +gafZ(){return"Comments"}, +gbPD(){return"Newest First"}, +gbPE(){return"Oldest First"}, +gbPI(d){return"Completed"}, +gaHw(d){return"Confirm"}, +gaHx(){return"Confirm password"}, +gbPP(){return"Confirm your vote submission"}, +gbPQ(){return"Confirmed"}, +gbQ_(){return"Continue as guest"}, +gaHE(){return"Continue"}, +gbQ0(){return"Contributor"}, +gbQ1(){return"Become a Catalyst contributor to help shape innovative ideas and have a voice in ecosystem decisions that matter."}, +gbQ2(){return"Comment on Proposals"}, +gbQ3(){return"Select favorites"}, +gbQ4(){return"Treasury guardian"}, +gbQe(){return"Copied!"}, +gbQf(){return"Copy Link"}, +gbTw(){return"In the next step you write your Catalyst roles and \naccount to the Cardano Mainnet."}, +gbTx(){return"Congratulations your Catalyst \nKeychain is created!"}, +gbTy(){return"Link your Cardano Wallet & Roles"}, +gbTz(){return"Make sure you write down your 12-words in a safe place as well."}, +gbTA(){return"Select your 12 written down words in \nthe correct order."}, +gbTB(){return"Next, we're going to make sure that you've written down your Catalyst seed phrase correctly. \n\nWe don't save your Catalyst seed phrase, so it's important \nto make sure you have it right. Thats why we don't trust, we verify before continuing. \n\nIt's also good practice to get familiar with using a \nseed phrase if you're new to crypto."}, +gbTC(){return"Write down your Catalyst seed phrase"}, +gbTD(){return"Input your Catalyst seed phrase"}, +gbTE(){return"Now let's set your Unlock password for this device!"}, +gbTF(){return"Enter your seed phrase to recover your Catalyst Keychain on any device.\n\nIt's kinda like your email and password all rolled into one, so keep it somewhere safe!\n\nIn the next step we'll add a password to your Catalyst Keychain, so you can lock/unlock access to Catalyst App."}, +gbTG(){return"Nice job! You've successfully verified your Catalyst seed phase"}, +gbTH(){return"Export Security Words"}, +bTI(d){return"Before using this feature, please read this "+d}, +gbTJ(){return"Are you sure you want to export your key?"}, +gbTK(){return"I have written down/exported my 12 words"}, +gbTL(){return"Write down your Catalyst seed phrase"}, +gbTM(){return"In this next step, you'll set your Unlock Password for this device. It's like a shortcut for proving ownership of your Keychain. \n\nWhenever you recover your account for the first time on a new device, you'll need to use your Catalyst seed phrase to get started. Every time after that, you can use your Unlock Password to quickly regain access."}, +gbTN(){return y.d}, +gbTO(){return"Please provide a password for your Catalyst Keychain."}, +gbTP(){return"Catalyst unlock password"}, +bTV(d){return"I agree to the "+d+"."}, +gbTW(){return"Mandatory Acknowledgements"}, +bTX(d,e){return"I agree to the "+d+" & "+e+"."}, +gbTY(){return"Congratulations your profile is setup!"}, +bTZ(d){return"In the following account creation steps we will:\n\n1. Setup your profile\n2. Create your Catalyst Keychain\n3. Link Cardano wallet & roles\n\nTo ensure a smooth experience, completing your account setup in one session is essential\u2014stay focused and avoid interruptions to finalize everything efficiently. Make sure your Cardano wallet has at least "+d+" before continuing."}, +gbU_(){return"Create your profile now"}, +gbU0(){return"Introduction"}, +gbU1(){return"In the next step you Create your Catalyst Keychain"}, +gbU2(){return"Please provide a nickname or real name."}, +gbU3(){return"Display name"}, +gbU4(){return"What should we call you?"}, +gbU5(){return"You must provide your e-mail if you want to become a proposer or a reviewer."}, +gbU6(){return"E-mail"}, +gbU7(){return"Your e-mail"}, +gbU8(){return"If you have an Ideascale account, using the same email works best."}, +gbU9(){return"Email is stored in Catalyst database"}, +gbUa(){return"Email is never published on chain"}, +gbUb(){return"Optional except for proposers or reviewers"}, +gbUc(){return"I agree to receive emails. I can unsubscribe anytime."}, +gbUd(){return"Setup your profile"}, +gagW(){return"Create Proposal"}, +bUe(d){return"Create Proposal in "+d}, +gbUo(){return"Current Ask"}, +gbUp(){return"Current Campaign"}, +gaJ9(){return"Project Catalyst turns economic power into innovation power by using the Cardano Treasury to incentivize and fund community-approved ideas."}, +gbUq(){return"We currently support the following wallets:"}, +aJg(d,e){return d+" at "+e}, +gbUy(){return"Please select a date within the range of today and one year from today."}, +gaJh(){return"Entered day exceeds the maximum days for this month."}, +gbUA(){return"d"}, +bUB(d){var w=null +return B.jq(d,w,this.a,w,"Day","Days",w,w)}, +gbUZ(){return"Default"}, +gahc(){return"Default"}, +gbV0(){return"Delegation"}, +gan_(d){return"Delete"}, +gbV7(){return"Error. Please type 'Remove Keychain' to remove your account from this device."}, +gbV8(){return"Confirm removal"}, +gbV9(){return"Remove Keychain"}, +gbVa(){return"To continue with the removal type: 'Remove Keychain'."}, +gbVb(){return"If you want to publish this iteration as final, please delete the local draft."}, +gEd(d){return"Description"}, +gbVw(){return"Already developer!"}, +bVx(d){return"Open DevTools with "+d+" shortcut"}, +gbVy(){return"You became developer!"}, +bVz(d){return"Open DevTools with "+d+" shortcut"}, +bVA(d){return""+d+" taps left"}, +gbVB(){return"Keep on tapping"}, +gbVM(){return"Please enable to continue your wallet extension, and refresh the application."}, +gbVN(){return"Disabled Extension"}, +gbVP(){return"Discovery Homepage"}, +gbVQ(){return"Once this campaign launches draft proposals will be shared here."}, +gaJL(d){return"Display Name"}, +gbW2(){return"Do:"}, +gbWw(){return"The imported document does not have valid format or is corrupted."}, +gbWy(){return"Don't show again"}, +gbWz(){return"Don't:"}, +gahF(){return"Draft"}, +gbWF(){return"Discovery"}, +gbWG(){return"Funded projects"}, +gbWH(){return"Treasury"}, +gbWI(){return"Voting"}, +gbWJ(){return"Workspace"}, +gbWK(){return"Drep"}, +gbWL(){return"The dRep has an Expert Role in the Cardano/Catalyst as people can delegate their vote to Cardano Experts."}, +gbWM(){return"Cast delegated votes"}, +gbWN(){return"Comment Functionality"}, +gbWO(){return"Delegated Votes"}, +gbWP(){return"dRep rewards"}, +gbWQ(){return"Community expert"}, +guU(d){return"Duration"}, +gaJX(){return"Edit"}, +gbWU(){return"Before you can change your role, there are a few steps to complete first. Follow the instructions carefully to ensure a smooth transition.\n\nTo continue you will need to re-register, this will allow you to select new roles. This process will take around 3-7 minutes.\n"}, +gbWV(){return"Editing Roles"}, +gbWW(){return"Editor"}, +gbWY(){return"Email"}, +gbWZ(){return"Email address"}, +gbX_(){return"Check your inbox to verify your email and unlock all features."}, +gbX0(){return"You can\u2019t publish a proposal until your email is verified. Go to {destination} to verify it."}, +gbX1(){return"Email Not Verified"}, +gaK1(){return"Go to My Account"}, +gaK2(){return"Before you can publish proposals, please make sure your \naccount has a verified email address."}, +gaK3(){return"Verified email not found!"}, +gbX4(){return"No search results found"}, +gbXs(d){return"Ended"}, +gbXA(){return"Enter password"}, +gbXB(){return"Enter phrase"}, +gbXC(){return"Enter title"}, +gbXK(){return"Active account not found."}, +gbXL(){return"Display name can not be empty"}, +gbXM(){return"Invalid length"}, +gbXN(){return"This email address is already in use."}, +gbXO(){return"Invalid length"}, +gbXP(){return"Incorrect email pattern"}, +gaKm(){return"Error deleting proposal"}, +gbXQ(){return"Something went wrong, please try again later"}, +gbXR(){return"Words do not match"}, +gaKn(){return"Can not publish comment"}, +gaKo(){return"Invalid format"}, +bXS(d){return'The value must be "'+d+'".'}, +gbXT(){return"Please select this field."}, +bXU(d){return"The value must be one of "+d+"."}, +gbXV(){return"Start your url with https://"}, +bXW(d){return"There should be no more than "+d+" "+B.jq(d,null,this.a,null,"item","items",null,"items")+"."}, +bXX(d){return"There should be at least "+d+" "+B.jq(d,null,this.a,null,"item","items",null,"items")+"."}, +gbXY(){return"The values must be unique."}, +bXZ(d,e){return"There should be between "+d+" and "+e+" "+B.jq(e,null,this.a,null,"item","items",null,"items")+"."}, +gbY_(){return"Please fill this field."}, +aKp(d){return"The value should be no bigger than "+d+"."}, +aKq(d){return"The value should be at least "+d+"."}, +aKr(d,e){return"The value should be between "+d+" and "+e}, +aKs(d){return"The value should be a multiple of "+d}, +gbY0(){return"The value does not match the valid pattern."}, +gbY1(){return"The text cannot be empty."}, +aKt(d){var w="characters" +return"The text should be no longer than "+d+" "+B.jq(d,null,this.a,null,"character",w,null,w)+"."}, +aKu(d){var w="characters" +return"The text should be at least "+d+" "+B.jq(d,null,this.a,null,"character",w,null,w)+"."}, +bY2(d){var w="characters" +return"The text should be "+d+" "+B.jq(d,null,this.a,null,"character",w,null,w)+"."}, +bY3(d,e){var w="characters" +return"The text should be between "+d+" and "+e+" "+B.jq(e,null,this.a,null,"character",w,null,w)+"."}, +gbY4(){return"Invalid input. Could not parse."}, +gbY5(){return"Wallet account change"}, +gbY6(){return"Wallet authorisation internal error"}, +gbY7(){return"Wallet authorisation invalid request"}, +gbY8(){return"Wallet authorisation failed"}, +gbY9(){return"Eternl Wallet"}, +gbYn(){return"Exit"}, +gbYr(){return"Show less"}, +gbYs(){return"Show more"}, +gbYw(){return"Explore Categories"}, +gaT1(){return"Export"}, +gaKA(){return"Export"}, +gbYx(){return"Export Key"}, +gbYy(){return"Export proposals individually"}, +gbYD(){return"You did not authorise the Catalyst App in your wallet, follow specific wallet instructions."}, +gbYE(){return"No/Failed Authorisation"}, +gbYF(){return"There was an error submitting your votes. Please check your connection and try casting again."}, +gbYG(){return"Error: Votes Not Submitted"}, +gbYH(){return"Feedback on Proposals"}, +gaKP(){return"Final"}, +gbZ4(){return"Finish account creation"}, +gbZa(){return"Finishes:"}, +gR2(){return"Forget proposal"}, +gbZp(){return"I acknowledge that this action cannot be undone."}, +gbZq(){return"You will no longer be able to view, edit, or publish this proposal"}, +gbZr(){return"This proposal will not be eligible to win funding, even if it has been previously published."}, +gbZs(){return"You are about to remove this proposal from the app."}, +gbZt(){return"Your proposal will not be visible to other users."}, +gbZu(){return"Forgot password"}, +gyP(){return"Format"}, +aih(d){return"Fund"+d+" Category"}, +bZJ(d){return"F"+d+" Voting: Make sure you're ready."}, +bZK(d){return"Fund"+d}, +gbZL(){return"Funded project space"}, +gaLi(){return"Funds Available"}, +gbZM(){return"Funds available for this category"}, +gJG(){return"Funds requested"}, +gaUs(){return"Get ready to vote"}, +gant(){return"Get Started"}, +gaUP(){return"Go My proposals"}, +gaUQ(){return"Good password strength"}, +gaUR(){return"Good to know"}, +gaUT(){return"Guest"}, +ganD(){return"Guidance"}, +gaiO(){return"Heads up"}, +gc03(){return"Heads Up"}, +gc05(){return"Create, fund and deliver the future of Cardano."}, +gc08(){return"Hide all issues"}, +gc0g(){return"h"}, +c0h(d){var w=null +return B.jq(d,w,this.a,w,"Hour","Hours",w,w)}, +gc0k(){return"Here's how it works"}, +gc0l(){return"Innovate together"}, +gc0m(){return"Got an Idea? Create an impactful proposal and collaborate with the community to develop and refine it."}, +gc0n(){return"Stay up to date"}, +gc0o(){return"Receive regular updates on all the funded ideas, so you can follow along and see how things are progressing."}, +gc0p(){return"Vote for your favorite ideas"}, +gc0q(){return"Use a voting app to choose ideas with impact worth funding. Get rewarded in ada for taking part."}, +gc0s(){return"Idea Journey"}, +c0t(d){return"#### Ideas come to life in Catalyst through the key stages below. For the full timeline, deadlines and latest updates, visit the [fund timeline]("+d+") Gitbook page."}, +gc0w(){return"IMPORTANT: The Catalyst app won\u2019t be accessible after the deadline."}, +gc0x(){return"Import Catalyst Key"}, +gc0y(){return"Import Proposal"}, +aM_(d){return"In "+d}, +gc0E(){return"In progress"}, +gc0F(){return"In Vote List"}, +gc0N(){return"The Catalyst keychain that you entered or uploaded is incorrect, please try again."}, +gc0O(){return"CATALYST KEY INCORRECT"}, +gc0P(){return"Try again"}, +gc1b(){return"Bring your proposal to life with visuals"}, +gc1c(){return"Image URL"}, +gc1d(){return"Insert Image"}, +gc1e(){return"Please provide a valid image URL to proceed"}, +gc1f(){return"Paste link to image here"}, +gc1g(){return"Insert New Image"}, +gc1h(){return"Image failed to load"}, +gc1i(){return"Install a Cardano wallet"}, +gc1v(){return"Invalid"}, +gc27(){return"Join Newsletter"}, +gv8(d){return"key"}, +gc29(){return"Your Catalyst Keychain is removed successfully from this device.\n\nWe reverted this device to Catalyst first use."}, +gc2a(){return"Catalyst keychain removed"}, +gc2b(){return"knowledge base"}, +gc2c(){return"Enter your unlock password to confirm"}, +gc2d(){return"Lace Wallet"}, +c2e(d){return"Last edit "+d}, +gc2h(){return"Latest"}, +gRI(){return"Learn More"}, +gc2s(){return"Link copied to clipboard"}, +gc2v(){return"Linked Wallet"}, +gc2z(d){return"Live"}, +gajE(){return"Local"}, +gc2W(d){return"Lock"}, +gc2X(){return"Lock account"}, +gc2Z(){return"Catalyst is now in guest/locked mode."}, +gc3_(){return"Catalyst locked"}, +gc37(){return"Mainnet"}, +gc3l(){return"Max budget request"}, +gc3p(){return"Maximum Ask"}, +c3w(d){return"Important: Read the [milestone guidelines]("+d+") before proceeding."}, +gc3x(){return"Min budget request"}, +gc3D(){return"Minimum Ask"}, +gaNz(){return"m"}, +c3F(d){var w=null +return B.jq(d,w,this.a,w,"Minute","Minutes",w,w)}, +gc3K(){return"For the best experience, head over to a desktop to get started. But don't worry - mobile is coming.\n\nStay tuned for more updates, or visit our Gitbook to learn more.\n\nSee you on the other side!"}, +gc3L(){return"We're not quite ready for you on mobile just yet\u2026"}, +gc3M(){return"Easy there, trailblazer"}, +gc3R(){return"Most Recent"}, +gc3T(){return"Move your Final proposals into the Review stage."}, +gaND(){return"My account"}, +gaNE(){return"My Opportunities"}, +gajW(){return"My Proposals"}, +gc3W(){return"My Roles"}, +gc3Z(){return"Name of the wallet"}, +gc43(){return"Nami Wallet"}, +c44(d,e,f){return"Iteration "+d+" \xb7 Not published - "+e+" - "+f}, +gc46(){return"New updates - see what's new!"}, +gdw(d){return"Next"}, +gc49(){return"Nickname"}, +gKh(){return"No"}, +gc4a(){return"We can't find an active campaign."}, +gc4b(){return"No date & time selected"}, +gc4c(){return"No Draft proposals found"}, +gc4d(){return"No Final proposals found"}, +gc4e(){return"There is no guidance for this section."}, +gc4f(){return"No Local proposals found"}, +aNR(d){return"All ("+d+")"}, +aNS(d){var w="comments",v=this.a +return C.do5(!1,A.ad9,v).dW(d)+" "+B.jq(d,null,v,null,"comment",w,null,w)}, +c4g(d){return"Draft proposals \xb7 "+d}, +aNT(d){return"Favorites \xb7 "+d}, +c4h(d){return"Final proposals \xb7 "+d}, +aNU(d){return"My proposals \xb7 "+d}, +c4i(d){return"Voted On \xb7 "+d}, +gc4j(){return"You have no proposals to publish, start a new proposal, or create a new iteration of an existing proposal to be able to publish."}, +gc4k(){return"No published proposals."}, +gc4l(){return"No Tag Selected"}, +gc4m(){return"No URL added"}, +gc4n(){return"No wallet found."}, +gc4o(){return"You don\u2019t have a (compatible) wallet installed, please select one of the Cardano wallets on the right, and follow installation instructions."}, +gc4p(){return"No wallet installed"}, +gc4r(){return"Normal password strength"}, +gc4t(){return"Not active campaign"}, +gc4u(){return"**Read only**"}, +ga3b(){return"N/A"}, +gc4v(){return"Go to Catalyst Home"}, +gc4w(){return"The requested data could not be found. Try different search."}, +gc4x(){return"Looks like we can't find the page you're looking for.\nMaybe a good time for a coffee break?"}, +gc4y(){return"We can't find that page."}, +gaNW(){return"Not published"}, +gc4z(){return"**Not published**\n\nThis draft is saved locally and only you can see it. You can edit it anytime on this device. It\u2019s not eligible for feedback or funding yet. Share for feedback or submit for review to publish it to the network."}, +gc4A(){return"Not published proposals"}, +gc4B(){return"Not verified"}, +gc4C(){return"Notice"}, +aNY(d){return"Iteration "+d}, +ge0(){return"Ok"}, +gak0(){return"Okay"}, +gwW(){return"Ongoing"}, +ge1(d){return"Open"}, +gc5N(){return"Open in editor"}, +gc5R(){return"Alphabetical"}, +gc5S(){return"Budget: lowest first"}, +gc5T(){return"Budget: highest first"}, +gc5U(){return"Newest to oldest"}, +c60(d,e,f){var w="proposals" +return""+d+"-"+e+" of "+f+" "+B.jq(f,null,this.a,null,"proposal",w,null,w)}, +gc6o(){return"Passwords do not match, please correct"}, +gaOx(){return"Password"}, +gc6v(){return"Email will be update after successful verification"}, +gc6w(){return"Please verify your new email address."}, +gc6x(){return"Pending email change"}, +gc6B(){return"Start writing your text..."}, +gc6O(){return"Potential reasons"}, +gc6P(){return"Curious? Learn more about the new Catalyst App\nand prepare yourself for success."}, +gc6V(){return"PreProd"}, +giB(){return"Previous"}, +ganE(){return"Private"}, +gaOQ(){return"Profile & Keychain"}, +gc76(){return"Project Catalyst is the world's largest decentralized innovation engine for solving real-world challenges.\n\nWe're putting the community at the heart of Cardano's future development.\n\nAre you ready for the Challenge?"}, +gSv(){return"Proposal"}, +gc77(){return"Back to Proposals"}, +gc78(){return"Delete Proposal"}, +gc79(){return"Are you sure you want to delete this proposal?\nThis action cannot be undone."}, +gc7a(){return"Delete Proposal?"}, +gaOU(){return"Not Answered"}, +gaOV(){return"We couldn't publish your proposal. Please try again in 5-10 minutes, as it may take some time for your information to update.\n\nIf the issue persists, feel free to contact support."}, +gc7b(){return"Unable to Publish Proposal"}, +gc7c(){return"Delete Proposal"}, +gc7d(){return"Export Proposal"}, +gc7e(){return"Share draft for feedback"}, +c7f(d){return"**Share new draft** \xb7 Draft Iteration "+d}, +gc7g(){return"Submit as final proposal for review / voting"}, +c7h(d){return"**Submit For Review** \xb7 Iteration "+d+" as Final"}, +c7i(d){return"Iteration "+d+" \xb7 Not published"}, +gakS(){return"Unnamed proposal"}, +gaOW(){return"We couldn't submit your proposal. Please try again in 5-10 minutes, as it may take some time for your information to update.\n\nIf the issue persists, feel free to contact support."}, +gc7j(){return"Unable to Submit Proposal"}, +gc7k(){return"Issues cleared"}, +gc7l(){return"Please review the sections highlighted in red and correct them to continue."}, +gc7m(){return"Please proceed sharing your draft for feedback."}, +gc7n(){return"In the next step, you'll see the issues that need to be resolved before publishing your draft."}, +aOX(d){return"To share new draft, resolve "+d+" form "+B.jq(d,null,this.a,null,"issue","issues",null,"issues")+"."}, +gc7o(){return"Please proceed sharing your final for review."}, +gc7p(){return"In the next step, you'll see the issues that need to be resolved before publishing your final."}, +aOY(d){return"To submit For Review, resolve "+d+" "+B.jq(d,null,this.a,null,"issue","issues",null,"issues")+"."}, +gc7q(){return"Exit form issue mode?"}, +gc7r(){return"Exit Issue Mode"}, +gc7s(){return"Make sure it's a correct Catalyst proposal file."}, +gc7t(){return"Import Proposal"}, +c7u(d,e,f,g){return"Iteration "+d+" - "+e+" - "+f+" - "+g}, +gc7v(){return"Proposal Options"}, +gc7w(){return"Check this out!\ud83c\udf0d #MustSee #SharingIsCaring"}, +c7x(d,e){return d+" - Iteration "+e}, +gc7y(){return"Ready"}, +gc7z(){return"We\u2019re moving into the Community Review stage!\nWant to stay involved and earn rewards? Check out our knowledge base."}, +c7A(d){return"Proposal submission is closed for Fund"+d+"."}, +c7B(d){return"Proposal submission opens at "+d}, +c7C(d){return"The proposal submission window closes on "+d}, +gc7D(){return"A strong proposal title can really set the tone and capture attention. "}, +ga4j(){return"Funding Requested"}, +gc7E(){return"Overview"}, +gc7F(){return"Metadata"}, +gc7G(){return"Project Delivery"}, +gc7H(){return"Project Duration"}, +gakT(){return"Proposals"}, +c7I(d){var w=null +return"Latest updates ("+d+B.jq(d,w,this.a,w,"hr","hrs",w,w)+")"}, +gc7J(){return"You need to put one of your final proposals to draft, before you can publish another final."}, +gc7K(){return"Want to publish another final?"}, +gc7L(){return"You can continue editing local/draft proposals."}, +c7M(d){return"You can't publish more than "+d+" "+B.jq(d,null,this.a,null,"final","finals",null,"finals")+"."}, +gc7N(){return"You can share for feedback."}, +gc7O(){return"Publish Final is disabled."}, +c7P(d){var w="proposals" +return"You already published "+d+" final "+B.jq(d,null,this.a,null,"proposal",w,null,w)+"!"}, +gc7Q(){return"Proposals submission close:"}, +c7R(d){var w=null +return""+d+" "+B.jq(d,w,this.a,w,"Proposal","Proposals",w,w)+" Submitted"}, +gc7S(){return"Proposer"}, +gc7T(){return"The Main Proposers are the Innovators in Project Catalyst, they are the shapers of the future."}, +gc7U(){return"Comment functionality"}, +gc7V(){return"Invite Team Members"}, +gc7W(){return"Rights to Submit to Fund"}, +gc7X(){return"Write/edit functionality"}, +gc7Y(){return"Main proposer"}, +gc7Z(){return"Provisional"}, +gc80(){return"Publish"}, +gc83(){return"Published iterations are always public."}, +gc84(){return"Contributors can comment on the latest iteration."}, +gc85(){return"Drafts are not submitted for voting. Use 'Submit for review' to enter the community review stage."}, +gc86(){return"Published proposal drafts are public to other users, including all previous iterations."}, +gc87(){return"Publish New Iteration?"}, +gc8b(){return"I confirm that I am ready to submit my finalized proposal into the review stage."}, +gc8c(){return"Published iterations are always public."}, +gc8d(){return"Contributors can only comment on the latest iteration."}, +gc8e(){return"Proposals are entered into the review stage unless a later iteration is published before the deadline."}, +gc8f(){return"Submitted final proposals are entered into the community review stage and are eligible for funding."}, +gc8g(){return"Submit For Review?"}, +aOZ(d,e){return"Published on "+d+" at "+e}, +gc8h(){return"Published Proposals"}, +gc8i(){return"Publishing history"}, +gc8S(){return"Re-send"}, +gc8Z(){return"Read category brief"}, +gc94(){return"Read More"}, +gaP9(){return"Restore Catalyst keychain"}, +gc9c(){return"Restore a different keychain"}, +gc9d(){return"Keychain found! \nPlease unlock your device."}, +gc9e(){return"How do you want Restore your Catalyst Keychain?"}, +gc9f(){return"Not to worry, in the next step you can choose the recovery option that applies to you for this device!"}, +gc9g(){return"Restore your Catalyst Keychain"}, +gc9h(){return"No Catalyst Keychain found\non this device."}, +gc9i(){return"Restore/Upload with 12-word seed phrase"}, +gc9j(){return"Set unlock password for this device"}, +gc9k(){return"Keychain recovered successfully!"}, +gc9l(){return"Catalyst account recovery"}, +gc9m(){return"Please continue your Catalyst Keychain restoration, if you cancel all input will be lost."}, +gc9n(){return"Continue recovery process"}, +gc9o(){return"12 word keychain restoration incomplete"}, +gc9p(){return"Enter each word of your Catalyst seed phrase in the right order to bring your Catalyst account to this device."}, +gc9q(){return"Restore your Catalyst Keychain with \nyour 12-word Catalyst seed phrase"}, +gc9r(){return"Check my account"}, +gc9s(){return"Start exploring Spaces"}, +gc9t(){return"You have successfully restored your Catalyst Keychain, and unlocked Catalyst App on this device."}, +gc9u(){return"Congratulations your Catalyst \nKeychain is restored!"}, +gc9v(){return"In this next step, you'll set your Unlock Password for this device. It's like a shortcut for proving ownership of your Keychain.\n\nWhenever you recover your account for the first time on a new device, you'll need to use your Catalyst seed phrase to get started. Every time after that, you can use your Unlock Password to quickly regain access."}, +gc9w(){return y.d}, +c9G(d){return"Register to Vote in Fund"+d}, +gc9K(){return"Registered Roles"}, +gc9L(){return"Account not found. Make sure seed phrase is correct."}, +c9M(d){return"UTxO asset name too long. \n"+d}, +gc9N(){return"Review my account"}, +gc9O(){return"Start exploring Spaces"}, +gc9P(){return"You created a Catalyst Keychain, backed up its seed phrase and set an unlock password."}, +gc9Q(){return"Catalyst Keychain created"}, +gc9R(){return"role registration"}, +gc9S(){return"You linked your Cardano wallet and selected Catalyst roles via a signed transaction."}, +gc9T(){return"Catalyst roles selected"}, +gc9U(){return"Catalyst account creation completed!"}, +gc9V(){return"Completed!"}, +gc9W(){return"Summary"}, +gc9X(){return"Catalyst account setup"}, +c9Y(d){return"You selected your "+d+" wallet as primary wallet for your voting power."}, +c9Z(d){return"Cardano "+d+" wallet selected"}, +gca_(){return"Catalyst Keychain created"}, +gca0(){return"Profile created"}, +gca1(){return"Leaving before creating your keychain means account creation is incomplete. \n\nYou cannot login without \ncompleting your key chain."}, +gca2(){return"Continue keychain creation"}, +gca3(){return"Account creation incomplete!"}, +gca4(){return"Transaction cannot proceed due to low wallet balance."}, +gca5(){return"Keychain was not found or locked"}, +gca6(){return"Link Cardano Wallet & Roles"}, +gca7(){return"Missing required signers from outputs"}, +aPh(d){return"It seems like you're trying to use\xa0a wallet on the wrong network.\nPlease switch to "+d+" to continue."}, +gca8(){return"Keychain was not found while recovering."}, +gca9(){return"Could not save progress"}, +gcaa(){return"Keychain not found"}, +gcab(){return"Keychain not found"}, +gcac(){return"Keychain not found"}, +gcad(){return"Seed phrase was not found. Make sure correct words are correct."}, +gcae(){return"Transaction failed"}, +caf(d,e){return"The creation of a Catalyst account costs, a transaction fee of (0,21 "+e+"), be sure your wallet has a minimum of "+d+"."}, +gcag(){return"Password was not found. Make sure valid password was created."}, +gcah(){return"Wallet not found"}, +gcat(){return"Remove from Vote List"}, +gcau(){return"Remove Keychain"}, +gcav(){return"Are you sure you wants to delete your Catalyst Keychain from this device? Make sure you have a working Catalyst 12-word seed-phrase!\nYour Catalyst account will be removed, this action cannot be undone!"}, +gcaw(){return"Remove Keychain?"}, +gcaz(){return"You remove this proposal from review/voting stage."}, +caT(d,e){return"Requested amount should be between "+d+" and "+e}, +caU(d){return"Requested amount should be at most "+d}, +caV(d){return"Requested amount should be at least "+d}, +gcaW(d){return"Required"}, +ga4V(d){return"Reset"}, +gcb1(){return"Provided data is not unique."}, +gcb5(){return"You need to resubmit this proposal as final, to be eligible for funding, so make sure to Submit to review stage (Final) after editing."}, +gcb7(){return"Resume"}, +galJ(){return"Retry"}, +gcba(){return"Reviewers registration:"}, +gcbb(){return"Review registration transaction"}, +gcbc(){return"Community review:"}, +gcbd(){return"The editor supports images from web URLs, including both traditional web servers and decentralized storage solutions. For more information, visit our"}, +gaUU(){return"Safari is not supported, please use Chrome or Firefox."}, +ganI(){return"Save"}, +gaV5(){return"Save Changes"}, +ganT(){return"Search Proposals"}, +gaVX(){return"Search Result"}, +gaVZ(){return"s"}, +aW0(d){var w=null +return B.jq(d,w,this.a,w,"Second","Seconds",w,w)}, +gaW1(){return"Section has input errors that need to be cleared before publishing."}, +ganW(){return"See all supported wallets"}, +aW2(d){return"Slot "+d}, +gaW6(){return"Select a section"}, +gaW8(){return"Select Category"}, +gaW9(){return"Do keep in mind that you can\u2019t change category\nafter starting your proposal."}, +gaWa(){return"Select a category to check if this category is for you."}, +gaXv(){return"Please wait while we are setting things up for you"}, +gaXw(){return"Settings"}, +gaXy(){return"Setup Base Proposal Template"}, +gaXz(){return"Setup Base Questions"}, +gaXA(){return"Setup Campaign Details"}, +gaXB(){return"Setup Campaign Stages"}, +gaXC(){return"You are setting date and times using this Timezone:"}, +gaXD(){return"Setup Catalyst roles"}, +gaXE(){return"Setup Categories"}, +gaXG(d){return"Share"}, +aXK(d){return"Share a link directly to this "+d}, +UL(d,e){return"Share this "+d+" link on "+e}, +gaXM(){return"Share new draft"}, +aXN(d){return"Share on "+d}, +gaXO(){return"Something went wrong while sharing proposal. Please try again later."}, +aXQ(d){return"Share "+d}, +gaXR(){return"Shared for public feedback / In progress"}, +gaXS(){return"**In progress**\n\nYour latest published version is visible to everyone, but edits you make stay private until you share again. Create a new local iteration to keep working. It\u2019s eligible for feedback but not for funding yet."}, +gaon(){return"Show All"}, +gaY1(){return"Show all issues"}, +gaYm(){return"Select the most relevant tag"}, +gaoC(){return"Error"}, +gaYr(){return"This is an error message!"}, +gaYs(){return"Info"}, +gaYt(){return"This is an info message!"}, +gaYu(){return"Success"}, +gaYv(){return"This is a success message!"}, +gaYw(){return"Warning"}, +gaYx(){return"This is a warning message!"}, +gul(){return"Something went wrong."}, +gaYD(){return"Discovery space"}, +gaYE(){return"Funded project space"}, +gaYG(){return"Treasury space"}, +gaoE(){return"Voting space"}, +gaYH(){return"Workspace"}, +gaYI(){return"Spaces"}, +gaZ2(){return"Start & End Dates"}, +gaZ4(){return"Start"}, +gaZb(){return"Start Proposal"}, +gaZq(){return"Starts:"}, +gbD(d){return"Status"}, +gaZs(){return"Stay Involved"}, +gaZt(){return"Get Ready! This is your chance to have a say in where the funds go. To make sure you're ready to participate.\n\nRemember, by voting, you're not only influencing the development of Cardano but also helping to build a community that reflects the values you care about."}, +gaZu(){return"Your voice has power. Use it to guide others and help improve the Cardano community.\n\nAs a reviewer, you\u2019ll make a real impact by sharing thoughtful feedback and influencing decisions.\n"}, +gUY(){return"Edit"}, +gaZx(){return"Before the Catalyst app closes, make sure to:"}, +gaZA(){return"Store in safe location"}, +gaZE(){return"Submit"}, +gaZH(){return"Unable to add Comment"}, +gaZI(){return"Submit"}, +gaZJ(){return"Submit for review"}, +gaZL(){return"Submit support request"}, +aZO(d,e){return"Submitted for Review / Locked final ("+d+"/"+e+")"}, +gaZP(){return"**Locked final**\n\nYour proposal is locked and visible to everyone. To make changes, you\u2019ll need to unlock it and start a new draft. It\u2019s eligible for both feedback and funding, so stay tuned for next steps."}, +gaZS(){return"Proposal deleted successfully"}, +gaZT(){return"Your local proposal has been deleted"}, +gaZU(){return"Proposal forgotten"}, +gaZV(){return"Your local proposal has been forgotten. No one can see it anymore."}, +gaZW(){return"Thank you for voting on Catalyst proposals. You can review or update your submitted votes at any time during the voting phase."}, +gaZX(){return"Your Votes Have Been Submitted"}, +gb5L(){return"We're aware of an issue and are working on a fix. Thanks for your patience! {readMoreLink}"}, +gb5M(){return"Heads Up"}, +gcbU(){return"Theme"}, +gcbV(){return"Dark"}, +gcbW(){return"Light"}, +gcc0(){return"Timezone"}, +gcc1(){return"Tip"}, +aQ8(d){return"Copy your Catalyst ID before you go to the [Community Review module.]("+d+")"}, +gkE(d){return"Title"}, +gcci(){return"to learn more."}, +gam5(d){return"Total"}, +gccw(){return"Total Voting Power"}, +gaQv(){return"Segments"}, +gccH(){return"Create Campaign"}, +gccN(){return"Please try another search."}, +gccO(){return"Turn Your Opinions into Impact."}, +gaQy(d){return"Unlock"}, +gccT(){return"Unlock & Edit"}, +gccU(){return"Please enter your device specific unlock password\nto unlock Catalyst App."}, +gccV(){return"Unlock Catalyst"}, +gccW(){return"Enter your Unlock password"}, +gaQz(){return"Password is incorrect, try again."}, +gccX(){return"Welcome back!"}, +gccY(){return"Unlock & Edit ?"}, +gcd_(){return"You can now fully use the application."}, +gcd0(){return"Catalyst unlocked!"}, +gcdn(){return"Updated"}, +gcdo(d){return"Upload"}, +gcdp(){return"Do you want to cancel manual input, and switch to Catalyst key upload?"}, +gcdq(){return"Resume manual inputs"}, +gcdr(){return"SWITCH TO FILE UPLOAD"}, +gcds(){return"Yes, switch to Catalyst key upload"}, +cdt(d){return"Drop your "+d+" here or "}, +gcdv(){return"Make sure it's a correct Catalyst keychain file."}, +gcdw(){return"Upload Catalyst Keychain"}, +gcdx(){return"Upload in progress"}, +gcdD(){return"User Authentication State"}, +gcdF(){return"All set! You can now join the conversation."}, +gcdG(){return"Display Name Saved"}, +cdK(d){var w="Milestones" +return""+d+" "+B.jq(d,null,this.a,null,"Milestone",w,null,w)}, +a5J(d){return""+d+" "+B.jq(d,null,this.a,null,"Month","Months",null,"Months")}, +gcdM(){return"We sent you an email with a Catalyst verification link."}, +gcdN(){return"Please verify your new email address."}, +gcdO(){return"Verify your email address"}, +gcdP(){return"Verification pending"}, +gcdQ(){return"Verified"}, +gcdR(){return"You need to verify your email address to publish your proposal."}, +gcdS(){return"A verified email is required to publish your proposal"}, +gcdX(d){return"View"}, +gaQX(){return"View All Proposals"}, +gcdY(){return"View Cast Votes"}, +gcdZ(){return"View comments"}, +gce_(){return"View Latest"}, +gce0(){return"View proposal comments"}, +gce1(){return"Open My proposals in the Discovery Space"}, +gamu(){return"View proposals"}, +gce2(){return"You are viewing an older iteration of this proposal"}, +gce3(){return"You are viewing an older version of this document while voting. Please view the latest version if you want to vote."}, +gce8(){return"Visit Catalyst wallet documentation"}, +gce9(){return"Visit Gitbook"}, +gceh(){return"Visitor"}, +gcei(){return"Abstain"}, +gcej(){return"Vote List"}, +gaRm(){return"Vote List"}, +gcel(){return"You can submit more votes or update your choices at any time during the voting phase."}, +gcem(){return"Yes"}, +gcen(){return"Abstained"}, +gceo(){return"Voted Yes"}, +gceq(){return"Voting ended"}, +aRn(d){return"Ends in "+d}, +gces(){return"Voting is closed!"}, +gcet(){return"Voting is open!"}, +gceu(){return"Cast Votes"}, +aRo(d){return"Last voted: "+d}, +gcev(){return"No votes added."}, +gcew(){return"View proposal details pages to cast votes"}, +gcex(){return"Not yet voted"}, +gcey(){return"Pending votes not yet cast"}, +gcez(){return"Voting Overview"}, +gaRp(){return"Voting Phase"}, +gceC(){return"Voting Power"}, +gceD(){return"Voting Registration"}, +gceE(){return"Voting registration:"}, +gceF(){return"Voting starts"}, +ceG(d){return"in "+d}, +gceH(){return"Voting Status"}, +gceJ(){return"Voting:"}, +gceM(){return"Wallet address"}, +gaRq(){return"Wallet balance"}, +gceN(){return"Wallet detection summary"}, +gceO(){return"Leaving before linking wallet means account creation is incomplete. \n\nYou cannot login without \ncompleting your account setup."}, +gceP(){return"Continue to link wallet"}, +gceQ(){return"Link wallet incomplete!"}, +gamJ(){return"Link keys to your Catalyst Keychain"}, +gceR(){return"You're almost there! This is the final and most important step in your account setup.\n\nWe're going to link a Cardano Wallet to your Catalyst Keychain, so you can start collecting Role Keys.\n\nWe'll start with your Contributor Key by default. You can decide to add a Proposer Key if you want."}, +gceS(){return"Link Cardano Wallet & Catalyst Roles \nto you Catalyst Keychain."}, +gceT(){return"In Catalyst you can take on different roles, learn more below and choose your additional roles now."}, +gceU(){return"How do you want to participate in Catalyst?"}, +gceV(){return"You would like to register "}, +ceW(d){var w=this.a +return C.do5(!1,A.ad9,w).dW(d)+" active "+B.jq(d,null,w,null,"role","roles",null,"roles")}, +gceX(){return" in Catalyst."}, +gceY(){return"Is this your correct Catalyst role setup?"}, +gceZ(){return"Select your Catalyst roles"}, +gcf_(){return y.a}, +gcf0(){return"Select the Cardano wallet to link\nto your Catalyst Keychain."}, +gcf1(){return"Account completion for Catalyst"}, +gcf2(){return"Back to Wallet Selection"}, +gaRr(){return"Change role setup"}, +cf3(d){return"1 "+d+" wallet"}, +cf4(d){return"1 "+d+" registration"}, +gcf5(){return"Sign transaction with wallet"}, +gcf6(){return"Sign your Catalyst roles to the\nCardano mainnet"}, +gcf7(){return y.a}, +gcf8(){return"Let's make sure everything looks right."}, +cf9(d){return d+" connected successfully!"}, +cfa(d){return"Please ensure your Cardano wallet has at least "+d+" before continuing."}, +gcfb(){return"To calculate voting power, Catalyst reads your wallet balance from the blockchain. "}, +gcfc(){return"Cardano wallet detection"}, +gcfd(){return"Link your Cardano wallet"}, +gcfe(){return"Want to back up your proposals?"}, +gLa(){return"Warning"}, +gcfn(){return"Weak password strength"}, +gcft(){return"Segments"}, +gcfL(){return"Please launch the browser with your wallets extension and revisit Catalyst."}, +gcfM(){return"Wrong Browser"}, +cfO(d){return""+d+" characters minimum length"}, +cfP(d){return""+d+" "+B.jq(d,null,this.a,null,"Vote","Votes",null,"Votes")}, +gLi(){return"Yes"}, +gcfR(){return"You are setting your final proposal back to draft."}, +gcfS(){return"Your next step"}} +C.aCp.prototype={} +C.Ch.prototype={} +C.b7J.prototype={ +ga1e(d){var w=this.d +w===$&&B.c() +return w.ga1e(w)}, +Gv(d,e){var w,v,u,t,s,r,q,p,o,n,m=this,l=Math.abs(d) +if(m.c==null){w=m.d +w===$&&B.c() +return w.Gv(d,e)}v=e.ay +if(e.db){u=B.ZD(e.N_(l)) +w=e.cy +if(w!=null)v=Math.max(0,w-u)}t=Math.pow(10,v) +s=D.h.au(l*t)/t +if(e.db&&!e.cx){r=D.f.j(e.N_(s*t)) +while(!0){if(!(v>0&&D.c.de(r,"0")))break;--v +r=D.c.Y(r,0,r.length-1)}}if(d>=0&&v===0){q=m.b.i(0,D.f.j(e.N_(s))) +if(q!=null)return q.Gv(d,e)}B.dt7(s,v) +p=m.c.$0() +w=m.d +w===$&&B.c() +switch(p.a){case 0:o=m.b.i(0,"zero") +if(o==null)o=m.d +break +case 1:o=m.b.i(0,"one") +if(o==null)o=m.d +break +case 2:w=m.b +n=w.i(0,"two") +o=n==null?w.i(0,"few"):n +if(o==null)o=m.d +break +case 3:o=m.b.i(0,"few") +if(o==null)o=m.d +break +case 4:o=m.b.i(0,"many") +if(o==null)o=m.d +break +default:o=w}return o.Gv(d,e)}} +C.b7I.prototype={ +Gv(d,e){return d<0?this.b:this.a}, +ga1e(d){return this.a.b}} +C.aoM.prototype={ +Gv(d,e){return this}, +ga1e(d){return this.b}} +C.cB9.prototype={ +I(){return"_CompactFormatType."+this.b}} +C.cBa.prototype={ +ga32(){var w=this.p2 +if(w!=null){w=w.a +w=!(w==null||w==="0")}else w=!1 +return w?0:B.ZB.prototype.ga32.call(this)}, +ga47(){var w=this.p2,v=w.a +return v==null||v==="0"?this.b:w.c}, +ga3a(){var w=this.p2,v=w.a +return v==null||v==="0"?this.a:w.d}, +gakL(){var w=this.p2,v=w.a +return v==null||v==="0"?this.d:w.e}, +gajX(){var w=this.p2,v=w.a +return v==null||v==="0"?this.c:w.f}, +dW(d){var w,v,u,t=this,s=t.p2=t.bFd(d),r=s.a,q=C.do6(d,r==null||r==="0"?1:s.b) +if(s.r){w=D.f.gib(d)?t.ga3a():t.ga47() +v=D.f.gib(d)?t.gajX():t.gakL() +u=w+B.w(r)+v}else u=t.b0y(q) +t.p2=null +return u}, +aED(){return!0}, +bFd(d){var w,v,u,t,s={} +if(Math.abs(d)<10)return $.dbE() +s.a=d +s.b=B.ZD(d) +s.c=1 +w=new C.cBd(s,this) +w.$0() +for(v=this.ok,v=new B.cP(v,B.y(v).h("cP<1,2>")).gak(0),u=null;v.v();){t=v.d +if(t.a+1>s.b)break +u=t.b +s.c=u.ga1e(u) +w.$0()}v=u==null?null:u.Gv(C.do6(d,s.c),this) +return v==null?$.dbE():v}} +var z=a.updateTypes(["ay(f,f)","f(AV)"]) +C.cBc.prototype={ +$2(d,e){var w,v=this,u=J.eI(e),t=J.aK(u.gd9(e))===1&&J.u(J.CI(u.gd9(e)),"other"),s=v.b,r=v.c +if(t)w=C.do8(J.CI(u.gen(e)),d,s,r) +else{u=u.jU(e,new C.cBb(d,s,r),x.w,x.B) +t=v.a.a +w=new C.b7J(u) +w.c=$.d3q().i(0,t) +u=u.i(0,"other") +u.toString +w.d=u}v.d.m(0,d,w)}, +$S:2263} +C.cBb.prototype={ +$2(d,e){return new B.ay(d,C.do8(e,this.a,this.b,this.c),x.q)}, +$S:z+0} +C.cBd.prototype={ +$0(){var w,v,u,t,s,r,q=this.b,p=q.at +if(q.db){w=this.a +v=B.ZD(w.c) +u=q.CW +t=u==null +q=t?q.cy:u +if(q==null)q=0 +p=q-w.b+v-1 +if(t)p=Math.max(0,p)}s=Math.pow(10,p) +q=this.a +w=q.a +u=q.c +r=D.h.au(w*s/u)*u/s +q.a=r +q.b=B.ZD(r)}, +$S:0};(function installTearOffs(){var w=a._static_1 +w(C,"ehc","do7",1)})();(function inheritance(){var w=a.inheritMany,v=a.inherit +w(B.L,[C.wO,C.aCp,C.Ch]) +v(C.T2,C.wO) +w(C.Ch,[C.b7J,C.b7I,C.aoM]) +v(C.cB9,B.apS) +v(C.cBa,B.ZB) +w(B.aai,[C.cBc,C.cBb]) +v(C.cBd,B.aah)})() +B.dp2(b.typeUniverse,JSON.parse('{"T2":{"wO":[]},"b7J":{"Ch":[]},"b7I":{"Ch":[]},"aoM":{"Ch":[]}}')) +var y={d:"Set your Catalyst unlock password for this device",a:"To complete this action, you'll submit a signed transaction to Cardano. There will be an ADA transaction fee."} +var x={p:B.V("q"),o:B.V("b>"),q:B.V("ay"),w:B.V("f"),B:B.V("Ch"),e:B.V("j")};(function constants(){A.e={other:0} +A.BN=new B.q(A.e,["0G"],x.p) +A.aVj=new B.b([9,A.BN],x.o) +A.p9={few:0,many:1,one:2,other:3,two:4,zero:5} +A.bv_=new B.q(A.p9,["0\xa0\u0622\u0644\u0627\u0641","0\xa0\u0623\u0644\u0641","0\xa0\u0623\u0644\u0641","0\xa0\u0623\u0644\u0641","0\xa0\u0623\u0644\u0641","0\xa0\u0623\u0644\u0641"],x.p) +A.bm4=new B.q(A.e,["00\xa0\u0623\u0644\u0641"],x.p) +A.bjP=new B.q(A.e,["0\xa0\u0645\u0644\u064a\u0648\u0646"],x.p) +A.btG=new B.q(A.e,["0\xa0\u0645\u0644\u064a\u0627\u0631"],x.p) +A.bnK=new B.q(A.e,["0\xa0\u062a\u0631\u0644\u064a\u0648\u0646"],x.p) +A.AS=new B.b([3,A.bv_,4,A.bm4,6,A.bjP,9,A.btG,12,A.bnK],x.o) +A.bG8={"1":0,one:1,other:2} +A.bDY=new B.q(A.bG8,["mille","0 millier","0 mille"],x.p) +A.a0N=new B.q(A.e,["00 mille"],x.p) +A.a3={one:0,other:1} +A.a1D=new B.q(A.a3,["0 million","0 millions"],x.p) +A.a1A=new B.q(A.a3,["0 milliard","0 milliards"],x.p) +A.a1B=new B.q(A.a3,["0 billion","0 billions"],x.p) +A.Y4=new B.b([3,A.bDY,4,A.a0N,6,A.a1D,9,A.a1A,12,A.a1B],x.o) +A.dm={few:0,one:1,other:2} +A.bui=new B.q(A.dm,["0\xa0mii\xa0\xa4","0\xa0mie\xa0\xa4","0\xa0mii\xa0\xa4"],x.p) +A.bq4=new B.q(A.e,["00\xa0mii\xa0\xa4"],x.p) +A.uF=new B.q(A.e,["0\xa0mil.\xa0\xa4"],x.p) +A.a0p=new B.q(A.e,["0\xa0mld.\xa0\xa4"],x.p) +A.boi=new B.q(A.e,["0\xa0tril.\xa0\xa4"],x.p) +A.aVk=new B.b([3,A.bui,4,A.bq4,6,A.uF,9,A.a0p,12,A.boi],x.o) +A.brQ=new B.q(A.e,["0 mille"],x.p) +A.aVl=new B.b([3,A.brQ,4,A.a0N,6,A.a1D,9,A.a1A,12,A.a1B],x.o) +A.btC=new B.q(A.e,["0\u1796\u17b6\u1793\u17cb"],x.p) +A.bpY=new B.q(A.e,["00\xa0\u1796\u17b6\u1793\u17cb"],x.p) +A.bre=new B.q(A.e,["0\xa0\u179b\u17b6\u1793"],x.p) +A.bsR=new B.q(A.e,["0\xa0\u1794\u17ca\u17b8\u179b\u17b6\u1793"],x.p) +A.bm8=new B.q(A.e,["0\xa0\u1791\u17d2\u179a\u17b8\u179b\u17b6\u1793"],x.p) +A.aVm=new B.b([3,A.btC,4,A.bpY,6,A.bre,9,A.bsR,12,A.bm8],x.o) +A.bn6=new B.q(A.e,["0 \u09b9\u09be\u099c\u09be\u09b0"],x.p) +A.a0z=new B.q(A.e,["0 \u09b2\u09be\u0996"],x.p) +A.bpD=new B.q(A.e,["0 \u0995\u09cb\u099f\u09bf"],x.p) +A.bti=new B.q(A.e,["0 \u09b2\u09be\u0996 \u0995\u09cb\u099f\u09bf"],x.p) +A.aYW=new B.b([3,A.bn6,5,A.a0z,7,A.bpD,12,A.bti],x.o) +A.btk=new B.q(A.e,["0\xa0\u09b9\u09be\xa4"],x.p) +A.boQ=new B.q(A.e,["0\xa0\u09b2\u09be\xa4"],x.p) +A.bq_=new B.q(A.e,["0\xa0\u0995\u09cb\xa4"],x.p) +A.bp1=new B.q(A.e,["0\xa0\u09b2\u09be.\u0995\u09cb.\xa4"],x.p) +A.aYX=new B.b([3,A.btk,5,A.boQ,7,A.bq_,12,A.bp1],x.o) +A.bC=new B.q(A.e,["0"],x.p) +A.fx=new B.q(A.e,["0\xa0M"],x.p) +A.j1=new B.q(A.e,["0\xa0B"],x.p) +A.Yu=new B.b([3,A.bC,4,A.bC,5,A.bC,6,A.fx,12,A.j1],x.o) +A.p3=new B.q(A.e,["0\xa0M\xa4"],x.p) +A.uP=new B.q(A.e,["0\xa0B\xa4"],x.p) +A.aZ0=new B.b([3,A.bC,4,A.bC,5,A.bC,6,A.p3,12,A.uP],x.o) +A.uI=new B.q(A.e,["0\xa0M\xa0\xa4"],x.p) +A.uJ=new B.q(A.e,["0\xa0B\xa0\xa4"],x.p) +A.aZ1=new B.b([3,A.bC,4,A.bC,5,A.bC,6,A.uI,12,A.uJ],x.o) +A.bsZ=new B.q(A.e,["0\xa0\u0438\u043b\u0458."],x.p) +A.a0o=new B.q(A.e,["0\xa0\u043c\u0438\u043b."],x.p) +A.brT=new B.q(A.e,["000\xa0\u041c"],x.p) +A.bpp=new B.q(A.e,["0\xa0\u043c\u0438\u043b\u0458."],x.p) +A.bx2=new B.q(A.a3,["000\xa0\u043c\u0458.","000\xa0\u043c\u0438."],x.p) +A.a0v=new B.q(A.e,["0\xa0\u0431\u0438\u043b."],x.p) +A.aZ8=new B.b([3,A.bsZ,6,A.a0o,8,A.brT,9,A.bpp,11,A.bx2,12,A.a0v],x.o) +A.a0T=new B.q(A.e,["0K\xa0\xa4"],x.p) +A.a0i=new B.q(A.e,["0M\xa0\xa4"],x.p) +A.a0Q=new B.q(A.e,["0G\xa0\xa4"],x.p) +A.bwp=new B.q(A.a3,["00G\xa0\xa4","\xa400B"],x.p) +A.bst=new B.q(A.e,["\xa4000B"],x.p) +A.a0J=new B.q(A.e,["0T\xa0\xa4"],x.p) +A.aZa=new B.b([3,A.a0T,6,A.a0i,9,A.a0Q,10,A.bwp,11,A.bst,12,A.a0J],x.o) +A.bn7=new B.q(A.e,["\xa4\xa00\u0b86"],x.p) +A.bkw=new B.q(A.e,["\xa4\xa00\u0bae\u0bbf"],x.p) +A.bwv=new B.q(A.a3,["\xa4\xa00\u0baa\u0bbf","\xa40\u0baa\u0bbf"],x.p) +A.bq6=new B.q(A.e,["\xa4\xa000\u0baa\u0bbf"],x.p) +A.bx4=new B.q(A.a3,["\xa4\xa0000\u0baa\u0bbf","\xa4000\u0baa\u0bbf"],x.p) +A.brr=new B.q(A.e,["\xa4\xa00\u0b9f\u0bbf"],x.p) +A.aZb=new B.b([3,A.bn7,6,A.bkw,9,A.bwv,10,A.bq6,11,A.bx4,12,A.brr],x.o) +A.bjR=new B.q(A.e,["0 \u0e1e\u0e31\u0e19"],x.p) +A.br3=new B.q(A.e,["0 \u0e2b\u0e21\u0e37\u0e48\u0e19"],x.p) +A.bl9=new B.q(A.e,["0 \u0e41\u0e2a\u0e19"],x.p) +A.bo8=new B.q(A.e,["0 \u0e25\u0e49\u0e32\u0e19"],x.p) +A.bor=new B.q(A.e,["0 \u0e1e\u0e31\u0e19\u0e25\u0e49\u0e32\u0e19"],x.p) +A.bqH=new B.q(A.e,["0 \u0e2b\u0e21\u0e37\u0e48\u0e19\u0e25\u0e49\u0e32\u0e19"],x.p) +A.bmx=new B.q(A.e,["0 \u0e41\u0e2a\u0e19\u0e25\u0e49\u0e32\u0e19"],x.p) +A.bk0=new B.q(A.e,["0 \u0e25\u0e49\u0e32\u0e19\u0e25\u0e49\u0e32\u0e19"],x.p) +A.aZf=new B.b([3,A.bjR,4,A.br3,5,A.bl9,6,A.bo8,9,A.bor,10,A.bqH,11,A.bmx,12,A.bk0],x.o) +A.br1=new B.q(A.e,["\xa40\xa0\u0ab9\u0a9c\u0abe\u0ab0"],x.p) +A.bnL=new B.q(A.e,["\xa40\xa0\u0ab2\u0abe\u0a96"],x.p) +A.bmY=new B.q(A.e,["\xa40\xa0\u0a95\u0ab0\u0acb\u0aa1"],x.p) +A.bsC=new B.q(A.e,["\xa40\xa0\u0a85\u0aac\u0a9c"],x.p) +A.brd=new B.q(A.e,["\xa40\xa0\u0aa8\u0abf\u0a96\u0ab0\u0acd\u0ab5"],x.p) +A.bnt=new B.q(A.e,["\xa40\xa0\u0aae\u0ab9\u0abe\u0aaa\u0aa6\u0acd\u0aae"],x.p) +A.bnY=new B.q(A.e,["\xa40\xa0\u0ab6\u0a82\u0a95\u0ac1"],x.p) +A.bnR=new B.q(A.e,["\xa40\xa0\u0a9c\u0ab2\u0aa7\u0abf"],x.p) +A.b0m=new B.b([3,A.br1,5,A.bnL,7,A.bmY,9,A.bsC,11,A.brd,12,A.bnt,13,A.bnY,14,A.bnR],x.o) +A.btt=new B.q(A.e,["0 \u0ab9\u0a9c\u0abe\u0ab0"],x.p) +A.bsD=new B.q(A.e,["0 \u0ab2\u0abe\u0a96"],x.p) +A.bp_=new B.q(A.e,["0 \u0a95\u0ab0\u0acb\u0aa1"],x.p) +A.boZ=new B.q(A.e,["0 \u0a85\u0aac\u0a9c"],x.p) +A.boj=new B.q(A.e,["0 \u0aa8\u0abf\u0a96\u0ab0\u0acd\u0ab5"],x.p) +A.bqG=new B.q(A.e,["0 \u0aae\u0ab9\u0abe\u0aaa\u0aa6\u0acd\u0aae"],x.p) +A.bt_=new B.q(A.e,["0 \u0ab6\u0a82\u0a95\u0ac1"],x.p) +A.bt5=new B.q(A.e,["0 \u0a9c\u0ab2\u0aa7\u0abf"],x.p) +A.b0n=new B.b([3,A.btt,5,A.bsD,7,A.bp_,9,A.boZ,11,A.boj,12,A.bqG,13,A.bt_,14,A.bt5],x.o) +A.bne=new B.q(A.e,["0\xa0\u0ab9\u0a9c\u0abe\u0ab0"],x.p) +A.bqJ=new B.q(A.e,["0\xa0\u0ab2\u0abe\u0a96"],x.p) +A.bqg=new B.q(A.e,["0\xa0\u0a95\u0ab0\u0acb\u0aa1"],x.p) +A.bny=new B.q(A.e,["0\xa0\u0a85\u0aac\u0a9c"],x.p) +A.bs5=new B.q(A.e,["0\xa0\u0aa8\u0abf\u0a96\u0ab0\u0acd\u0ab5"],x.p) +A.bpn=new B.q(A.e,["0\xa0\u0aae\u0ab9\u0abe\u0aaa\u0aa6\u0acd\u0aae"],x.p) +A.bnh=new B.q(A.e,["0\xa0\u0ab6\u0a82\u0a95\u0ac1"],x.p) +A.bmL=new B.q(A.e,["0\xa0\u0a9c\u0ab2\u0aa7\u0abf"],x.p) +A.b0o=new B.b([3,A.bne,5,A.bqJ,7,A.bqg,9,A.bny,11,A.bs5,12,A.bpn,13,A.bnh,14,A.bmL],x.o) +A.a0P=new B.q(A.e,["0 tusen"],x.p) +A.bxn=new B.q(A.a3,["0 miljon","0 miljoner"],x.p) +A.bl7=new B.q(A.e,["000 miljoner"],x.p) +A.bx7=new B.q(A.a3,["0 miljard","0 miljarder"],x.p) +A.bkC=new B.q(A.e,["00 miljarder"],x.p) +A.bxj=new B.q(A.a3,["0 biljon","0 biljoner"],x.p) +A.bn5=new B.q(A.e,["00 biljoner"],x.p) +A.b0C=new B.b([3,A.a0P,6,A.bxn,8,A.bl7,9,A.bx7,10,A.bkC,12,A.bxj,13,A.bn5],x.o) +A.bnv=new B.q(A.e,["0\xa0\u0e9e\u0eb1\u0e99"],x.p) +A.brk=new B.q(A.e,["0\xa0\u0ea5\u0ec9\u0eb2\u0e99"],x.p) +A.bqL=new B.q(A.e,["0\xa0\u0e95\u0eb7\u0ec9"],x.p) +A.bpA=new B.q(A.e,["0\xa0\u0ea5\u0ec9\u0eb2\u0e99\u0ea5\u0ec9\u0eb2\u0e99"],x.p) +A.bq7=new B.q(A.e,["00\u0ea5\u0ea5"],x.p) +A.b0X=new B.b([3,A.bnv,6,A.brk,9,A.bqL,12,A.bpA,13,A.bq7],x.o) +A.p0=new B.q(A.e,["\xa40K"],x.p) +A.bxh=new B.q(A.a3,["\xa40M","\xa4\xa00M"],x.p) +A.brn=new B.q(A.e,["\xa4000M"],x.p) +A.uE=new B.q(A.e,["\xa40B"],x.p) +A.p4=new B.q(A.e,["\xa40T"],x.p) +A.b18=new B.b([3,A.p0,6,A.bxh,8,A.brn,9,A.uE,12,A.p4],x.o) +A.bwt=new B.q(A.a3,["0 \u0c35\u0c47\u0c2f\u0c3f","0 \u0c35\u0c47\u0c32\u0c41"],x.p) +A.brx=new B.q(A.e,["00 \u0c35\u0c47\u0c32\u0c41"],x.p) +A.bxc=new B.q(A.a3,["0 \u0c2e\u0c3f\u0c32\u0c3f\u0c2f\u0c28\u0c4d","0 \u0c2e\u0c3f\u0c32\u0c3f\u0c2f\u0c28\u0c4d\u0c32\u0c41"],x.p) +A.bns=new B.q(A.e,["00 \u0c2e\u0c3f\u0c32\u0c3f\u0c2f\u0c28\u0c4d\u0c32\u0c41"],x.p) +A.bxe=new B.q(A.a3,["0 \u0c2c\u0c3f\u0c32\u0c3f\u0c2f\u0c28\u0c4d","0 \u0c2c\u0c3f\u0c32\u0c3f\u0c2f\u0c28\u0c4d\u0c32\u0c41"],x.p) +A.bog=new B.q(A.e,["00 \u0c2c\u0c3f\u0c32\u0c3f\u0c2f\u0c28\u0c4d\u0c32\u0c41"],x.p) +A.bwO=new B.q(A.a3,["0 \u0c1f\u0c4d\u0c30\u0c3f\u0c32\u0c3f\u0c2f\u0c28\u0c4d","0 \u0c1f\u0c4d\u0c30\u0c3f\u0c32\u0c3f\u0c2f\u0c28\u0c4d\u0c32\u0c41"],x.p) +A.bmI=new B.q(A.e,["00 \u0c1f\u0c4d\u0c30\u0c3f\u0c32\u0c3f\u0c2f\u0c28\u0c4d\u0c32\u0c41"],x.p) +A.b1u=new B.b([3,A.bwt,4,A.brx,6,A.bxc,7,A.bns,9,A.bxe,10,A.bog,12,A.bwO,13,A.bmI],x.o) +A.bxp=new B.q(A.a3,["0 \u0445\u0438\u043b.","0 \u0445\u0438\u043b\u044f\u0434\u0438"],x.p) +A.boP=new B.q(A.e,["00 \u0445\u0438\u043b\u044f\u0434\u0438"],x.p) +A.bwF=new B.q(A.a3,["0 \u043c\u0438\u043b\u0438\u043e\u043d","0 \u043c\u0438\u043b\u0438\u043e\u043d\u0430"],x.p) +A.bru=new B.q(A.e,["00 \u043c\u0438\u043b\u0438\u043e\u043d\u0430"],x.p) +A.bwH=new B.q(A.a3,["0 \u043c\u0438\u043b\u0438\u0430\u0440\u0434","0 \u043c\u0438\u043b\u0438\u0430\u0440\u0434\u0430"],x.p) +A.blV=new B.q(A.e,["00 \u043c\u0438\u043b\u0438\u0430\u0440\u0434\u0430"],x.p) +A.bwr=new B.q(A.a3,["0 \u0442\u0440\u0438\u043b\u0438\u043e\u043d","0 \u0442\u0440\u0438\u043b\u0438\u043e\u043d\u0430"],x.p) +A.bo5=new B.q(A.e,["00 \u0442\u0440\u0438\u043b\u0438\u043e\u043d\u0430"],x.p) +A.b1v=new B.b([3,A.bxp,4,A.boP,6,A.bwF,7,A.bru,9,A.bwH,10,A.blV,12,A.bwr,13,A.bo5],x.o) +A.j6={one:0,other:1,zero:2} +A.bz3=new B.q(A.j6,["0 t\u016bkstotis","0 t\u016bksto\u0161i","0 t\u016bksto\u0161u"],x.p) +A.bz0=new B.q(A.j6,["00 t\u016bkstotis","00 t\u016bksto\u0161i","00 t\u016bksto\u0161i"],x.p) +A.bz5=new B.q(A.j6,["0 miljons","0 miljoni","0 miljonu"],x.p) +A.bz2=new B.q(A.j6,["00 miljons","00 miljoni","00 miljoni"],x.p) +A.bz6=new B.q(A.j6,["0 miljards","0 miljardi","0 miljardu"],x.p) +A.bz1=new B.q(A.j6,["00 miljards","00 miljardi","00 miljardi"],x.p) +A.bz7=new B.q(A.j6,["0 triljons","0 triljoni","0 triljonu"],x.p) +A.bz4=new B.q(A.j6,["00 triljons","00 triljoni","00 triljoni"],x.p) +A.b1w=new B.b([3,A.bz3,4,A.bz0,6,A.bz5,7,A.bz2,9,A.bz6,10,A.bz1,12,A.bz7,13,A.bz4],x.o) +A.aX={few:0,many:1,one:2,other:3} +A.aW8=new B.q(A.aX,["0 tis\xedce","0 tis\xedce","0 tis\xedc","0 tis\xedc"],x.p) +A.aWn=new B.q(A.aX,["00 tis\xedc","00 tis\xedce","00 tis\xedc","00 tis\xedc"],x.p) +A.aVW=new B.q(A.aX,["0 miliony","0 milionu","0 milion","0 milion\u016f"],x.p) +A.aWe=new B.q(A.aX,["00 milion\u016f","00 milionu","00 milion\u016f","00 milion\u016f"],x.p) +A.aVT=new B.q(A.aX,["0 miliardy","0 miliardy","0 miliarda","0 miliard"],x.p) +A.aW0=new B.q(A.aX,["00 miliard","00 miliardy","00 miliard","00 miliard"],x.p) +A.aW3=new B.q(A.aX,["0 biliony","0 bilionu","0 bilion","0 bilion\u016f"],x.p) +A.aVQ=new B.q(A.aX,["00 bilion\u016f","00 bilionu","00 bilion\u016f","00 bilion\u016f"],x.p) +A.b1x=new B.b([3,A.aW8,4,A.aWn,6,A.aVW,7,A.aWe,9,A.aVT,10,A.aW0,12,A.aW3,13,A.aVQ],x.o) +A.bxb=new B.q(A.a3,["0 miler","0 milers"],x.p) +A.brE=new B.q(A.e,["00 milers"],x.p) +A.bwD=new B.q(A.a3,["0 mili\xf3","0 milions"],x.p) +A.brF=new B.q(A.e,["00 milions"],x.p) +A.bwA=new B.q(A.a3,["0 miler de milions","0 milers de milions"],x.p) +A.bto=new B.q(A.e,["00 milers de milions"],x.p) +A.bwP=new B.q(A.a3,["0 bili\xf3","0 bilions"],x.p) +A.bqD=new B.q(A.e,["00 bilions"],x.p) +A.b1y=new B.b([3,A.bxb,4,A.brE,6,A.bwD,7,A.brF,9,A.bwA,10,A.bto,12,A.bwP,13,A.bqD],x.o) +A.bxm=new B.q(A.a3,["mille","0 mila"],x.p) +A.bpR=new B.q(A.e,["00 mila"],x.p) +A.bxr=new B.q(A.a3,["0 milione","0 milioni"],x.p) +A.brG=new B.q(A.e,["00 milioni"],x.p) +A.bwx=new B.q(A.a3,["0 miliardo","0 miliardi"],x.p) +A.bnr=new B.q(A.e,["00 miliardi"],x.p) +A.bwT=new B.q(A.a3,["0 mille miliardi","0 mila miliardi"],x.p) +A.boD=new B.q(A.e,["00 mila miliardi"],x.p) +A.YT=new B.b([3,A.bxm,4,A.bpR,6,A.bxr,7,A.brG,9,A.bwx,10,A.bnr,12,A.bwT,13,A.boD],x.o) +A.bxd=new B.q(A.a3,["0 \u03c7\u03b9\u03bb\u03b9\u03ac\u03b4\u03b1","0 \u03c7\u03b9\u03bb\u03b9\u03ac\u03b4\u03b5\u03c2"],x.p) +A.bk5=new B.q(A.e,["00 \u03c7\u03b9\u03bb\u03b9\u03ac\u03b4\u03b5\u03c2"],x.p) +A.bx0=new B.q(A.a3,["0 \u03b5\u03ba\u03b1\u03c4\u03bf\u03bc\u03bc\u03cd\u03c1\u03b9\u03bf","0 \u03b5\u03ba\u03b1\u03c4\u03bf\u03bc\u03bc\u03cd\u03c1\u03b9\u03b1"],x.p) +A.bp3=new B.q(A.e,["00 \u03b5\u03ba\u03b1\u03c4\u03bf\u03bc\u03bc\u03cd\u03c1\u03b9\u03b1"],x.p) +A.bwN=new B.q(A.a3,["0 \u03b4\u03b9\u03c3\u03b5\u03ba\u03b1\u03c4\u03bf\u03bc\u03bc\u03cd\u03c1\u03b9\u03bf","0 \u03b4\u03b9\u03c3\u03b5\u03ba\u03b1\u03c4\u03bf\u03bc\u03bc\u03cd\u03c1\u03b9\u03b1"],x.p) +A.bpX=new B.q(A.e,["00 \u03b4\u03b9\u03c3\u03b5\u03ba\u03b1\u03c4\u03bf\u03bc\u03bc\u03cd\u03c1\u03b9\u03b1"],x.p) +A.bxi=new B.q(A.a3,["0 \u03c4\u03c1\u03b9\u03c3\u03b5\u03ba\u03b1\u03c4\u03bf\u03bc\u03bc\u03cd\u03c1\u03b9\u03bf","0 \u03c4\u03c1\u03b9\u03c3\u03b5\u03ba\u03b1\u03c4\u03bf\u03bc\u03bc\u03cd\u03c1\u03b9\u03b1"],x.p) +A.bpQ=new B.q(A.e,["00 \u03c4\u03c1\u03b9\u03c3\u03b5\u03ba\u03b1\u03c4\u03bf\u03bc\u03bc\u03cd\u03c1\u03b9\u03b1"],x.p) +A.b1z=new B.b([3,A.bxd,4,A.bk5,6,A.bx0,7,A.bp3,9,A.bwN,10,A.bpX,12,A.bxi,13,A.bpQ],x.o) +A.bwG=new B.q(A.a3,["0 tuhat","0 tuhatta"],x.p) +A.bqS=new B.q(A.e,["00 tuhatta"],x.p) +A.bxl=new B.q(A.a3,["0 miljoona","0 miljoonaa"],x.p) +A.bnd=new B.q(A.e,["00 miljoonaa"],x.p) +A.bxf=new B.q(A.a3,["0 miljardi","0 miljardia"],x.p) +A.brH=new B.q(A.e,["00 miljardia"],x.p) +A.bwq=new B.q(A.a3,["0 biljoona","0 biljoonaa"],x.p) +A.bth=new B.q(A.e,["00 biljoonaa"],x.p) +A.b1A=new B.b([3,A.bwG,4,A.bqS,6,A.bxl,7,A.bnd,9,A.bxf,10,A.brH,12,A.bwq,13,A.bth],x.o) +A.aW9=new B.q(A.aX,["0 tis\xedce","0 tis\xedca","0 tis\xedc","0 tis\xedc"],x.p) +A.aWm=new B.q(A.aX,["00 tis\xedc","00 tis\xedca","00 tis\xedc","00 tis\xedc"],x.p) +A.aVV=new B.q(A.aX,["0 mili\xf3ny","0 mili\xf3na","0 mili\xf3n","0 mili\xf3nov"],x.p) +A.aVY=new B.q(A.aX,["00 mili\xf3nov","00 mili\xf3na","00 mili\xf3nov","00 mili\xf3nov"],x.p) +A.aWh=new B.q(A.aX,["0 miliardy","0 miliardy","0 miliarda","0 mili\xe1rd"],x.p) +A.aW1=new B.q(A.aX,["00 mili\xe1rd","00 miliardy","00 mili\xe1rd","00 mili\xe1rd"],x.p) +A.aWf=new B.q(A.aX,["0 bili\xf3ny","0 bili\xf3na","0 bili\xf3n","0 bili\xf3nov"],x.p) +A.aWb=new B.q(A.aX,["00 bili\xf3nov","00 bili\xf3na","00 bili\xf3nov","00 bili\xf3nov"],x.p) +A.b1B=new B.b([3,A.aW9,4,A.aWm,6,A.aVV,7,A.aVY,9,A.aWh,10,A.aW1,12,A.aWf,13,A.aWb],x.o) +A.a0g=new B.q(A.e,["\xa40\u4e07"],x.p) +A.a12=new B.q(A.e,["\xa40\u5104"],x.p) +A.a0n=new B.q(A.e,["\xa40\u5146"],x.p) +A.bmO=new B.q(A.e,["\xa40\u4eac"],x.p) +A.b2c=new B.b([3,A.bC,4,A.a0g,8,A.a12,12,A.a0n,16,A.bmO],x.o) +A.a0S=new B.q(A.e,["0\u4e07"],x.p) +A.a0R=new B.q(A.e,["0\u5104"],x.p) +A.a0C=new B.q(A.e,["0\u5146"],x.p) +A.bsg=new B.q(A.e,["0\u4eac"],x.p) +A.b2d=new B.b([3,A.bC,4,A.a0S,8,A.a0R,12,A.a0C,16,A.bsg],x.o) +A.a0l=new B.q(A.e,["0\xa0mil\xa0\xa4"],x.p) +A.blW=new B.q(A.e,["00\xa0mil\xa0M\xa4"],x.p) +A.Z_=new B.b([3,A.a0l,6,A.p3,10,A.blW,12,A.uP],x.o) +A.BM=new B.q(A.e,["0\xa0k\xa4"],x.p) +A.bty=new B.q(A.e,["00\xa0kM\xa4"],x.p) +A.b3b=new B.b([3,A.BM,6,A.p3,10,A.bty,12,A.uP],x.o) +A.bsH=new B.q(A.e,["00\xa0MRD\xa0\xa4"],x.p) +A.b3c=new B.b([3,A.BM,6,A.p3,10,A.bsH,12,A.uP],x.o) +A.p5=new B.q(A.e,["0\xa0k"],x.p) +A.bk_=new B.q(A.e,["00\xa0kM"],x.p) +A.b3d=new B.b([3,A.p5,6,A.fx,10,A.bk_,12,A.j1],x.o) +A.a0O=new B.q(A.e,["\xa40\xa0K"],x.p) +A.BS=new B.q(A.e,["\xa40\xa0M"],x.p) +A.boK=new B.q(A.e,["\xa400\xa0MRD"],x.p) +A.blN=new B.q(A.e,["\xa40\xa0B"],x.p) +A.b3e=new B.b([3,A.a0O,6,A.BS,10,A.boK,12,A.blN],x.o) +A.BW=new B.q(A.e,["0\xa0mil"],x.p) +A.uO=new B.q(A.e,["00\xa0mil\xa0M"],x.p) +A.Z0=new B.b([3,A.BW,6,A.fx,10,A.uO,12,A.j1],x.o) +A.bkT=new B.q(A.e,["\xa400\xa0B"],x.p) +A.a0x=new B.q(A.e,["\xa40\xa0T"],x.p) +A.b3f=new B.b([3,A.a0O,6,A.BS,10,A.bkT,12,A.a0x],x.o) +A.b3g=new B.b([3,A.p5,6,A.fx,10,A.uO,12,A.j1],x.o) +A.BI=new B.q(A.e,["0\xa0K"],x.p) +A.b3h=new B.b([3,A.BI,6,A.fx,10,A.uO,12,A.j1],x.o) +A.bnM=new B.q(A.e,["0 \u0939\u091c\u093c\u093e\u0930"],x.p) +A.BO=new B.q(A.e,["0 \u0932\u093e\u0916"],x.p) +A.bof=new B.q(A.e,["0 \u0915\u0930\u094b\u0921\u093c"],x.p) +A.a0A=new B.q(A.e,["0 \u0905\u0930\u092c"],x.p) +A.bo1=new B.q(A.e,["0 \u0916\u0930\u092c"],x.p) +A.b3p=new B.b([3,A.bnM,5,A.BO,7,A.bof,9,A.a0A,11,A.bo1],x.o) +A.bwJ=new B.q(A.a3,["0 mill\xf3n","0 mill\xf3ns"],x.p) +A.bpU=new B.q(A.e,["00 mill\xf3ns"],x.p) +A.bwV=new B.q(A.a3,["0 bill\xf3n","0 bill\xf3ns"],x.p) +A.bsi=new B.q(A.e,["00 bill\xf3ns"],x.p) +A.b6a=new B.b([6,A.bwJ,7,A.bpU,12,A.bwV,13,A.bsi],x.o) +A.brj=new B.q(A.e,["0 \u1011\u1031\u102c\u1004\u103a"],x.p) +A.bt0=new B.q(A.e,["0 \u101e\u1031\u102c\u1004\u103a\u1038"],x.p) +A.blP=new B.q(A.e,["0 \u101e\u102d\u1014\u103a\u1038"],x.p) +A.bpz=new B.q(A.e,["0 \u101e\u1014\u103a\u1038"],x.p) +A.boX=new B.q(A.e,["0 \u1000\u102f\u100b\u1031"],x.p) +A.bmM=new B.q(A.e,["\u1000\u102f\u100b\u1031 0 \u101e\u1031\u102c\u1004\u103a\u1038"],x.p) +A.bts=new B.q(A.e,["\u1000\u102f\u100b\u1031 0 \u101e\u102d\u1014\u103a\u1038"],x.p) +A.bpW=new B.q(A.e,["\u1000\u102f\u100b\u1031 0 \u101e\u1014\u103a\u1038"],x.p) +A.bnk=new B.q(A.e,["0 \u1000\u1031\u102c\u100b\u102d"],x.p) +A.b6g=new B.b([3,A.brj,4,A.bt0,5,A.blP,6,A.bpz,7,A.boX,11,A.bmM,12,A.bts,13,A.bpW,14,A.bnk],x.o) +A.bmT=new B.q(A.e,["\xa4\xa00\xa0\u1011\u1031\u102c\u1004\u103a"],x.p) +A.bmy=new B.q(A.e,["\xa4\xa00\xa0\u101e\u1031\u102c\u1004\u103a\u1038"],x.p) +A.bnT=new B.q(A.e,["\xa4\xa00\xa0\u101e\u102d\u1014\u103a\u1038"],x.p) +A.bte=new B.q(A.e,["\xa4\xa00\xa0\u101e\u1014\u103a\u1038"],x.p) +A.bm1=new B.q(A.e,["\xa4\xa00\xa0\u1000\u102f\u100b\u1031"],x.p) +A.blv=new B.q(A.e,["\xa4\xa0\u1000\u102f\u100b\u1031\xa00\xa0\u101e\u1031\u102c\u1004\u103a\u1038"],x.p) +A.bsv=new B.q(A.e,["\xa4\xa0\u1000\u102f\u100b\u1031\xa00\xa0\u101e\u102d\u1014\u103a\u1038"],x.p) +A.blp=new B.q(A.e,["\xa4\xa0\u1000\u102f\u100b\u1031\xa00\xa0\u101e\u1014\u103a\u1038"],x.p) +A.bni=new B.q(A.e,["\xa4\xa00\xa0\u1000\u1031\u102c\u100b\u102d"],x.p) +A.b6h=new B.b([3,A.bmT,4,A.bmy,5,A.bnT,6,A.bte,7,A.bm1,11,A.blv,12,A.bsv,13,A.blp,14,A.bni],x.o) +A.bmv=new B.q(A.e,["0\xa0\u09b9\u09be"],x.p) +A.bsc=new B.q(A.e,["0\xa0\u09b2\u09be"],x.p) +A.boU=new B.q(A.e,["0\xa0\u0995\u09cb"],x.p) +A.bwL=new B.q(A.a3,["00\xa0\u09b6\u09a4\xa0\u0995\u09cb","00\u09b6\u09a4\xa0\u0995\u09cb"],x.p) +A.bl3=new B.q(A.e,["000\u0995\u09cb"],x.p) +A.boB=new B.q(A.e,["0\xa0\u09b2\u09be.\u0995\u09cb."],x.p) +A.b7x=new B.b([3,A.bmv,5,A.bsc,7,A.boU,10,A.bwL,11,A.bl3,12,A.boB],x.o) +A.BH=new B.q(A.e,["0 mil"],x.p) +A.bx1=new B.q(A.a3,["0 mill\xf3n","0 millones"],x.p) +A.bsY=new B.q(A.e,["00 millones"],x.p) +A.bri=new B.q(A.e,["0 mil millones"],x.p) +A.bx3=new B.q(A.a3,["0 bill\xf3n","0 billones"],x.p) +A.bjO=new B.q(A.e,["00 billones"],x.p) +A.oS=new B.b([3,A.BH,6,A.bx1,7,A.bsY,9,A.bri,12,A.bx3,13,A.bjO],x.o) +A.a1z=new B.q(A.a3,["0 milh\xe3o","0 milh\xf5es"],x.p) +A.bq1=new B.q(A.e,["00 milh\xf5es"],x.p) +A.br4=new B.q(A.e,["0 mil milh\xf5es"],x.p) +A.bwW=new B.q(A.a3,["0 bili\xe3o","0 bili\xf5es"],x.p) +A.br6=new B.q(A.e,["00 bili\xf5es"],x.p) +A.b7B=new B.b([3,A.BH,6,A.a1z,7,A.bq1,9,A.br4,12,A.bwW,13,A.br6],x.o) +A.bq5=new B.q(A.e,["0 \u1796\u17b6\u1793\u17cb"],x.p) +A.bqo=new B.q(A.e,["000\u1796\u17b6\u1793\u17cb"],x.p) +A.bn0=new B.q(A.e,["0 \u179b\u17b6\u1793"],x.p) +A.br2=new B.q(A.e,["0 \u1794\u17ca\u17b8\u179b\u17b6\u1793"],x.p) +A.bqp=new B.q(A.e,["0 \u1791\u17d2\u179a\u17b8\u179b\u17b6\u1793"],x.p) +A.b9f=new B.b([3,A.bq5,5,A.bqo,6,A.bn0,9,A.br2,12,A.bqp],x.o) +A.bkP=new B.q(A.e,["\xa4\xa00\xa0\u09b9\u09be\u099c\u09be\u09f0"],x.p) +A.br5=new B.q(A.e,["\xa4\xa0000\xa0\u09b2\u09be\u0996"],x.p) +A.bkr=new B.q(A.e,["\xa4\xa00\xa0\u09a8\u09bf\u09af\u09c1\u09a4"],x.p) +A.bsX=new B.q(A.e,["\xa4\xa00\xa0\u09b6\u09a4\xa0\u0995\u09cb\u099f\u09bf"],x.p) +A.bqX=new B.q(A.e,["\xa4\xa00\xa0\u09b6\u09a4\xa0\u09aa\u09f0\u09be\u09f0\u09cd\u09a6\u09cd\u09a7"],x.p) +A.b9g=new B.b([3,A.bkP,5,A.br5,6,A.bkr,9,A.bsX,12,A.bqX],x.o) +A.bt2=new B.q(A.e,["0 \u0e9e\u0eb1\u0e99"],x.p) +A.bqu=new B.q(A.e,["0 \u0ec1\u0eaa\u0e99"],x.p) +A.bk7=new B.q(A.e,["0 \u0ea5\u0ec9\u0eb2\u0e99"],x.p) +A.bpu=new B.q(A.e,["0 \u0e95\u0eb7\u0ec9"],x.p) +A.brm=new B.q(A.e,["0 \u0ea5\u0ec9\u0eb2\u0e99\u0ea5\u0ec9\u0eb2\u0e99"],x.p) +A.b9h=new B.b([3,A.bt2,5,A.bqu,6,A.bk7,9,A.bpu,12,A.brm],x.o) +A.bjY=new B.q(A.e,["0\xa0\u043c\u044b\u04a3"],x.p) +A.bna=new B.q(A.e,["000\xa0\u043c."],x.p) +A.uQ=new B.q(A.e,["0\xa0\u043c\u043b\u043d"],x.p) +A.BJ=new B.q(A.e,["0\xa0\u043c\u043b\u0440\u0434"],x.p) +A.uK=new B.q(A.e,["0\xa0\u0442\u0440\u043b\u043d"],x.p) +A.b9i=new B.b([3,A.bjY,5,A.bna,6,A.uQ,9,A.BJ,12,A.uK],x.o) +A.bp6=new B.q(A.e,["0 \u09b9\u09be\u099c\u09be\u09f0"],x.p) +A.bpt=new B.q(A.e,["0 \u09a8\u09bf\u09af\u09c1\u09a4"],x.p) +A.bpm=new B.q(A.e,["0 \u09b6\u09a4 \u0995\u09cb\u099f\u09bf"],x.p) +A.btc=new B.q(A.e,["0 \u09b6\u09a4 \u09aa\u09f0\u09be\u09f0\u09cd\u09a6\u09cd\u09a7"],x.p) +A.b9j=new B.b([3,A.bp6,5,A.a0z,6,A.bpt,9,A.bpm,12,A.btc],x.o) +A.ble=new B.q(A.e,["\xa4\xa0elfu0"],x.p) +A.bjZ=new B.q(A.e,["\xa4\xa0elfu00;\xa4elfu\xa0-00"],x.p) +A.boA=new B.q(A.e,["\xa4\xa0laki000;\xa4laki\xa0-000"],x.p) +A.bwE=new B.q(A.a3,["\xa4\xa00M;\xa4-0M","\xa4\xa00M"],x.p) +A.bwQ=new B.q(A.a3,["\xa4\xa000M;\xa4M-00M","\xa4\xa000M;\xa4-00M"],x.p) +A.bxt=new B.q(A.a3,["\xa4\xa0000M;\xa4Milioni-000","\xa4\xa0000M"],x.p) +A.bsl=new B.q(A.e,["\xa4\xa00B;\xa4-0B"],x.p) +A.bx6=new B.q(A.a3,["\xa4\xa00T;\xa4-0T","\xa4\xa00T"],x.p) +A.bqe=new B.q(A.e,["\xa4\xa0000T;\xa4-000T"],x.p) +A.b9S=new B.b([3,A.ble,4,A.bjZ,5,A.boA,6,A.bwE,7,A.bwQ,8,A.bxt,9,A.bsl,12,A.bx6,14,A.bqe],x.o) +A.brs=new B.q(A.e,["0\xa0N"],x.p) +A.bm7=new B.q(A.e,["0\xa0Tr"],x.p) +A.BP=new B.q(A.e,["0\xa0T"],x.p) +A.bot=new B.q(A.e,["0\xa0NT"],x.p) +A.b9X=new B.b([3,A.brs,6,A.bm7,9,A.BP,12,A.bot],x.o) +A.bkp=new B.q(A.e,["0\xa0Md"],x.p) +A.boC=new B.q(A.e,["0\xa0Bn"],x.p) +A.ZS=new B.b([3,A.p5,6,A.fx,9,A.bkp,12,A.boC],x.o) +A.bku=new B.q(A.e,["elfu 0;elfu -0"],x.p) +A.brp=new B.q(A.e,["milioni 0;milioni -0"],x.p) +A.btb=new B.q(A.e,["bilioni 0;bilioni -0"],x.p) +A.bpG=new B.q(A.e,["trilioni 0;trilioni -0"],x.p) +A.b9Y=new B.b([3,A.bku,6,A.brp,9,A.btb,12,A.bpG],x.o) +A.bkZ=new B.q(A.e,["0\xa0E"],x.p) +A.bn8=new B.q(A.e,["0\xa0Mrd"],x.p) +A.b9Z=new B.b([3,A.bkZ,6,A.fx,9,A.bn8,12,A.j1],x.o) +A.aVZ=new B.q(A.aX,["0 \u0442\u044b\u0441\u044f\u0447\u0438","0 \u0442\u044b\u0441\u044f\u0447","0 \u0442\u044b\u0441\u044f\u0447\u0430","0 \u0442\u044b\u0441\u044f\u0447\u0438"],x.p) +A.aW7=new B.q(A.aX,["0 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u0430","0 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u043e\u0432","0 \u043c\u0438\u043b\u043b\u0438\u043e\u043d","0 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u0430"],x.p) +A.aWd=new B.q(A.aX,["0 \u043c\u0438\u043b\u043b\u0438\u0430\u0440\u0434\u0430","0 \u043c\u0438\u043b\u043b\u0438\u0430\u0440\u0434\u043e\u0432","0 \u043c\u0438\u043b\u043b\u0438\u0430\u0440\u0434","0 \u043c\u0438\u043b\u043b\u0438\u0430\u0440\u0434\u0430"],x.p) +A.aWg=new B.q(A.aX,["0 \u0442\u0440\u0438\u043b\u043b\u0438\u043e\u043d\u0430","0 \u0442\u0440\u0438\u043b\u043b\u0438\u043e\u043d\u043e\u0432","0 \u0442\u0440\u0438\u043b\u043b\u0438\u043e\u043d","0 \u0442\u0440\u0438\u043b\u043b\u0438\u043e\u043d\u0430"],x.p) +A.ba_=new B.b([3,A.aVZ,6,A.aW7,9,A.aWd,12,A.aWg],x.o) +A.bqk=new B.q(A.e,["0 mij\xeb"],x.p) +A.bmm=new B.q(A.e,["0 milion"],x.p) +A.brR=new B.q(A.e,["0 miliard"],x.p) +A.a0G=new B.q(A.e,["0 bilion"],x.p) +A.ba0=new B.b([3,A.bqk,6,A.bmm,9,A.brR,12,A.a0G],x.o) +A.brb=new B.q(A.e,["\u0daf\u0dc4\u0dc3 0"],x.p) +A.brA=new B.q(A.e,["\u0db8\u0dd2\u0dbd\u0dd2\u0dba\u0db1 0"],x.p) +A.bjW=new B.q(A.e,["\u0db6\u0dd2\u0dbd\u0dd2\u0dba\u0db1 0"],x.p) +A.bpr=new B.q(A.e,["\u0da7\u0dca\u200d\u0dbb\u0dd2\u0dbd\u0dd2\u0dba\u0db1 0"],x.p) +A.ba1=new B.b([3,A.brb,6,A.brA,9,A.bjW,12,A.bpr],x.o) +A.bme=new B.q(A.e,["0\xa0tys."],x.p) +A.p1=new B.q(A.e,["0\xa0mln"],x.p) +A.BY=new B.q(A.e,["0\xa0mld"],x.p) +A.a15=new B.q(A.e,["0\xa0bln"],x.p) +A.ba2=new B.b([3,A.bme,6,A.p1,9,A.BY,12,A.a15],x.o) +A.bqM=new B.q(A.e,["elfu\xa00;elfu\xa0-0"],x.p) +A.bwY=new B.q(A.a3,["0M;-0M","0M"],x.p) +A.bom=new B.q(A.e,["0B;-0B"],x.p) +A.bx_=new B.q(A.a3,["0T;-0T","0T"],x.p) +A.ba3=new B.b([3,A.bqM,6,A.bwY,9,A.bom,12,A.bx_],x.o) +A.bmq=new B.q(A.e,["\xa40k"],x.p) +A.BX=new B.q(A.e,["\xa40M"],x.p) +A.ba4=new B.b([3,A.bmq,6,A.BX,9,A.uE,12,A.p4],x.o) +A.buc=new B.q(A.dm,["0 tisu\u0107e","0 tisu\u0107a","0 tisu\u0107a"],x.p) +A.bu9=new B.q(A.dm,["0 milijuna","0 milijun","0 milijuna"],x.p) +A.a1g=new B.q(A.dm,["0 milijarde","0 milijarda","0 milijardi"],x.p) +A.bug=new B.q(A.dm,["0 bilijuna","0 bilijun","0 bilijuna"],x.p) +A.ba5=new B.b([3,A.buc,6,A.bu9,9,A.a1g,12,A.bug],x.o) +A.a0q=new B.q(A.e,["0 ribu"],x.p) +A.a18=new B.q(A.e,["0 juta"],x.p) +A.bl1=new B.q(A.e,["0 miliar"],x.p) +A.bnU=new B.q(A.e,["0 triliun"],x.p) +A.ZT=new B.b([3,A.a0q,6,A.a18,9,A.bl1,12,A.bnU],x.o) +A.bpZ=new B.q(A.e,["0 ngh\xecn"],x.p) +A.bt4=new B.q(A.e,["0 tri\u1ec7u"],x.p) +A.brv=new B.q(A.e,["0 t\u1ef7"],x.p) +A.bkM=new B.q(A.e,["0 ngh\xecn t\u1ef7"],x.p) +A.ba6=new B.b([3,A.bpZ,6,A.bt4,9,A.brv,12,A.bkM],x.o) +A.btj=new B.q(A.e,["0\xa0hilj.\xa0\xa4"],x.p) +A.a16=new B.q(A.e,["0\xa0mlrd.\xa0\xa4"],x.p) +A.uD=new B.q(A.e,["0\xa0bil.\xa0\xa4"],x.p) +A.ZU=new B.b([3,A.btj,6,A.uF,9,A.a16,12,A.uD],x.o) +A.BV=new B.q(A.e,["0\xa0tis."],x.p) +A.uG=new B.q(A.e,["0\xa0mil."],x.p) +A.bs2=new B.q(A.e,["0\xa0mlr."],x.p) +A.uL=new B.q(A.e,["0\xa0bil."],x.p) +A.ba7=new B.b([3,A.BV,6,A.uG,9,A.bs2,12,A.uL],x.o) +A.C_=new B.q(A.e,["0\xa0mld."],x.p) +A.bks=new B.q(A.e,["0\xa0tril."],x.p) +A.ba8=new B.b([3,A.BI,6,A.uG,9,A.C_,12,A.bks],x.o) +A.box=new B.q(A.e,["0\xa0ming"],x.p) +A.a13=new B.q(A.e,["0\xa0mlrd"],x.p) +A.BU=new B.q(A.e,["0\xa0trln"],x.p) +A.ba9=new B.b([3,A.box,6,A.p1,9,A.a13,12,A.BU],x.o) +A.bqY=new B.q(A.e,["0\xa0t"],x.p) +A.a0W=new B.q(A.e,["0\xa0mio."],x.p) +A.bn1=new B.q(A.e,["0\xa0mia."],x.p) +A.bol=new B.q(A.e,["0\xa0bio."],x.p) +A.baa=new B.b([3,A.bqY,6,A.a0W,9,A.bn1,12,A.bol],x.o) +A.blw=new B.q(A.e,["0\xa0Tsg.\xa0\xa4"],x.p) +A.a19=new B.q(A.e,["0\xa0Mio.\xa0\xa4"],x.p) +A.a1e=new B.q(A.e,["0\xa0Mrd.\xa0\xa4"],x.p) +A.a10=new B.q(A.e,["0\xa0Bio.\xa0\xa4"],x.p) +A.bab=new B.b([3,A.blw,6,A.a19,9,A.a1e,12,A.a10],x.o) +A.brV=new B.q(A.e,["\xa40\xa0\u123a"],x.p) +A.bld=new B.q(A.e,["\xa40\xa0\u121a"],x.p) +A.bov=new B.q(A.e,["\xa40\xa0\u1262"],x.p) +A.bm2=new B.q(A.e,["\xa40\xa0\u1275"],x.p) +A.bac=new B.b([3,A.brV,6,A.bld,9,A.bov,12,A.bm2],x.o) +A.boc=new B.q(A.e,["\xa40\u0c35\u0c47"],x.p) +A.bkj=new B.q(A.e,["\xa40\u0c2e\u0c3f"],x.p) +A.blL=new B.q(A.e,["\xa40\u0c2c\u0c3f"],x.p) +A.bsn=new B.q(A.e,["\xa40\u0c1f\u0c4d\u0c30\u0c3f"],x.p) +A.bad=new B.b([3,A.boc,6,A.bkj,9,A.blL,12,A.bsn],x.o) +A.bkg=new B.q(A.e,["0\xa0tn"],x.p) +A.bnG=new B.q(A.e,["0\xa0mn"],x.p) +A.brD=new B.q(A.e,["0\xa0md"],x.p) +A.BZ=new B.q(A.e,["0\xa0bn"],x.p) +A.bae=new B.b([3,A.bkg,6,A.bnG,9,A.brD,12,A.BZ],x.o) +A.bsO=new B.q(A.e,["\xa40K\u200f"],x.p) +A.bpw=new B.q(A.e,["\xa40M\u200f"],x.p) +A.bjN=new B.q(A.e,["\xa40B\u200f"],x.p) +A.bl4=new B.q(A.e,["\xa40T\u200f"],x.p) +A.ZV=new B.b([3,A.bsO,6,A.bpw,9,A.bjN,12,A.bl4],x.o) +A.btd=new B.q(A.e,["\xa40G"],x.p) +A.oU=new B.b([3,A.p0,6,A.BX,9,A.btd,12,A.p4],x.o) +A.bk4=new B.q(A.e,["0\xa0\u0438\u043b\u0458.\xa0\xa4"],x.p) +A.a0u=new B.q(A.e,["0\xa0\u043c\u0438\u043b.\xa0\xa4"],x.p) +A.bkv=new B.q(A.e,["0\xa0\u043c\u0438\u043b\u0458.\xa0\xa4"],x.p) +A.a0E=new B.q(A.e,["0\xa0\u0431\u0438\u043b.\xa0\xa4"],x.p) +A.baf=new B.b([3,A.bk4,6,A.a0u,9,A.bkv,12,A.a0E],x.o) +A.bll=new B.q(A.e,["0\xa0\u03c7\u03b9\u03bb.\xa0\xa4"],x.p) +A.bp0=new B.q(A.e,["0\xa0\u03b5\u03ba.\xa0\xa4"],x.p) +A.btE=new B.q(A.e,["0\xa0\u03b4\u03b9\u03c3.\xa0\xa4"],x.p) +A.bp7=new B.q(A.e,["0\xa0\u03c4\u03c1\u03b9\u03c3.\xa0\xa4"],x.p) +A.bag=new B.b([3,A.bll,6,A.bp0,9,A.btE,12,A.bp7],x.o) +A.bqb=new B.q(A.e,["0\u0b86"],x.p) +A.bmU=new B.q(A.e,["0\u0bae\u0bbf"],x.p) +A.bsu=new B.q(A.e,["0\u0baa\u0bbf"],x.p) +A.bqA=new B.q(A.e,["0\u0b9f\u0bbf"],x.p) +A.bah=new B.b([3,A.bqb,6,A.bmU,9,A.bsu,12,A.bqA],x.o) +A.bqx=new B.q(A.e,["0 \u043c\u044b\u04a3"],x.p) +A.a1f=new B.q(A.e,["0 \u043c\u0438\u043b\u043b\u0438\u043e\u043d"],x.p) +A.a1c=new B.q(A.e,["0 \u043c\u0438\u043b\u043b\u0438\u0430\u0440\u0434"],x.p) +A.a0H=new B.q(A.e,["0 \u0442\u0440\u0438\u043b\u043b\u0438\u043e\u043d"],x.p) +A.bai=new B.b([3,A.bqx,6,A.a1f,9,A.a1c,12,A.a0H],x.o) +A.bkK=new B.q(A.e,["0\xa0mij\xeb"],x.p) +A.baj=new B.b([3,A.bkK,6,A.p1,9,A.BY,12,A.a15],x.o) +A.bqj=new B.q(A.e,["0\xa0tys.\xa0\xa4"],x.p) +A.uM=new B.q(A.e,["0\xa0mln\xa0\xa4"],x.p) +A.C0=new B.q(A.e,["0\xa0mld\xa0\xa4"],x.p) +A.a0k=new B.q(A.e,["0\xa0bln\xa0\xa4"],x.p) +A.bak=new B.b([3,A.bqj,6,A.uM,9,A.C0,12,A.a0k],x.o) +A.brK=new B.q(A.e,["0 \u10d0\u10d7\u10d0\u10e1\u10d8"],x.p) +A.bn2=new B.q(A.e,["0 \u10db\u10d8\u10da\u10d8\u10dd\u10dc\u10d8"],x.p) +A.bq8=new B.q(A.e,["0 \u10db\u10d8\u10da\u10d8\u10d0\u10e0\u10d3\u10d8"],x.p) +A.btz=new B.q(A.e,["0 \u10e2\u10e0\u10d8\u10da\u10d8\u10dd\u10dc\u10d8"],x.p) +A.bal=new B.b([3,A.brK,6,A.bn2,9,A.bq8,12,A.btz],x.o) +A.bkq=new B.q(A.e,["0 ezer"],x.p) +A.bsk=new B.q(A.e,["0 milli\xf3"],x.p) +A.bkQ=new B.q(A.e,["0 milli\xe1rd"],x.p) +A.bky=new B.q(A.e,["0 billi\xf3"],x.p) +A.bam=new B.b([3,A.bkq,6,A.bsk,9,A.bkQ,12,A.bky],x.o) +A.brU=new B.q(A.e,["0 \u043c\u044f\u043d\u0433\u0430"],x.p) +A.bsx=new B.q(A.e,["0 \u0441\u0430\u044f"],x.p) +A.bpN=new B.q(A.e,["0 \u0442\u044d\u0440\u0431\u0443\u043c"],x.p) +A.bqK=new B.q(A.e,["0 \u0438\u0445 \u043d\u0430\u044f\u0434"],x.p) +A.ban=new B.b([3,A.brU,6,A.bsx,9,A.bpN,12,A.bqK],x.o) +A.bkA=new B.q(A.e,["0 thousand"],x.p) +A.a0f=new B.q(A.e,["0 million"],x.p) +A.bqd=new B.q(A.e,["0 billion"],x.p) +A.a0Y=new B.q(A.e,["0 trillion"],x.p) +A.fu=new B.b([3,A.bkA,6,A.a0f,9,A.bqd,12,A.a0Y],x.o) +A.bnp=new B.q(A.e,["0\xa0tuh\xa0\xa4"],x.p) +A.a0Z=new B.q(A.e,["0\xa0trln\xa0\xa4"],x.p) +A.bao=new B.b([3,A.bnp,6,A.uM,9,A.C0,12,A.a0Z],x.o) +A.bpe=new B.q(A.e,["0\u0c35\u0c47"],x.p) +A.btw=new B.q(A.e,["0\u0c2e\u0c3f"],x.p) +A.bmD=new B.q(A.e,["0\u0c2c\u0c3f"],x.p) +A.brt=new B.q(A.e,["0\u0c1f\u0c4d\u0c30\u0c3f"],x.p) +A.bap=new B.b([3,A.bpe,6,A.btw,9,A.bmD,12,A.brt],x.o) +A.bsN=new B.q(A.e,["0\xa0hilj."],x.p) +A.a0F=new B.q(A.e,["0\xa0mlrd."],x.p) +A.ZW=new B.b([3,A.bsN,6,A.uG,9,A.a0F,12,A.uL],x.o) +A.bnV=new B.q(A.e,["0 trilion"],x.p) +A.baq=new B.b([3,A.a0q,6,A.a18,9,A.a0G,12,A.bnV],x.o) +A.brX=new B.q(A.e,["0\xa0\u0445\u0438\u0459."],x.p) +A.a0B=new B.q(A.e,["0\xa0\u043c\u043b\u0440\u0434."],x.p) +A.bar=new B.b([3,A.brX,6,A.a0o,9,A.a0B,12,A.a0v],x.o) +A.btf=new B.q(A.e,["\xa40\u0b39"],x.p) +A.btr=new B.q(A.e,["\xa40\u0b28\u0b3f"],x.p) +A.bsT=new B.q(A.e,["\xa40\u0b2c\u0b3f"],x.p) +A.bko=new B.q(A.e,["\xa40\u0b1f\u0b4d\u0b30\u0b3f"],x.p) +A.bas=new B.b([3,A.btf,6,A.btr,9,A.bsT,12,A.bko],x.o) +A.blQ=new B.q(A.e,["0 duisend"],x.p) +A.a1d=new B.q(A.e,["0 miljoen"],x.p) +A.a11=new B.q(A.e,["0 miljard"],x.p) +A.a0I=new B.q(A.e,["0 biljoen"],x.p) +A.bat=new B.b([3,A.blQ,6,A.a1d,9,A.a11,12,A.a0I],x.o) +A.blR=new B.q(A.e,["0 duizend"],x.p) +A.bau=new B.b([3,A.blR,6,A.a1d,9,A.a11,12,A.a0I],x.o) +A.bqc=new B.q(A.e,["0 \u0d06\u0d2f\u0d3f\u0d30\u0d02"],x.p) +A.bkO=new B.q(A.e,["0 \u0d26\u0d36\u0d32\u0d15\u0d4d\u0d37\u0d02"],x.p) +A.blX=new B.q(A.e,["0 \u0d2c\u0d3f\u0d32\u0d4d\u0d2f\u0d7a"],x.p) +A.bkt=new B.q(A.e,["0 \u0d1f\u0d4d\u0d30\u0d3f\u0d32\u0d4d\u0d2f\u0d7a"],x.p) +A.bav=new B.b([3,A.bqc,6,A.bkO,9,A.blX,12,A.bkt],x.o) +A.bx5=new B.q(A.a3,["0 \u0438\u043b\u0458\u0430\u0434\u0430","0 \u0438\u043b\u0458\u0430\u0434\u0438"],x.p) +A.bwZ=new B.q(A.a3,["0 \u043c\u0438\u043b\u0438\u043e\u043d","0 \u043c\u0438\u043b\u0438\u043e\u043d\u0438"],x.p) +A.bwy=new B.q(A.a3,["0 \u043c\u0438\u043b\u0438\u0458\u0430\u0440\u0434\u0430","0 \u043c\u0438\u043b\u0438\u0458\u0430\u0440\u0434\u0438"],x.p) +A.bwK=new B.q(A.a3,["0 \u0431\u0438\u043b\u0438\u043e\u043d","0 \u0431\u0438\u043b\u0438\u043e\u043d\u0438"],x.p) +A.baw=new B.b([3,A.bx5,6,A.bwZ,9,A.bwy,12,A.bwK],x.o) +A.bsG=new B.q(A.e,["0\xa0\u0647\u0632\u0627\u0631\xa0\xa4"],x.p) +A.bmV=new B.q(A.e,["0\xa0\u0645\u06cc\u0644\u06cc\u0648\u0646\xa0\xa4"],x.p) +A.blE=new B.q(A.e,["0\xa0\u0645\u06cc\u0644\u06cc\u0627\u0631\u062f\xa0\xa4"],x.p) +A.bl8=new B.q(A.e,["0\xa0\u0647\u0632\u0627\u0631\u0645\u06cc\u0644\u06cc\u0627\u0631\u062f\xa0\xa4"],x.p) +A.bax=new B.b([3,A.bsG,6,A.bmV,9,A.blE,12,A.bl8],x.o) +A.brg=new B.q(A.e,["0\xa0\u043c\u0438\u04a3\xa0\xa4"],x.p) +A.uC=new B.q(A.e,["0\xa0\u043c\u043b\u043d\xa0\xa4"],x.p) +A.bt6=new B.q(A.e,["0\xa0\u043c\u043b\u0434\xa0\xa4"],x.p) +A.uN=new B.q(A.e,["0\xa0\u0442\u0440\u043b\u043d\xa0\xa4"],x.p) +A.bay=new B.b([3,A.brg,6,A.uC,9,A.bt6,12,A.uN],x.o) +A.btF=new B.q(A.e,["0\xa0\u0442\u0438\u0441.\xa0\xa4"],x.p) +A.BQ=new B.q(A.e,["0\xa0\u043c\u043b\u0440\u0434\xa0\xa4"],x.p) +A.baz=new B.b([3,A.btF,6,A.uC,9,A.BQ,12,A.uN],x.o) +A.BL=new B.q(A.e,["0\xa0tis.\xa0\xa4"],x.p) +A.a0M=new B.q(A.e,["0\xa0mio.\xa0\xa4"],x.p) +A.a0U=new B.q(A.e,["0\xa0mrd.\xa0\xa4"],x.p) +A.baA=new B.b([3,A.BL,6,A.a0M,9,A.a0U,12,A.uD],x.o) +A.bm6=new B.q(A.e,["\xa40J"],x.p) +A.baB=new B.b([3,A.p0,6,A.bm6,9,A.uE,12,A.p4],x.o) +A.bra=new B.q(A.e,["0\xa0\u0623\u0644\u0641\xa0\xa4"],x.p) +A.bnB=new B.q(A.e,["0\xa0\u0645\u0644\u064a\u0648\u0646\xa0\xa4"],x.p) +A.bmH=new B.q(A.e,["0\xa0\u0645\u0644\u064a\u0627\u0631\xa0\xa4"],x.p) +A.bps=new B.q(A.e,["0\xa0\u062a\u0631\u0644\u064a\u0648\u0646\xa0\xa4"],x.p) +A.Bk=new B.b([3,A.bra,6,A.bnB,9,A.bmH,12,A.bps],x.o) +A.ZX=new B.b([3,A.a0T,6,A.a0i,9,A.a0Q,12,A.a0J],x.o) +A.a0h=new B.q(A.e,["\xa4\xa00K"],x.p) +A.bk9=new B.q(A.e,["\xa4\xa00M"],x.p) +A.blm=new B.q(A.e,["\xa4\xa00G"],x.p) +A.bnx=new B.q(A.e,["\xa4\xa00T"],x.p) +A.baC=new B.b([3,A.a0h,6,A.bk9,9,A.blm,12,A.bnx],x.o) +A.bsK=new B.q(A.e,["0\xa0mM"],x.p) +A.bla=new B.q(A.e,["0\xa0Bi"],x.p) +A.baD=new B.b([3,A.BW,6,A.fx,9,A.bsK,12,A.bla],x.o) +A.bqE=new B.q(A.e,["0 min"],x.p) +A.a0r=new B.q(A.e,["0 milyon"],x.p) +A.brS=new B.q(A.e,["0 milyard"],x.p) +A.a17=new B.q(A.e,["0 trilyon"],x.p) +A.baE=new B.b([3,A.bqE,6,A.a0r,9,A.brS,12,A.a17],x.o) +A.bsM=new B.q(A.e,["0 \u13a2\u13ef\u13a6\u13f4\u13b5"],x.p) +A.blu=new B.q(A.e,["0 \u13a2\u13f3\u13c6\u13d7\u13c5\u13db"],x.p) +A.bqs=new B.q(A.e,["0 \u13a2\u13ef\u13d4\u13b3\u13d7\u13c5\u13db"],x.p) +A.boq=new B.q(A.e,["0 \u13a2\u13ef\u13e6\u13a0\u13d7\u13c5\u13db"],x.p) +A.baF=new B.b([3,A.bsM,6,A.blu,9,A.bqs,12,A.boq],x.o) +A.BT=new B.q(A.e,["0k"],x.p) +A.p2=new B.q(A.e,["0M"],x.p) +A.uH=new B.q(A.e,["0B"],x.p) +A.mm=new B.q(A.e,["0T"],x.p) +A.baG=new B.b([3,A.BT,6,A.p2,9,A.uH,12,A.mm],x.o) +A.bjQ=new B.q(A.e,["0 ming"],x.p) +A.bkR=new B.q(A.e,["0 milliard"],x.p) +A.baH=new B.b([3,A.bjQ,6,A.a0f,9,A.bkR,12,A.a0Y],x.o) +A.kw=new B.q(A.e,["0K"],x.p) +A.baI=new B.b([3,A.kw,6,A.p1,9,A.a13,12,A.BU],x.o) +A.bpK=new B.q(A.e,["0 \u0647\u0632\u0627\u0631"],x.p) +A.blx=new B.q(A.e,["0 \u0645\u06cc\u0644\u06cc\u0648\u0646"],x.p) +A.bmf=new B.q(A.e,["0 \u0645\u06cc\u0644\u06cc\u0627\u0631\u062f"],x.p) +A.bk1=new B.q(A.e,["0 \u0647\u0632\u0627\u0631\u0645\u06cc\u0644\u06cc\u0627\u0631\u062f"],x.p) +A.baJ=new B.b([3,A.bpK,6,A.blx,9,A.bmf,12,A.bk1],x.o) +A.bnP=new B.q(A.e,["0\xa0t\xa0\xa4"],x.p) +A.bsr=new B.q(A.e,["0\xa0mia.\xa0\xa4"],x.p) +A.bsh=new B.q(A.e,["0\xa0bio.\xa0\xa4"],x.p) +A.baK=new B.b([3,A.bnP,6,A.a0M,9,A.bsr,12,A.bsh],x.o) +A.buf=new B.q(A.dm,["0 \u0445\u0438\u0459\u0430\u0434\u0435","0 \u0445\u0438\u0459\u0430\u0434\u0430","0 \u0445\u0438\u0459\u0430\u0434\u0430"],x.p) +A.bum=new B.q(A.dm,["0 \u043c\u0438\u043b\u0438\u043e\u043d\u0430","0 \u043c\u0438\u043b\u0438\u043e\u043d","0 \u043c\u0438\u043b\u0438\u043e\u043d\u0430"],x.p) +A.buk=new B.q(A.dm,["0 \u043c\u0438\u043b\u0438\u0458\u0430\u0440\u0434\u0435","0 \u043c\u0438\u043b\u0438\u0458\u0430\u0440\u0434\u0430","0 \u043c\u0438\u043b\u0438\u0458\u0430\u0440\u0434\u0438"],x.p) +A.bua=new B.q(A.dm,["0 \u0431\u0438\u043b\u0438\u043e\u043d\u0430","0 \u0431\u0438\u043b\u0438\u043e\u043d","0 \u0431\u0438\u043b\u0438\u043e\u043d\u0430"],x.p) +A.baL=new B.b([3,A.buf,6,A.bum,9,A.buk,12,A.bua],x.o) +A.bma=new B.q(A.e,["0\xa0\u043c\u044b\u04a3\xa0\xa4"],x.p) +A.baM=new B.b([3,A.bma,6,A.uC,9,A.BQ,12,A.uN],x.o) +A.bow=new B.q(A.e,["0\xa0\u0647\u0632\u0627\u0631"],x.p) +A.bsq=new B.q(A.e,["0\xa0\u0645\u06cc\u0644\u06cc\u0648\u0646"],x.p) +A.bkG=new B.q(A.e,["0\xa0\u0645\u06cc\u0644\u06cc\u0627\u0631\u062f"],x.p) +A.bpy=new B.q(A.e,["0\xa0\u062a\u0631\u06cc\u0644\u06cc\u0648\u0646"],x.p) +A.baN=new B.b([3,A.bow,6,A.bsq,9,A.bkG,12,A.bpy],x.o) +A.brC=new B.q(A.e,["0\xa0\xfe.\xa0\xa4"],x.p) +A.bt3=new B.q(A.e,["0\xa0m.\xa0\xa4"],x.p) +A.boO=new B.q(A.e,["0\xa0ma.\xa0\xa4"],x.p) +A.a0y=new B.q(A.e,["0\xa0bn\xa0\xa4"],x.p) +A.baO=new B.b([3,A.brC,6,A.bt3,9,A.boO,12,A.a0y],x.o) +A.bph=new B.q(A.e,["0\xa0\u0442\u044b\u0441."],x.p) +A.ZY=new B.b([3,A.bph,6,A.uQ,9,A.BJ,12,A.uK],x.o) +A.a0L=new B.q(A.e,["0\xa0mln."],x.p) +A.btv=new B.q(A.e,["0\xa0bln."],x.p) +A.baP=new B.b([3,A.kw,6,A.a0L,9,A.C_,12,A.btv],x.o) +A.bjX=new B.q(A.e,["0\xa0ming\xa0\xa4"],x.p) +A.btp=new B.q(A.e,["0\xa0mlrd\xa0\xa4"],x.p) +A.baQ=new B.b([3,A.bjX,6,A.uM,9,A.btp,12,A.a0Z],x.o) +A.bm3=new B.q(A.e,["0\xa0\u0442\u0438\u0441."],x.p) +A.baR=new B.b([3,A.bm3,6,A.uQ,9,A.BJ,12,A.uK],x.o) +A.bkl=new B.q(A.e,["\u200f0 \u05d0\u05dc\u05e3"],x.p) +A.bpF=new B.q(A.e,["\u200f0 \u05de\u05d9\u05dc\u05d9\u05d5\u05df"],x.p) +A.bkb=new B.q(A.e,["\u200f0 \u05de\u05d9\u05dc\u05d9\u05d0\u05e8\u05d3"],x.p) +A.bl0=new B.q(A.e,["\u200f0 \u05d8\u05e8\u05d9\u05dc\u05d9\u05d5\u05df"],x.p) +A.ZZ=new B.b([3,A.bkl,6,A.bpF,9,A.bkb,12,A.bl0],x.o) +A.blh=new B.q(A.e,["0\xa0\u0570\u0566\u0580"],x.p) +A.bmh=new B.q(A.e,["0\xa0\u0574\u056c\u0576"],x.p) +A.blo=new B.q(A.e,["0\xa0\u0574\u056c\u0580\u0564"],x.p) +A.bln=new B.q(A.e,["0\xa0\u057f\u0580\u056c\u0576"],x.p) +A.baS=new B.b([3,A.blh,6,A.bmh,9,A.blo,12,A.bln],x.o) +A.bo4=new B.q(A.e,["0\xa0Mn\xa0\xa4"],x.p) +A.btu=new B.q(A.e,["0\xa0Mr\xa0\xa4"],x.p) +A.bqy=new B.q(A.e,["0\xa0Tn\xa0\xa4"],x.p) +A.baT=new B.b([3,A.uJ,6,A.bo4,9,A.btu,12,A.bqy],x.o) +A.bt7=new B.q(A.e,["\xa4\xa00k"],x.p) +A.bmX=new B.q(A.e,["\xa4\xa00\xa0mill."],x.p) +A.bnn=new B.q(A.e,["\xa4\xa00\xa0mrd."],x.p) +A.bod=new B.q(A.e,["\xa4\xa00\xa0bill."],x.p) +A.Bl=new B.b([3,A.bt7,6,A.bmX,9,A.bnn,12,A.bod],x.o) +A.d7=new B.b([3,A.p0,6,A.BX,9,A.uE,12,A.p4],x.o) +A.blF=new B.q(A.e,["0\xa0\u0442\u044b\u0441.\xa0\xa4"],x.p) +A.a__=new B.b([3,A.blF,6,A.uC,9,A.BQ,12,A.uN],x.o) +A.a14=new B.q(A.e,["0\xa0t\u016bkst."],x.p) +A.a1_=new B.q(A.e,["0\xa0milj."],x.p) +A.bou=new B.q(A.e,["0\xa0mljrd."],x.p) +A.bmj=new B.q(A.e,["0\xa0trilj."],x.p) +A.baU=new B.b([3,A.a14,6,A.a1_,9,A.bou,12,A.bmj],x.o) +A.bro=new B.q(A.e,["0\xa0rb"],x.p) +A.brw=new B.q(A.e,["0\xa0jt"],x.p) +A.a_0=new B.b([3,A.bro,6,A.brw,9,A.fx,12,A.BP],x.o) +A.h5={few:0,many:1,one:2,other:3,two:4} +A.aYq=new B.q(A.h5,["0 miliad","0 a viliado\xf9","0 miliad","0 miliad","0 viliad"],x.p) +A.aYl=new B.q(A.h5,["0 milion","0 a v/miliono\xf9","0 milion","0 milion","0 v/milion"],x.p) +A.aYm=new B.q(A.h5,["0 miliard","0 a viliardo\xf9","0 miliard","0 miliard","0 viliard"],x.p) +A.aYk=new B.q(A.h5,["0 bilion","0 a v/biliono\xf9","0 bilion","0 bilion","0 v/bilion"],x.p) +A.baV=new B.b([3,A.aYq,6,A.aYl,9,A.aYm,12,A.aYk],x.o) +A.boL=new B.q(A.e,["0 \u0b86\u0baf\u0bbf\u0bb0\u0bae\u0bcd"],x.p) +A.bkD=new B.q(A.e,["0 \u0bae\u0bbf\u0bb2\u0bcd\u0bb2\u0bbf\u0baf\u0ba9\u0bcd"],x.p) +A.bkm=new B.q(A.e,["0 \u0baa\u0bbf\u0bb2\u0bcd\u0bb2\u0bbf\u0baf\u0ba9\u0bcd"],x.p) +A.bk6=new B.q(A.e,["0 \u0b9f\u0bbf\u0bb0\u0bbf\u0bb2\u0bcd\u0bb2\u0bbf\u0baf\u0ba9\u0bcd"],x.p) +A.baW=new B.b([3,A.boL,6,A.bkD,9,A.bkm,12,A.bk6],x.o) +A.bli=new B.q(A.e,["0\xa0\u043c\u0438\u04a3"],x.p) +A.bl6=new B.q(A.e,["0\xa0\u043c\u043b\u0434"],x.p) +A.baX=new B.b([3,A.bli,6,A.uQ,9,A.bl6,12,A.uK],x.o) +A.bmw=new B.q(A.e,["0\xa0N\xa0\xa4"],x.p) +A.brP=new B.q(A.e,["0\xa0Tr\xa0\xa4"],x.p) +A.bp2=new B.q(A.e,["0\xa0T\xa0\xa4"],x.p) +A.blB=new B.q(A.e,["0\xa0NT\xa0\xa4"],x.p) +A.baY=new B.b([3,A.bmw,6,A.brP,9,A.bp2,12,A.blB],x.o) +A.bkk=new B.q(A.e,["\xa4\xa00\xa0mil"],x.p) +A.bsA=new B.q(A.e,["\xa4\xa00\xa0mi"],x.p) +A.boV=new B.q(A.e,["\xa4\xa00\xa0bi"],x.p) +A.bka=new B.q(A.e,["\xa4\xa00\xa0tri"],x.p) +A.a_1=new B.b([3,A.bkk,6,A.bsA,9,A.boV,12,A.bka],x.o) +A.bki=new B.q(A.e,["0 inkulungwane"],x.p) +A.bkL=new B.q(A.e,["0 isigidi"],x.p) +A.blt=new B.q(A.e,["0 isigidi sezigidi"],x.p) +A.bkx=new B.q(A.e,["0 isigidintathu"],x.p) +A.baZ=new B.b([3,A.bki,6,A.bkL,9,A.blt,12,A.bkx],x.o) +A.bsF=new B.q(A.e,["0\xa0\u0445\u0438\u043b.\xa0\xa4"],x.p) +A.btx=new B.q(A.e,["0\xa0\u043c\u043b\u043d.\xa0\xa4"],x.p) +A.a0j=new B.q(A.e,["0\xa0\u043c\u043b\u0440\u0434.\xa0\xa4"],x.p) +A.bqT=new B.q(A.e,["0\xa0\u0442\u0440\u043b\u043d.\xa0\xa4"],x.p) +A.bb_=new B.b([3,A.bsF,6,A.btx,9,A.a0j,12,A.bqT],x.o) +A.bjV=new B.q(A.e,["0J"],x.p) +A.bb0=new B.b([3,A.kw,6,A.bjV,9,A.uH,12,A.mm],x.o) +A.aVX=new B.q(A.aX,["0 t\u016bkstan\u010diai","0 t\u016bkstan\u010dio","0 t\u016bkstantis","0 t\u016bkstan\u010di\u0173"],x.p) +A.aWa=new B.q(A.aX,["0 milijonai","0 milijono","0 milijonas","0 milijon\u0173"],x.p) +A.aWc=new B.q(A.aX,["0 milijardai","0 milijardo","0 milijardas","0 milijard\u0173"],x.p) +A.aVS=new B.q(A.aX,["0 trilijonai","0 trilijono","0 trilijonas","0 trilijon\u0173"],x.p) +A.bb1=new B.b([3,A.aVX,6,A.aWa,9,A.aWc,12,A.aVS],x.o) +A.bqQ=new B.q(A.e,["0\xa0t.\xa0\xa4"],x.p) +A.a1b=new B.q(A.e,["0\xa0milj.\xa0\xa4"],x.p) +A.bmt=new B.q(A.e,["0\xa0bilj.\xa0\xa4"],x.p) +A.bb2=new B.b([3,A.bqQ,6,A.a1b,9,A.a0U,12,A.bmt],x.o) +A.a_2=new B.b([3,A.BV,6,A.uG,9,A.C_,12,A.uL],x.o) +A.blq=new B.q(A.e,["0\xa0mM\xa0\xa4"],x.p) +A.bb3=new B.b([3,A.a0l,6,A.uI,9,A.blq,12,A.uJ],x.o) +A.bsL=new B.q(A.e,["0\xa0mij\xeb\xa0\xa4"],x.p) +A.bb4=new B.b([3,A.bsL,6,A.uM,9,A.C0,12,A.a0k],x.o) +A.aW_=new B.q(A.aX,["0 \u0442\u044b\u0441\u044f\u0447\u044b","0 \u0442\u044b\u0441\u044f\u0447","0 \u0442\u044b\u0441\u044f\u0447\u0430","0 \u0442\u044b\u0441\u044f\u0447\u044b"],x.p) +A.aWl=new B.q(A.aX,["0 \u043c\u0456\u043b\u044c\u0451\u043d\u044b","0 \u043c\u0456\u043b\u044c\u0451\u043d\u0430\u045e","0 \u043c\u0456\u043b\u044c\u0451\u043d","0 \u043c\u0456\u043b\u044c\u0451\u043d\u0430"],x.p) +A.aW5=new B.q(A.aX,["0 \u043c\u0456\u043b\u044c\u044f\u0440\u0434\u044b","0 \u043c\u0456\u043b\u044c\u044f\u0440\u0434\u0430\u045e","0 \u043c\u0456\u043b\u044c\u044f\u0440\u0434","0 \u043c\u0456\u043b\u044c\u044f\u0440\u0434\u0430"],x.p) +A.aWo=new B.q(A.aX,["0 \u0442\u0440\u044b\u043b\u044c\u0451\u043d\u044b","0 \u0442\u0440\u044b\u043b\u044c\u0451\u043d\u0430\u045e","0 \u0442\u0440\u044b\u043b\u044c\u0451\u043d","0 \u0442\u0440\u044b\u043b\u044c\u0451\u043d\u0430"],x.p) +A.bb5=new B.b([3,A.aW_,6,A.aWl,9,A.aW5,12,A.aWo],x.o) +A.btq=new B.q(A.e,["0\xa0mlr.\xa0\xa4"],x.p) +A.bb6=new B.b([3,A.BL,6,A.uF,9,A.btq,12,A.uD],x.o) +A.bs7=new B.q(A.e,["\xa40\xa0\u0e9e\u0eb1\u0e99"],x.p) +A.bke=new B.q(A.e,["\xa40\xa0\u0ea5\u0ec9\u0eb2\u0e99"],x.p) +A.bqa=new B.q(A.e,["\xa40\xa0\u0e95\u0eb7\u0ec9"],x.p) +A.bt9=new B.q(A.e,["\xa40\xa0\u0ea5\u0ec9\u0eb2\u0e99\u0ea5\u0ec9\u0eb2\u0e99"],x.p) +A.bb7=new B.b([3,A.bs7,6,A.bke,9,A.bqa,12,A.bt9],x.o) +A.a0m=new B.q(A.e,["0\xa0t\u016bkst.\xa0\xa4"],x.p) +A.bsb=new B.q(A.e,["0\xa0mln.\xa0\xa4"],x.p) +A.bmZ=new B.q(A.e,["0\xa0trln.\xa0\xa4"],x.p) +A.bb8=new B.b([3,A.a0m,6,A.bsb,9,A.a16,12,A.bmZ],x.o) +A.bm5=new B.q(A.e,["0 \u043c\u0438\u04a3"],x.p) +A.bb9=new B.b([3,A.bm5,6,A.a1f,9,A.a1c,12,A.a0H],x.o) +A.a_3=new B.b([3,A.BL,6,A.uF,9,A.a0p,12,A.uD],x.o) +A.bl_=new B.q(A.e,["0 bin"],x.p) +A.bl2=new B.q(A.e,["0 milyar"],x.p) +A.bba=new B.b([3,A.bl_,6,A.a0r,9,A.bl2,12,A.a17],x.o) +A.boR=new B.q(A.e,["0\xa0mljrd.\xa0\xa4"],x.p) +A.bk2=new B.q(A.e,["0\xa0trilj.\xa0\xa4"],x.p) +A.bbb=new B.b([3,A.a0m,6,A.a1b,9,A.boR,12,A.bk2],x.o) +A.bkB=new B.q(A.e,["0\xa0tuh"],x.p) +A.bbc=new B.b([3,A.bkB,6,A.p1,9,A.BY,12,A.BU],x.o) +A.aVR=new B.q(A.aX,["0 tysi\u0105ce","0 tysi\u0119cy","0 tysi\u0105c","0 tysi\u0105ca"],x.p) +A.aW4=new B.q(A.aX,["0 miliony","0 milion\xf3w","0 milion","0 miliona"],x.p) +A.aW2=new B.q(A.aX,["0 miliardy","0 miliard\xf3w","0 miliard","0 miliarda"],x.p) +A.aWk=new B.q(A.aX,["0 biliony","0 bilion\xf3w","0 bilion","0 biliona"],x.p) +A.bbd=new B.b([3,A.aVR,6,A.aW4,9,A.aW2,12,A.aWk],x.o) +A.bmC=new B.q(A.e,["\xa40\xa0\u1796\u17b6\u1793\u17cb"],x.p) +A.bp8=new B.q(A.e,["\xa40\xa0\u179b\u17b6\u1793"],x.p) +A.bms=new B.q(A.e,["\xa40\xa0\u1794\u17ca\u17b8\u179b\u17b6\u1793"],x.p) +A.boM=new B.q(A.e,["\xa40\xa0\u1791\u17d2\u179a\u17b8\u179b\u17b6\u1793"],x.p) +A.bbe=new B.b([3,A.bmC,6,A.bp8,9,A.bms,12,A.boM],x.o) +A.brl=new B.q(A.e,["0\xa0Tsg."],x.p) +A.a0s=new B.q(A.e,["0\xa0Mio."],x.p) +A.a0V=new B.q(A.e,["0\xa0Mrd."],x.p) +A.a0w=new B.q(A.e,["0\xa0Bio."],x.p) +A.bbf=new B.b([3,A.brl,6,A.a0s,9,A.a0V,12,A.a0w],x.o) +A.bpS=new B.q(A.e,["\xa4\xa00\xa0mln."],x.p) +A.bno=new B.q(A.e,["\xa4\xa00\xa0mld."],x.p) +A.bsQ=new B.q(A.e,["\xa4\xa00\xa0bln."],x.p) +A.bbg=new B.b([3,A.a0h,6,A.bpS,9,A.bno,12,A.bsQ],x.o) +A.bnA=new B.q(A.e,["\xa40\u0cb8\u0cbe"],x.p) +A.bpq=new B.q(A.e,["\xa40\u0cae\u0cbf"],x.p) +A.btg=new B.q(A.e,["\xa40\u0cac\u0cbf"],x.p) +A.bqC=new B.q(A.e,["\xa40\u0c9f\u0ccd\u0cb0\u0cbf"],x.p) +A.bbh=new B.b([3,A.bnA,6,A.bpq,9,A.btg,12,A.bqC],x.o) +A.bso=new B.q(A.e,["0\xa0E\xa0\xa4"],x.p) +A.bsU=new B.q(A.e,["0\xa0Mrd\xa0\xa4"],x.p) +A.bbi=new B.b([3,A.bso,6,A.uI,9,A.bsU,12,A.uJ],x.o) +A.bsJ=new B.q(A.e,["0 \u0570\u0561\u0566\u0561\u0580"],x.p) +A.blU=new B.q(A.e,["0 \u0574\u056b\u056c\u056b\u0578\u0576"],x.p) +A.blH=new B.q(A.e,["0 \u0574\u056b\u056c\u056b\u0561\u0580\u0564"],x.p) +A.bmG=new B.q(A.e,["0 \u057f\u0580\u056b\u056c\u056b\u0578\u0576"],x.p) +A.bbj=new B.b([3,A.bsJ,6,A.blU,9,A.blH,12,A.bmG],x.o) +A.bbk=new B.b([3,A.BT,6,A.p2,9,A.BN,12,A.mm],x.o) +A.bpv=new B.q(A.e,["0\xa0G"],x.p) +A.bbl=new B.b([3,A.p5,6,A.fx,9,A.bpv,12,A.BP],x.o) +A.brI=new B.q(A.e,["0\xa0k\xa0\xa4"],x.p) +A.bkU=new B.q(A.e,["0\xa0Md\xa0\xa4"],x.p) +A.bqn=new B.q(A.e,["0\xa0Bn\xa0\xa4"],x.p) +A.a_4=new B.b([3,A.brI,6,A.uI,9,A.bkU,12,A.bqn],x.o) +A.bpf=new B.q(A.e,["0\u0b39"],x.p) +A.bsj=new B.q(A.e,["0\u0b28\u0b3f"],x.p) +A.bo7=new B.q(A.e,["0\u0b2c\u0b3f"],x.p) +A.bsd=new B.q(A.e,["0\u0b1f\u0b4d\u0b30\u0b3f"],x.p) +A.bbm=new B.b([3,A.bpf,6,A.bsj,9,A.bo7,12,A.bsd],x.o) +A.bmW=new B.q(A.e,["\xa40\xa0rb"],x.p) +A.br_=new B.q(A.e,["\xa40\xa0jt"],x.p) +A.a_5=new B.b([3,A.bmW,6,A.br_,9,A.BS,12,A.a0x],x.o) +A.bu8=new B.q(A.dm,["0 hiljade","0 hiljada","0 hiljada"],x.p) +A.bub=new B.q(A.dm,["0 miliona","0 milion","0 miliona"],x.p) +A.buh=new B.q(A.dm,["0 biliona","0 bilion","0 biliona"],x.p) +A.a_6=new B.b([3,A.bu8,6,A.bub,9,A.a1g,12,A.buh],x.o) +A.blg=new B.q(A.e,["0\xa0\u0570\u0566\u0580\xa0\xa4"],x.p) +A.bkn=new B.q(A.e,["0\xa0\u0574\u056c\u0576\xa0\xa4"],x.p) +A.bkY=new B.q(A.e,["0\xa0\u0574\u056c\u0580\u0564\xa0\xa4"],x.p) +A.bta=new B.q(A.e,["0\xa0\u057f\u0580\u056c\u0576\xa0\xa4"],x.p) +A.bbn=new B.b([3,A.blg,6,A.bkn,9,A.bkY,12,A.bta],x.o) +A.bqZ=new B.q(A.e,["0\xa0mi"],x.p) +A.bqP=new B.q(A.e,["0\xa0bi"],x.p) +A.brN=new B.q(A.e,["0\xa0tri"],x.p) +A.a_7=new B.b([3,A.BW,6,A.bqZ,9,A.bqP,12,A.brN],x.o) +A.boz=new B.q(A.e,["0 Tuusig"],x.p) +A.bwI=new B.q(A.a3,["0 Millioon","0 Millioone"],x.p) +A.blC=new B.q(A.e,["0 Milliarde"],x.p) +A.bwC=new B.q(A.a3,["0 Billioon","0 Billioone"],x.p) +A.bbo=new B.b([3,A.boz,6,A.bwI,9,A.blC,12,A.bwC],x.o) +A.bqz=new B.q(A.e,["0\xa0trln."],x.p) +A.bbp=new B.b([3,A.a14,6,A.a0L,9,A.a0F,12,A.bqz],x.o) +A.boh=new B.q(A.e,["0\u0cb8\u0cbe"],x.p) +A.bsw=new B.q(A.e,["0\u0cae\u0cbf"],x.p) +A.blI=new B.q(A.e,["0\u0cac\u0cbf"],x.p) +A.bnm=new B.q(A.e,["0\u0c9f\u0ccd\u0cb0\u0cbf"],x.p) +A.bbq=new B.b([3,A.boh,6,A.bsw,9,A.blI,12,A.bnm],x.o) +A.bo2=new B.q(A.e,["0 \u123a"],x.p) +A.bo9=new B.q(A.e,["0 \u121a\u120a\u12ee\u1295"],x.p) +A.bjT=new B.q(A.e,["0 \u1262\u120a\u12ee\u1295"],x.p) +A.bm0=new B.q(A.e,["0 \u1275\u122a\u120a\u12ee\u1295"],x.p) +A.bbr=new B.b([3,A.bo2,6,A.bo9,9,A.bjT,12,A.bm0],x.o) +A.bkW=new B.q(A.e,["0 \u0cb8\u0cbe\u0cb5\u0cbf\u0cb0"],x.p) +A.bkz=new B.q(A.e,["0 \u0cae\u0cbf\u0cb2\u0cbf\u0caf\u0ca8\u0ccd"],x.p) +A.bkf=new B.q(A.e,["0 \u0cac\u0cbf\u0cb2\u0cbf\u0caf\u0ca8\u0ccd"],x.p) +A.brB=new B.q(A.e,["0 \u0c9f\u0ccd\u0cb0\u0cbf\u0cb2\u0cbf\u0caf\u0ca8\u0ccd\u200c"],x.p) +A.bbs=new B.b([3,A.bkW,6,A.bkz,9,A.bkf,12,A.brB],x.o) +A.boW=new B.q(A.e,["0\xa0\u0445\u0438\u043b."],x.p) +A.btm=new B.q(A.e,["0\xa0\u043c\u043b\u043d."],x.p) +A.bpE=new B.q(A.e,["0\xa0\u0442\u0440\u043b\u043d."],x.p) +A.bbt=new B.b([3,A.boW,6,A.btm,9,A.a0B,12,A.bpE],x.o) +A.brJ=new B.q(A.e,["0\xa0mill."],x.p) +A.BK=new B.q(A.e,["0\xa0mrd."],x.p) +A.bkI=new B.q(A.e,["0\xa0bill."],x.p) +A.Bm=new B.b([3,A.BT,6,A.brJ,9,A.BK,12,A.bkI],x.o) +A.bmi=new B.q(A.e,["0\xa0G\xa4"],x.p) +A.bkh=new B.q(A.e,["0\xa0T\xa4"],x.p) +A.a_8=new B.b([3,A.BM,6,A.p3,9,A.bmi,12,A.bkh],x.o) +A.btK=new B.q(A.e,["0\xa0\xfe."],x.p) +A.bs1=new B.q(A.e,["0\xa0m."],x.p) +A.btH=new B.q(A.e,["0\xa0ma."],x.p) +A.bbu=new B.b([3,A.btK,6,A.bs1,9,A.btH,12,A.BZ],x.o) +A.buj=new B.q(A.dm,["0 mii","0 mie","0 de mii"],x.p) +A.bue=new B.q(A.dm,["0 milioane","0 milion","0 de milioane"],x.p) +A.bul=new B.q(A.dm,["0 miliarde","0 miliard","0 de miliarde"],x.p) +A.bud=new B.q(A.dm,["0 trilioane","0 trilion","0 de trilioane"],x.p) +A.bbv=new B.b([3,A.buj,6,A.bue,9,A.bul,12,A.bud],x.o) +A.cU=new B.b([3,A.kw,6,A.p2,9,A.uH,12,A.mm],x.o) +A.aVU=new B.q(A.aX,["0 \u0442\u0438\u0441\u044f\u0447\u0456","0 \u0442\u0438\u0441\u044f\u0447","0 \u0442\u0438\u0441\u044f\u0447\u0430","0 \u0442\u0438\u0441\u044f\u0447\u0456"],x.p) +A.aWj=new B.q(A.aX,["0 \u043c\u0456\u043b\u044c\u0439\u043e\u043d\u0438","0 \u043c\u0456\u043b\u044c\u0439\u043e\u043d\u0456\u0432","0 \u043c\u0456\u043b\u044c\u0439\u043e\u043d","0 \u043c\u0456\u043b\u044c\u0439\u043e\u043d\u0430"],x.p) +A.aW6=new B.q(A.aX,["0 \u043c\u0456\u043b\u044c\u044f\u0440\u0434\u0438","0 \u043c\u0456\u043b\u044c\u044f\u0440\u0434\u0456\u0432","0 \u043c\u0456\u043b\u044c\u044f\u0440\u0434","0 \u043c\u0456\u043b\u044c\u044f\u0440\u0434\u0430"],x.p) +A.aWi=new B.q(A.aX,["0 \u0442\u0440\u0438\u043b\u044c\u0439\u043e\u043d\u0438","0 \u0442\u0440\u0438\u043b\u044c\u0439\u043e\u043d\u0456\u0432","0 \u0442\u0440\u0438\u043b\u044c\u0439\u043e\u043d","0 \u0442\u0440\u0438\u043b\u044c\u0439\u043e\u043d\u0430"],x.p) +A.bbw=new B.b([3,A.aVU,6,A.aWj,9,A.aW6,12,A.aWi],x.o) +A.bxg=new B.q(A.a3,["0 libo","0 na libo"],x.p) +A.bwz=new B.q(A.a3,["0 milyon","0 na milyon"],x.p) +A.bxa=new B.q(A.a3,["0 bilyon","0 na bilyon"],x.p) +A.bxo=new B.q(A.a3,["0 trilyon","0 na trilyon"],x.p) +A.a_9=new B.b([3,A.bxg,6,A.bwz,9,A.bxa,12,A.bxo],x.o) +A.bmg=new B.q(A.e,["\u0daf0"],x.p) +A.blS=new B.q(A.e,["\u0db8\u0dd20"],x.p) +A.bpx=new B.q(A.e,["\u0db6\u0dd20"],x.p) +A.bqh=new B.q(A.e,["\u0da7\u0dca\u200d\u0dbb\u0dd20"],x.p) +A.bbx=new B.b([3,A.bmg,6,A.blS,9,A.bpx,12,A.bqh],x.o) +A.bnZ=new B.q(A.e,["0\xa0\u123a"],x.p) +A.bqN=new B.q(A.e,["0\xa0\u121a"],x.p) +A.bsS=new B.q(A.e,["0\xa0\u1262"],x.p) +A.bqB=new B.q(A.e,["0\xa0\u1275"],x.p) +A.bby=new B.b([3,A.bnZ,6,A.bqN,9,A.bsS,12,A.bqB],x.o) +A.bbz=new B.b([3,A.BV,6,A.a0W,9,A.BK,12,A.uL],x.o) +A.bq3=new B.q(A.e,["0\xa0m"],x.p) +A.bon=new B.q(A.e,["0\xa0mjd"],x.p) +A.bbA=new B.b([3,A.p5,6,A.bq3,9,A.bon,12,A.BZ],x.o) +A.bpB=new B.q(A.e,["0\xa0t."],x.p) +A.bkJ=new B.q(A.e,["0\xa0bilj."],x.p) +A.bbB=new B.b([3,A.bpB,6,A.a1_,9,A.BK,12,A.bkJ],x.o) +A.bo6=new B.q(A.e,["0 \u0b39\u0b1c\u0b3e\u0b30"],x.p) +A.bk8=new B.q(A.e,["0 \u0b28\u0b3f\u0b5f\u0b41\u0b24"],x.p) +A.brZ=new B.q(A.e,["0 \u0b36\u0b39\u0b15\u0b4b\u0b1f\u0b3f"],x.p) +A.bqW=new B.q(A.e,["0 \u0b32\u0b15\u0b4d\u0b37\u0b15\u0b4b\u0b1f\u0b3f"],x.p) +A.bbC=new B.b([3,A.bo6,6,A.bk8,9,A.brZ,12,A.bqW],x.o) +A.bnO=new B.q(A.e,["0\xa0Mn"],x.p) +A.bnQ=new B.q(A.e,["0\xa0Mr"],x.p) +A.brf=new B.q(A.e,["0\xa0Tn"],x.p) +A.bbD=new B.b([3,A.j1,6,A.bnO,9,A.bnQ,12,A.brf],x.o) +A.bs0=new B.q(A.e,["\xa4\u0daf0"],x.p) +A.bnF=new B.q(A.e,["\xa4\u0db8\u0dd20"],x.p) +A.bp5=new B.q(A.e,["\xa4\u0db6\u0dd20"],x.p) +A.bmJ=new B.q(A.e,["\xa4\u0da7\u0dca\u200d\u0dbb\u0dd20"],x.p) +A.bbE=new B.b([3,A.bs0,6,A.bnF,9,A.bp5,12,A.bmJ],x.o) +A.ku=new B.b([3,A.kw,6,A.p2,9,A.BN,12,A.mm],x.o) +A.bwX=new B.q(A.a3,["0 bilh\xe3o","0 bilh\xf5es"],x.p) +A.bww=new B.q(A.a3,["0 trilh\xe3o","0 trilh\xf5es"],x.p) +A.a_a=new B.b([3,A.BH,6,A.a1z,9,A.bwX,12,A.bww],x.o) +A.bml=new B.q(A.e,["0\xa0tn\xa0\xa4"],x.p) +A.brO=new B.q(A.e,["0\xa0mn\xa0\xa4"],x.p) +A.bk3=new B.q(A.e,["0\xa0md\xa0\xa4"],x.p) +A.bbF=new B.b([3,A.bml,6,A.brO,9,A.bk3,12,A.a0y],x.o) +A.bpV=new B.q(A.e,["0\xa0\u0445\u0438\u0459.\xa0\xa4"],x.p) +A.bbG=new B.b([3,A.bpV,6,A.a0u,9,A.a0j,12,A.a0E],x.o) +A.brM=new B.q(A.e,["0K\u200f"],x.p) +A.bo3=new B.q(A.e,["0M\u200f"],x.p) +A.blc=new B.q(A.e,["0B\u200f"],x.p) +A.bpT=new B.q(A.e,["0T\u200f"],x.p) +A.a_b=new B.b([3,A.brM,6,A.bo3,9,A.blc,12,A.bpT],x.o) +A.bqR=new B.q(A.e,["0\xa0\u03c7\u03b9\u03bb."],x.p) +A.boG=new B.q(A.e,["0\xa0\u03b5\u03ba."],x.p) +A.btn=new B.q(A.e,["0\xa0\u03b4\u03b9\u03c3."],x.p) +A.bpL=new B.q(A.e,["0\xa0\u03c4\u03c1\u03b9\u03c3."],x.p) +A.bbH=new B.b([3,A.bqR,6,A.boG,9,A.btn,12,A.bpL],x.o) +A.bng=new B.q(A.e,["0 \xfe\xfasund"],x.p) +A.bws=new B.q(A.a3,["0 millj\xf3n","0 millj\xf3nir"],x.p) +A.bxq=new B.q(A.a3,["0 milljar\xf0ur","0 milljar\xf0ar"],x.p) +A.bwB=new B.q(A.a3,["0 billj\xf3n","0 billj\xf3nir"],x.p) +A.bbI=new B.b([3,A.bng,6,A.bws,9,A.bxq,12,A.bwB],x.o) +A.bmu=new B.q(A.e,["00\xa0k"],x.p) +A.bbM=new B.b([3,A.BI,4,A.bmu,6,A.fx,10,A.uO,12,A.j1],x.o) +A.Bn=new B.b([3,A.bC,4,A.bC,5,A.bC,6,A.a0s,9,A.a0V,12,A.a0w],x.o) +A.bm9=new B.q(A.e,["0\xa0Mln\xa0\xa4"],x.p) +A.brW=new B.q(A.e,["0\xa0Mld\xa0\xa4"],x.p) +A.bmR=new B.q(A.e,["0\xa0Bln\xa0\xa4"],x.p) +A.a_e=new B.b([3,A.bC,4,A.bC,5,A.bC,6,A.bm9,9,A.brW,12,A.bmR],x.o) +A.Bo=new B.b([3,A.bC,4,A.bC,5,A.bC,6,A.a19,9,A.a1e,12,A.a10],x.o) +A.bnI=new B.q(A.e,["0\xa0Mln"],x.p) +A.bsz=new B.q(A.e,["0\xa0Mld"],x.p) +A.bsB=new B.q(A.e,["0\xa0Bln"],x.p) +A.a_f=new B.b([3,A.bC,4,A.bC,5,A.bC,6,A.bnI,9,A.bsz,12,A.bsB],x.o) +A.bmc=new B.q(A.e,["0\xa0\u09b9\u09be\u099c\u09be\u09f0"],x.p) +A.bmA=new B.q(A.e,["0\xa0\u09b2\u09be\u0996"],x.p) +A.bsW=new B.q(A.e,["0\xa0\u09a8\u09bf\u09af\u09c1\u09a4"],x.p) +A.bnE=new B.q(A.e,["000\xa0\u09a8\u09bf\u0983"],x.p) +A.bmN=new B.q(A.e,["0\xa0\u09b6\u0983\xa0\u0995\u09cb\u0983"],x.p) +A.brc=new B.q(A.e,["000\xa0\u09b6\u0983\xa0\u0995\u0983"],x.p) +A.bsa=new B.q(A.e,["0\xa0\u09b6\u0983\xa0\u09aa\u0983"],x.p) +A.bdn=new B.b([3,A.bmc,5,A.bmA,6,A.bsW,8,A.bnE,9,A.bmN,11,A.brc,12,A.bsa],x.o) +A.bv3=new B.q(A.p9,["0 \u0622\u0644\u0627\u0641","0 \u0623\u0644\u0641","0 \u0623\u0644\u0641","0 \u0623\u0644\u0641","0 \u0623\u0644\u0641","0 \u0623\u0644\u0641"],x.p) +A.btD=new B.q(A.e,["00 \u0623\u0644\u0641"],x.p) +A.bv2=new B.q(A.p9,["0 \u0645\u0644\u0627\u064a\u064a\u0646","0 \u0645\u0644\u064a\u0648\u0646","0 \u0645\u0644\u064a\u0648\u0646","0 \u0645\u0644\u064a\u0648\u0646","0 \u0645\u0644\u064a\u0648\u0646","0 \u0645\u0644\u064a\u0648\u0646"],x.p) +A.bqU=new B.q(A.e,["000 \u0645\u0644\u064a\u0648\u0646"],x.p) +A.bmS=new B.q(A.e,["0 \u0645\u0644\u064a\u0627\u0631"],x.p) +A.bq9=new B.q(A.e,["0 \u062a\u0631\u0644\u064a\u0648\u0646"],x.p) +A.Bp=new B.b([3,A.bv3,4,A.btD,6,A.bv2,8,A.bqU,9,A.bmS,12,A.bq9],x.o) +A.bmn=new B.q(A.e,["0 milioi"],x.p) +A.bp4=new B.q(A.e,["0 bilioi"],x.p) +A.bdB=new B.b([6,A.bmn,12,A.bp4],x.o) +A.bkN=new B.q(A.e,["\xa40\xa0k"],x.p) +A.bqf=new B.q(A.e,["\xa40\xa0m"],x.p) +A.bnz=new B.q(A.e,["\xa40\xa0mjd"],x.p) +A.bob=new B.q(A.e,["\xa40\xa0bn"],x.p) +A.bxk=new B.q(A.a3,["\xa4000\xa0bn","\xa4\xa0000\xa0bn"],x.p) +A.bfc=new B.b([3,A.bkN,6,A.bqf,9,A.bnz,12,A.bob,14,A.bxk],x.o) +A.bv0=new B.q(A.p9,["0K","0K","0 mil","0 mil","0K","0 mil"],x.p) +A.buY=new B.q(A.p9,["00K","00K","00 mil","00 mil","00K","00K"],x.p) +A.boo=new B.q(A.e,["0 miliwn"],x.p) +A.btB=new B.q(A.e,["0 biliwn"],x.p) +A.bnW=new B.q(A.e,["0 triliwn"],x.p) +A.bwS=new B.q(A.a3,["000T","000 triliwn"],x.p) +A.bjx=new B.b([3,A.bv0,4,A.buY,6,A.boo,9,A.btB,12,A.bnW,14,A.bwS],x.o) +A.aYr=new B.q(A.h5,["0 mh\xedle","0 m\xedle","0 mh\xedle","0 m\xedle","0 mh\xedle"],x.p) +A.brq=new B.q(A.e,["00 m\xedle"],x.p) +A.aYs=new B.q(A.h5,["0 mhilli\xfan","0 milli\xfan","0 mhilli\xfan","0 milli\xfan","0 mhilli\xfan"],x.p) +A.bmd=new B.q(A.e,["00 milli\xfan"],x.p) +A.aYj=new B.q(A.h5,["0 bhilli\xfan","0 mbilli\xfan","0 bhilli\xfan","0 billi\xfan","0 bhilli\xfan"],x.p) +A.aYn=new B.q(A.h5,["00 billi\xfan","00 mbilli\xfan","00 billi\xfan","00 billi\xfan","00 billi\xfan"],x.p) +A.br0=new B.q(A.e,["000 billi\xfan"],x.p) +A.aYp=new B.q(A.h5,["0 thrilli\xfan","0 dtrilli\xfan","0 trilli\xfan","0 trilli\xfan","0 thrilli\xfan"],x.p) +A.aYo=new B.q(A.h5,["00 trilli\xfan","00 dtrilli\xfan","00 trilli\xfan","00 trilli\xfan","00 trilli\xfan"],x.p) +A.bmP=new B.q(A.e,["000 trilli\xfan"],x.p) +A.bjL=new B.b([3,A.aYr,4,A.brq,6,A.aYs,7,A.bmd,9,A.aYj,10,A.aYn,11,A.br0,12,A.aYp,13,A.aYo,14,A.bmP],x.o) +A.bjU=new B.q(A.e,["0L"],x.p) +A.bkH=new B.q(A.e,["0Cr"],x.p) +A.blM=new B.q(A.e,["0KCr"],x.p) +A.boJ=new B.q(A.e,["0LCr"],x.p) +A.bu0=new B.b([3,A.kw,5,A.bjU,7,A.bkH,10,A.blM,12,A.boJ],x.o) +A.bmz=new B.q(A.e,["\xa40L"],x.p) +A.bn9=new B.q(A.e,["\xa40Cr"],x.p) +A.blY=new B.q(A.e,["\xa40KCr"],x.p) +A.blA=new B.q(A.e,["\xa40LCr"],x.p) +A.bu1=new B.b([3,A.p0,5,A.bmz,7,A.bn9,10,A.blY,12,A.blA],x.o) +A.a0K=new B.q(A.e,["0 \u0939\u091c\u093e\u0930"],x.p) +A.btA=new B.q(A.e,["0 \u0915\u0930\u094b\u0921"],x.p) +A.brh=new B.q(A.e,["00 \u0916\u0930\u092c"],x.p) +A.bqF=new B.q(A.e,["0 \u0936\u0902\u0916"],x.p) +A.bu2=new B.b([3,A.a0K,5,A.BO,6,A.btA,9,A.a0A,12,A.brh,13,A.bqF],x.o) +A.bmo=new B.q(A.e,["\xa4\xa00\xa0\u06c1\u0632\u0627\u0631"],x.p) +A.boF=new B.q(A.e,["\xa4\xa00\xa0\u0644\u0627\u06a9\u06be"],x.p) +A.bjS=new B.q(A.e,["\xa4\xa00\xa0\u06a9\u0631\u0648\u0691"],x.p) +A.bqm=new B.q(A.e,["\xa4\xa00\xa0\u0627\u0631\u0628"],x.p) +A.bss=new B.q(A.e,["\xa4\xa00\xa0\u06a9\u06be\u0631\u0628"],x.p) +A.bmQ=new B.q(A.e,["\xa40\xa0\u0679\u0631\u06cc\u0644\u06cc\u0646"],x.p) +A.blT=new B.q(A.e,["\xa4\xa000\xa0\u0679\u0631\u06cc\u0644\u06cc\u0646"],x.p) +A.bv9=new B.b([3,A.bmo,5,A.boF,7,A.bjS,9,A.bqm,11,A.bss,12,A.bmQ,13,A.blT],x.o) +A.bkV=new B.q(A.e,["0 Tausend"],x.p) +A.bwR=new B.q(A.a3,["0 Million","0 Millionen"],x.p) +A.bm_=new B.q(A.e,["00 Millionen"],x.p) +A.bwM=new B.q(A.a3,["0 Milliarde","0 Milliarden"],x.p) +A.bmF=new B.q(A.e,["00 Milliarden"],x.p) +A.bwu=new B.q(A.a3,["0 Billion","0 Billionen"],x.p) +A.bqw=new B.q(A.e,["00 Billionen"],x.p) +A.C3=new B.b([3,A.bkV,6,A.bwR,7,A.bm_,9,A.bwM,10,A.bmF,12,A.bwu,13,A.bqw],x.o) +A.a1y=new B.q(A.a3,["0 million","0 millioner"],x.p) +A.a0t=new B.q(A.e,["00 millioner"],x.p) +A.a1C=new B.q(A.a3,["0 milliard","0 milliarder"],x.p) +A.a0D=new B.q(A.e,["00 milliarder"],x.p) +A.a1x=new B.q(A.a3,["0 billion","0 billioner"],x.p) +A.a1a=new B.q(A.e,["00 billioner"],x.p) +A.C4=new B.b([3,A.a0P,6,A.a1y,7,A.a0t,9,A.a1C,10,A.a0D,12,A.a1x,13,A.a1a],x.o) +A.bpi=new B.q(A.e,["0 tuhat"],x.p) +A.bx9=new B.q(A.a3,["0 miljon","0 miljonit"],x.p) +A.bs4=new B.q(A.e,["00 miljonit"],x.p) +A.bx8=new B.q(A.a3,["0 miljard","0 miljardit"],x.p) +A.bpo=new B.q(A.e,["00 miljardit"],x.p) +A.bwU=new B.q(A.a3,["0 triljon","0 triljonit"],x.p) +A.bsm=new B.q(A.e,["00 triljonit"],x.p) +A.bvd=new B.b([3,A.bpi,6,A.bx9,7,A.bs4,9,A.bx8,10,A.bpo,12,A.bwU,13,A.bsm],x.o) +A.bmK=new B.q(A.e,["0 tusind"],x.p) +A.bve=new B.b([3,A.bmK,6,A.a1y,7,A.a0t,9,A.a1C,10,A.a0D,12,A.a1x,13,A.a1a],x.o) +A.bkX=new B.q(A.e,["0 tiso\u010d"],x.p) +A.v0={few:0,one:1,other:2,two:3} +A.b9l=new B.q(A.v0,["0 milijone","0 milijon","0 milijonov","0 milijona"],x.p) +A.b9k=new B.q(A.v0,["00 milijoni","00 milijon","00 milijonov","00 milijona"],x.p) +A.b9m=new B.q(A.v0,["0 milijarde","0 milijarda","0 milijard","0 milijardi"],x.p) +A.b9n=new B.q(A.v0,["0 bilijoni","0 bilijon","0 bilijonov","0 bilijona"],x.p) +A.bvf=new B.b([3,A.bkX,6,A.b9l,7,A.b9k,9,A.b9m,12,A.b9n],x.o) +A.bqI=new B.q(A.e,["\xa40\u842c"],x.p) +A.bvm=new B.b([3,A.bC,4,A.bqI,8,A.a12,12,A.a0n],x.o) +A.bnX=new B.q(A.e,["0\u842c"],x.p) +A.bvn=new B.b([3,A.bC,4,A.bnX,8,A.a0R,12,A.a0C],x.o) +A.boy=new B.q(A.e,["0\u4ebf"],x.p) +A.bsI=new B.q(A.e,["0\u4e07\u4ebf"],x.p) +A.a1n=new B.b([3,A.bC,4,A.a0S,8,A.boy,12,A.bsI],x.o) +A.bp9=new B.q(A.e,["\xa40\ucc9c"],x.p) +A.bt8=new B.q(A.e,["\xa40\ub9cc"],x.p) +A.boa=new B.q(A.e,["\xa40\uc5b5"],x.p) +A.blG=new B.q(A.e,["\xa40\uc870"],x.p) +A.bvo=new B.b([3,A.bp9,4,A.bt8,8,A.boa,12,A.blG],x.o) +A.bkF=new B.q(A.e,["0\ucc9c"],x.p) +A.boH=new B.q(A.e,["0\ub9cc"],x.p) +A.brY=new B.q(A.e,["0\uc5b5"],x.p) +A.blf=new B.q(A.e,["0\uc870"],x.p) +A.bvp=new B.b([3,A.bkF,4,A.boH,8,A.brY,12,A.blf],x.o) +A.bly=new B.q(A.e,["\xa40\u4ebf"],x.p) +A.bqr=new B.q(A.e,["\xa40\u4e07\u4ebf"],x.p) +A.a1o=new B.b([3,A.bC,4,A.a0g,8,A.bly,12,A.bqr],x.o) +A.bpI=new B.q(A.e,["\xa4\xa00\xa0\u043c\u044f\u043d\u0433\u0430"],x.p) +A.bos=new B.q(A.e,["\xa4000\xa0\u043c\u044f\u043d\u0433\u0430"],x.p) +A.bsp=new B.q(A.e,["\xa40\xa0\u0441\u0430\u044f"],x.p) +A.bnl=new B.q(A.e,["\xa40\xa0\u0442\u044d\u0440\u0431\u0443\u043c"],x.p) +A.bn_=new B.q(A.e,["\xa4\xa000\xa0\u0442\u044d\u0440\u0431\u0443\u043c"],x.p) +A.bl5=new B.q(A.e,["\xa4\xa00\xa0\u0438\u0445\xa0\u043d\u0430\u044f\u0434"],x.p) +A.bvu=new B.b([3,A.bpI,5,A.bos,6,A.bsp,9,A.bnl,10,A.bn_,12,A.bl5],x.o) +A.bs8=new B.q(A.e,["0 \u06c1\u0632\u0627\u0631"],x.p) +A.bpj=new B.q(A.e,["0 \u0644\u0627\u06a9\u06be"],x.p) +A.bnH=new B.q(A.e,["0 \u06a9\u0631\u0648\u0691"],x.p) +A.bqO=new B.q(A.e,["0 \u0627\u0631\u0628"],x.p) +A.bmB=new B.q(A.e,["0 \u06a9\u06be\u0631\u0628"],x.p) +A.bnS=new B.q(A.e,["00 \u0679\u0631\u06cc\u0644\u06cc\u0646"],x.p) +A.bxv=new B.b([3,A.bs8,5,A.bpj,7,A.bnH,9,A.bqO,11,A.bmB,13,A.bnS],x.o) +A.bs9=new B.q(A.e,["\xa4\xa00\xa0\u0939\u091c\u093e\u0930"],x.p) +A.boe=new B.q(A.e,["\xa4\xa00\xa0\u0932\u093e\u0916"],x.p) +A.bt1=new B.q(A.e,["\xa4\xa00\xa0\u0915\u0930\u094b\u0921"],x.p) +A.bls=new B.q(A.e,["\xa4\xa00\xa0\u0905\u0930\u092c"],x.p) +A.bmp=new B.q(A.e,["\xa4\xa00\xa0\u0916\u0930\u092c"],x.p) +A.blb=new B.q(A.e,["\xa4\xa00\xa0\u0936\u0902\u0916"],x.p) +A.bxw=new B.b([3,A.bs9,5,A.boe,7,A.bt1,9,A.bls,11,A.bmp,13,A.blb],x.o) +A.bnD=new B.q(A.e,["0 \u0915\u094b\u091f\u0940"],x.p) +A.bqV=new B.q(A.e,["0 \u0905\u092c\u094d\u091c"],x.p) +A.blK=new B.q(A.e,["0 \u0916\u0930\u094d\u0935"],x.p) +A.btl=new B.q(A.e,["0 \u092a\u0926\u094d\u092e"],x.p) +A.bxx=new B.b([3,A.a0K,5,A.BO,7,A.bnD,9,A.bqV,11,A.blK,13,A.btl],x.o) +A.bpk=new B.q(A.e,["0\xa0\u0a39\u0a1c\u0a3c\u0a3e\u0a30"],x.p) +A.bpM=new B.q(A.e,["0\xa0\u0a32\u0a71\u0a16"],x.p) +A.blz=new B.q(A.e,["0\xa0\u0a15\u0a30\u0a4b\u0a5c"],x.p) +A.boY=new B.q(A.e,["0\xa0\u0a05\u0a30\u0a2c"],x.p) +A.boS=new B.q(A.e,["0\xa0\u0a16\u0a30\u0a2c"],x.p) +A.bpl=new B.q(A.e,["0\xa0\u0a28\u0a40\u0a32"],x.p) +A.bxy=new B.b([3,A.bpk,5,A.bpM,7,A.blz,9,A.boY,11,A.boS,13,A.bpl],x.o) +A.bnN=new B.q(A.e,["0\xa0\u06c1\u0632\u0627\u0631"],x.p) +A.blr=new B.q(A.e,["0\xa0\u0644\u0627\u06a9\u06be"],x.p) +A.bpO=new B.q(A.e,["0\xa0\u06a9\u0631\u0648\u0691"],x.p) +A.bkS=new B.q(A.e,["0\xa0\u0627\u0631\u0628"],x.p) +A.boT=new B.q(A.e,["0\xa0\u06a9\u06be\u0631\u0628"],x.p) +A.blk=new B.q(A.e,["00\xa0\u0679\u0631\u06cc\u0644\u06cc\u0646"],x.p) +A.bxz=new B.b([3,A.bnN,5,A.blr,7,A.bpO,9,A.bkS,11,A.boT,13,A.blk],x.o) +A.bop=new B.q(A.e,["0 \u0a39\u0a1c\u0a3c\u0a3e\u0a30"],x.p) +A.bpJ=new B.q(A.e,["0 \u0a32\u0a71\u0a16"],x.p) +A.bok=new B.q(A.e,["0 \u0a15\u0a30\u0a4b\u0a5c"],x.p) +A.bpP=new B.q(A.e,["0 \u0a05\u0a30\u0a2c"],x.p) +A.bnu=new B.q(A.e,["0 \u0a16\u0a30\u0a2c"],x.p) +A.brz=new B.q(A.e,["0 \u0a28\u0a40\u0a32"],x.p) +A.bxA=new B.b([3,A.bop,5,A.bpJ,7,A.bok,9,A.bpP,11,A.bnu,13,A.brz],x.o) +A.bqi=new B.q(A.e,["0\xa0\u0939\u091c\u093c\u093e\u0930"],x.p) +A.BR=new B.q(A.e,["0\xa0\u0932\u093e\u0916"],x.p) +A.bnf=new B.q(A.e,["0\xa0\u0915\u0970"],x.p) +A.blZ=new B.q(A.e,["0\xa0\u0905\u0970"],x.p) +A.bnq=new B.q(A.e,["0\xa0\u0916\u0970"],x.p) +A.bnC=new B.q(A.e,["0\xa0\u0928\u0940\u0932"],x.p) +A.bxB=new B.b([3,A.bqi,5,A.BR,7,A.bnf,9,A.blZ,11,A.bnq,13,A.bnC],x.o) +A.bs_=new B.q(A.e,["0\xa0\u0939"],x.p) +A.bn3=new B.q(A.e,["0\xa0\u0915\u094b\u091f\u0940"],x.p) +A.bpc=new B.q(A.e,["0\xa0\u0905\u092c\u094d\u091c"],x.p) +A.bmr=new B.q(A.e,["0\xa0\u0916\u0930\u094d\u0935"],x.p) +A.bpg=new B.q(A.e,["0\xa0\u092a\u0926\u094d\u092e"],x.p) +A.bxC=new B.b([3,A.bs_,5,A.BR,7,A.bn3,9,A.bpc,11,A.bmr,13,A.bpg],x.o) +A.bq2=new B.q(A.e,["\xa40\xa0\u0939\u091c\u093c\u093e\u0930"],x.p) +A.a0X=new B.q(A.e,["\xa40\xa0\u0932\u093e\u0916"],x.p) +A.bry=new B.q(A.e,["\xa40\xa0\u0915\u0970"],x.p) +A.bpC=new B.q(A.e,["\xa40\xa0\u0905\u0970"],x.p) +A.bqv=new B.q(A.e,["\xa40\xa0\u0916\u0970"],x.p) +A.bmk=new B.q(A.e,["\xa40\xa0\u0928\u0940\u0932"],x.p) +A.bxD=new B.b([3,A.bq2,5,A.a0X,7,A.bry,9,A.bpC,11,A.bqv,13,A.bmk],x.o) +A.boE=new B.q(A.e,["\xa40\xa0\u0939"],x.p) +A.br7=new B.q(A.e,["\xa40\xa0\u0915\u094b\u091f\u0940"],x.p) +A.bsV=new B.q(A.e,["\xa40\xa0\u0905\u092c\u094d\u091c"],x.p) +A.br9=new B.q(A.e,["\xa40\xa0\u0916\u0930\u094d\u0935"],x.p) +A.bs6=new B.q(A.e,["\xa40\xa0\u092a\u0926\u094d\u092e"],x.p) +A.bxE=new B.b([3,A.boE,5,A.a0X,7,A.br7,9,A.bsV,11,A.br9,13,A.bs6],x.o) +A.bqq=new B.q(A.e,["\xa4\xa00\xa0\u0a39\u0a1c\u0a3c\u0a3e\u0a30"],x.p) +A.bsy=new B.q(A.e,["\xa4\xa00\xa0\u0a32\u0a71\u0a16"],x.p) +A.bnc=new B.q(A.e,["\xa4\xa00\xa0\u0a15\u0a30\u0a4b\u0a5c"],x.p) +A.bpd=new B.q(A.e,["\xa4\xa00\xa0\u0a05\u0a30\u0a2c"],x.p) +A.bnJ=new B.q(A.e,["\xa4\xa00\xa0\u0a16\u0a30\u0a2c"],x.p) +A.bs3=new B.q(A.e,["\xa4\xa00\xa0\u0a28\u0a40\u0a32"],x.p) +A.bxF=new B.b([3,A.bqq,5,A.bsy,7,A.bnc,9,A.bpd,11,A.bnJ,13,A.bs3],x.o) +A.blD=new B.q(A.e,["0\xa0\u0939\u091c\u093e\u0930"],x.p) +A.boI=new B.q(A.e,["0\xa0\u0915\u0930\u094b\u0921"],x.p) +A.brL=new B.q(A.e,["0\xa0\u0905\u0930\u092c"],x.p) +A.blO=new B.q(A.e,["0\xa0\u0916\u0930\u092c"],x.p) +A.bsP=new B.q(A.e,["0\xa0\u0936\u0902\u0916"],x.p) +A.bxG=new B.b([3,A.blD,5,A.BR,7,A.boI,9,A.brL,11,A.blO,13,A.bsP],x.o) +A.br8=new B.q(A.e,["0\xa0\u043c\u044f\u043d\u0433\u0430"],x.p) +A.bsf=new B.q(A.e,["0\xa0\u0441\u0430\u044f"],x.p) +A.bmE=new B.q(A.e,["0\xa0\u0442\u044d\u0440\u0431\u0443\u043c"],x.p) +A.blJ=new B.q(A.e,["000\u0422"],x.p) +A.bnw=new B.q(A.e,["0\u0418\u041d"],x.p) +A.bD4=new B.b([3,A.br8,6,A.bsf,9,A.bmE,11,A.blJ,12,A.bnw],x.o) +A.bnb=new B.q(A.e,["0\xa0\u10d0\u10d7.\xa0\xa4"],x.p) +A.boN=new B.q(A.e,["0\xa0\u10db\u10da\u10dc.\xa0\xa4"],x.p) +A.bql=new B.q(A.e,["0\xa0\u10db\u10da\u10e0\u10d3.\xa0\xa4"],x.p) +A.bqt=new B.q(A.e,["000\xa0\u10db\u10da\u10e0.\xa0\xa4"],x.p) +A.bkE=new B.q(A.e,["0\xa0\u10e2\u10e0\u10da.\xa0\xa4"],x.p) +A.bD5=new B.b([3,A.bnb,6,A.boN,9,A.bql,11,A.bqt,12,A.bkE],x.o) +A.bxs=new B.q(A.a3,["000G","000B"],x.p) +A.bD6=new B.b([3,A.kw,6,A.p2,9,A.uH,11,A.bxs,12,A.mm],x.o) +A.bnj=new B.q(A.e,["0\xa0\u10d0\u10d7."],x.p) +A.bpH=new B.q(A.e,["0\xa0\u10db\u10da\u10dc."],x.p) +A.bpa=new B.q(A.e,["0\xa0\u10db\u10da\u10e0\u10d3."],x.p) +A.bsE=new B.q(A.e,["000\xa0\u10db\u10da\u10e0."],x.p) +A.btI=new B.q(A.e,["0\xa0\u10e2\u10e0\u10da."],x.p) +A.bD7=new B.b([3,A.bnj,6,A.bpH,9,A.bpa,11,A.bsE,12,A.btI],x.o) +A.bse=new B.q(A.e,["0\xa0\u1011\u1031\u102c\u1004\u103a"],x.p) +A.bkc=new B.q(A.e,["0\xa0\u101e\u1031\u102c\u1004\u103a\u1038"],x.p) +A.bkd=new B.q(A.e,["0\xa0\u101e\u102d\u1014\u103a\u1038"],x.p) +A.bn4=new B.q(A.e,["0\xa0\u101e\u1014\u103a\u1038"],x.p) +A.bq0=new B.q(A.e,["0\xa0\u1000\u102f\u100b\u1031"],x.p) +A.bmb=new B.q(A.e,["000\xa0\u100b\u1031"],x.p) +A.bo_=new B.q(A.e,["\u100b\u1031\xa00\xa0\u1011"],x.p) +A.bo0=new B.q(A.e,["\u100b\u1031\xa00\xa0\u101e"],x.p) +A.bpb=new B.q(A.e,["\u100b\u1031\xa00\xa0\u101e\u102d\u1014\u103a\u1038"],x.p) +A.blj=new B.q(A.e,["\u100b\u1031\xa00\xa0\u101e\u1014\u103a\u1038"],x.p) +A.btJ=new B.q(A.e,["0\xa0\u1000\u1031\u102c\u100b\u102d"],x.p) +A.bDJ=new B.b([3,A.bse,4,A.bkc,5,A.bkd,6,A.bn4,7,A.bq0,9,A.bmb,10,A.bo_,11,A.bo0,12,A.bpb,13,A.blj,14,A.btJ],x.o) +A.ad9=new C.cB9(0,"COMPACT_DECIMAL_SHORT_PATTERN")})();(function lazyInitializers(){var w=a.lazy,v=a.lazyFinal +w($,"eg0","dAm",()=>{var u=null +return B.S(["af",C.bw(A.bat,A.bfc,A.bbA),"am",C.bw(A.bbr,A.bac,A.bby),"ar",C.bw(A.Bp,A.Bk,A.AS),"ar_DZ",C.bw(A.Bp,A.Bk,A.AS),"ar_EG",C.bw(A.Bp,A.Bk,A.AS),"as",C.bw(A.b9j,A.b9g,A.bdn),"az",C.bw(A.baE,A.ZX,A.baI),"be",C.bw(A.bb5,A.a__,A.ZY),"bg",C.bw(A.b1v,A.bb_,A.bbt),"bm",C.bw(u,A.oU,A.ku),"bn",C.bw(A.aYW,A.aYX,A.b7x),"br",C.bw(A.baV,A.a_8,A.bbk),"bs",C.bw(A.a_6,A.ZU,A.ZW),"ca",C.bw(A.b1y,A.b3b,A.b3d),"chr",C.bw(A.baF,A.d7,A.cU),"cs",C.bw(A.b1x,A.a_3,A.a_2),"cy",C.bw(A.bjx,A.d7,A.cU),"da",C.bw(A.bve,A.baK,A.baa),"de",C.bw(A.C3,A.Bo,A.Bn),"de_AT",C.bw(A.C3,A.Bo,A.Bn),"de_CH",C.bw(A.C3,A.Bo,A.Bn),"el",C.bw(A.b1z,A.bag,A.bbH),"en",C.bw(A.fu,A.d7,A.cU),"en_AU",C.bw(A.fu,A.d7,A.cU),"en_CA",C.bw(A.fu,A.d7,A.cU),"en_GB",C.bw(A.fu,A.d7,A.cU),"en_IE",C.bw(A.fu,A.d7,A.cU),"en_IN",C.bw(A.fu,A.bu1,A.bu0),"en_MY",C.bw(A.fu,A.d7,A.cU),"en_NZ",C.bw(A.fu,A.d7,A.cU),"en_SG",C.bw(A.fu,A.d7,A.cU),"en_US",C.bw(A.fu,A.d7,A.cU),"en_ZA",C.bw(A.fu,A.d7,A.cU),"es",C.bw(A.oS,A.Z_,A.Z0),"es_419",C.bw(A.oS,A.b3e,A.bbM),"es_ES",C.bw(A.oS,A.Z_,A.Z0),"es_MX",C.bw(A.oS,A.b3c,A.b3g),"es_US",C.bw(A.oS,A.b3f,A.b3h),"et",C.bw(A.bvd,A.bao,A.bbc),"eu",C.bw(A.bdB,A.aZ1,A.Yu),"fa",C.bw(A.baJ,A.bax,A.baN),"fi",C.bw(A.b1A,A.bb2,A.bbB),"fil",C.bw(A.a_9,A.d7,A.cU),"fr",C.bw(A.Y4,A.a_4,A.ZS),"fr_CA",C.bw(A.aVl,A.a_8,A.bbl),"fr_CH",C.bw(A.Y4,A.a_4,A.ZS),"fur",C.bw(u,A.baC,A.ku),"ga",C.bw(A.bjL,A.ba4,A.baG),"gl",C.bw(A.b6a,A.aZ0,A.Yu),"gsw",C.bw(A.bbo,A.bab,A.bbf),"gu",C.bw(A.b0n,A.b0m,A.b0o),"haw",C.bw(u,A.oU,A.ku),"he",C.bw(A.ZZ,A.ZV,A.a_b),"hi",C.bw(A.b3p,A.bxD,A.bxB),"hr",C.bw(A.ba5,A.bb6,A.ba7),"hu",C.bw(A.bam,A.bbi,A.b9Z),"hy",C.bw(A.bbj,A.bbn,A.baS),"id",C.bw(A.ZT,A.a_5,A.a_0),"in",C.bw(A.ZT,A.a_5,A.a_0),"is",C.bw(A.bbI,A.baO,A.bbu),"it",C.bw(A.YT,A.a_e,A.a_f),"it_CH",C.bw(A.YT,A.a_e,A.a_f),"iw",C.bw(A.ZZ,A.ZV,A.a_b),"ja",C.bw(u,A.b2c,A.b2d),"ka",C.bw(A.bal,A.bD5,A.bD7),"kk",C.bw(A.bai,A.baM,A.b9i),"km",C.bw(A.b9f,A.bbe,A.aVm),"kn",C.bw(A.bbs,A.bbh,A.bbq),"ko",C.bw(u,A.bvo,A.bvp),"ky",C.bw(A.bb9,A.bay,A.baX),"ln",C.bw(u,A.ZX,A.ku),"lo",C.bw(A.b9h,A.bb7,A.b0X),"lt",C.bw(A.bb1,A.bb8,A.bbp),"lv",C.bw(A.b1w,A.bbb,A.baU),"mg",C.bw(u,A.oU,A.ku),"mk",C.bw(A.baw,A.baf,A.aZ8),"ml",C.bw(A.bav,A.d7,A.cU),"mn",C.bw(A.ban,A.bvu,A.bD4),"mr",C.bw(A.bxx,A.bxE,A.bxC),"ms",C.bw(A.baq,A.baB,A.bb0),"mt",C.bw(u,A.oU,A.ku),"my",C.bw(A.b6g,A.b6h,A.bDJ),"nb",C.bw(A.C4,A.Bl,A.Bm),"ne",C.bw(A.bu2,A.bxw,A.bxG),"nl",C.bw(A.bau,A.bbg,A.baP),"no",C.bw(A.C4,A.Bl,A.Bm),"no_NO",C.bw(A.C4,A.Bl,A.Bm),"nyn",C.bw(u,A.oU,A.ku),"or",C.bw(A.bbC,A.bas,A.bbm),"pa",C.bw(A.bxA,A.bxF,A.bxy),"pl",C.bw(A.bbd,A.bak,A.ba2),"ps",C.bw(A.aVj,A.aZa,A.bD6),"pt",C.bw(A.a_a,A.a_1,A.a_7),"pt_BR",C.bw(A.a_a,A.a_1,A.a_7),"pt_PT",C.bw(A.b7B,A.bb3,A.baD),"ro",C.bw(A.bbv,A.aVk,A.ba8),"ru",C.bw(A.ba_,A.a__,A.ZY),"si",C.bw(A.ba1,A.bbE,A.bbx),"sk",C.bw(A.b1B,A.a_3,A.a_2),"sl",C.bw(A.bvf,A.baA,A.bbz),"sq",C.bw(A.ba0,A.bb4,A.baj),"sr",C.bw(A.baL,A.bbG,A.bar),"sr_Latn",C.bw(A.a_6,A.ZU,A.ZW),"sv",C.bw(A.b0C,A.bbF,A.bae),"sw",C.bw(A.b9Y,A.b9S,A.ba3),"ta",C.bw(A.baW,A.aZb,A.bah),"te",C.bw(A.b1u,A.bad,A.bap),"th",C.bw(A.aZf,A.d7,A.cU),"tl",C.bw(A.a_9,A.d7,A.cU),"tr",C.bw(A.bba,A.baT,A.bbD),"uk",C.bw(A.bbw,A.baz,A.baR),"ur",C.bw(A.bxv,A.bv9,A.bxz),"uz",C.bw(A.baH,A.baQ,A.ba9),"vi",C.bw(A.ba6,A.baY,A.b9X),"zh",C.bw(u,A.a1o,A.a1n),"zh_CN",C.bw(u,A.a1o,A.a1n),"zh_HK",C.bw(u,A.d7,A.cU),"zh_TW",C.bw(u,A.bvm,A.bvn),"zu",C.bw(A.baZ,A.b18,A.cU)],x.w,B.V("aCp"))}) +v($,"ec5","dxM",()=>B.ax("([^0]*)(0+)(.*)",!0,!1,!1,!1)) +v($,"ec4","dxL",()=>B.ax("^0*$",!0,!1,!1,!1)) +v($,"ed9","dbE",()=>C.dSb(1,!1,"","",null,"",""))})()}; +(a=>{a["SJMcvnVBRxGPP7kdwosSYwggho0="]=a.current})($__dart_deferred_initializers__); \ No newline at end of file diff --git a/catalyst_voices/scripts/version_web_assets/test/helper/build_wasm/main.dart.mjs b/catalyst_voices/scripts/version_web_assets/test/helper/build_wasm/main.dart.mjs new file mode 100644 index 000000000000..a5cc89fe2f3f --- /dev/null +++ b/catalyst_voices/scripts/version_web_assets/test/helper/build_wasm/main.dart.mjs @@ -0,0 +1,1333 @@ +// Compiles a dart2wasm-generated main module from `source` which can then +// instantiatable via the `instantiate` method. +// +// `source` needs to be a `Response` object (or promise thereof) e.g. created +// via the `fetch()` JS API. +export async function compileStreaming(source) { + const builtins = {builtins: ['js-string']}; + return new CompiledApp( + await WebAssembly.compileStreaming(source, builtins), builtins); +} + +// Compiles a dart2wasm-generated wasm modules from `bytes` which is then +// instantiatable via the `instantiate` method. +export async function compile(bytes) { + const builtins = {builtins: ['js-string']}; + return new CompiledApp(await WebAssembly.compile(bytes, builtins), builtins); +} + +// DEPRECATED: Please use `compile` or `compileStreaming` to get a compiled app, +// use `instantiate` method to get an instantiated app and then call +// `invokeMain` to invoke the main function. +export async function instantiate(modulePromise, importObjectPromise) { + var moduleOrCompiledApp = await modulePromise; + if (!(moduleOrCompiledApp instanceof CompiledApp)) { + moduleOrCompiledApp = new CompiledApp(moduleOrCompiledApp); + } + const instantiatedApp = await moduleOrCompiledApp.instantiate(await importObjectPromise); + return instantiatedApp.instantiatedModule; +} + +// DEPRECATED: Please use `compile` or `compileStreaming` to get a compiled app, +// use `instantiate` method to get an instantiated app and then call +// `invokeMain` to invoke the main function. +export const invoke = (moduleInstance, ...args) => { + moduleInstance.exports.$invokeMain(args); +} + +class CompiledApp { + constructor(module, builtins) { + this.module = module; + this.builtins = builtins; + } + + // The second argument is an options object containing: + // `loadDeferredWasm` is a JS function that takes a module name matching a + // wasm file produced by the dart2wasm compiler and returns the bytes to + // load the module. These bytes can be in either a format supported by + // `WebAssembly.compile` or `WebAssembly.compileStreaming`. + // `loadDynamicModule` is a JS function that takes two string names matching, + // in order, a wasm file produced by the dart2wasm compiler during dynamic + // module compilation and a corresponding js file produced by the same + // compilation. It should return a JS Array containing 2 elements. The first + // should be the bytes for the wasm module in a format supported by + // `WebAssembly.compile` or `WebAssembly.compileStreaming`. The second + // should be the result of using the JS 'import' API on the js file path. + async instantiate(additionalImports, {loadDeferredWasm, loadDynamicModule} = {}) { + let dartInstance; + + // Prints to the console + function printToConsole(value) { + if (typeof dartPrint == "function") { + dartPrint(value); + return; + } + if (typeof console == "object" && typeof console.log != "undefined") { + console.log(value); + return; + } + if (typeof print == "function") { + print(value); + return; + } + + throw "Unable to print message: " + value; + } + + // A special symbol attached to functions that wrap Dart functions. + const jsWrappedDartFunctionSymbol = Symbol("JSWrappedDartFunction"); + + function finalizeWrapper(dartFunction, wrapped) { + wrapped.dartFunction = dartFunction; + wrapped[jsWrappedDartFunctionSymbol] = true; + return wrapped; + } + + // Imports + const dart2wasm = { + _3: (o, t) => typeof o === t, + _4: (o, c) => o instanceof c, + _6: (o,s,v) => o[s] = v, + _7: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._7(f,arguments.length,x0) }), + _8: f => finalizeWrapper(f, function(x0,x1) { return dartInstance.exports._8(f,arguments.length,x0,x1) }), + _9: (o, a) => o + a, + _19: (o, a) => o == a, + _36: () => new Array(), + _37: x0 => new Array(x0), + _39: x0 => x0.length, + _41: (x0,x1) => x0[x1], + _42: (x0,x1,x2) => { x0[x1] = x2 }, + _43: x0 => new Promise(x0), + _45: (x0,x1,x2) => new DataView(x0,x1,x2), + _47: x0 => new Int8Array(x0), + _48: (x0,x1,x2) => new Uint8Array(x0,x1,x2), + _49: x0 => new Uint8Array(x0), + _51: x0 => new Uint8ClampedArray(x0), + _53: x0 => new Int16Array(x0), + _55: x0 => new Uint16Array(x0), + _57: x0 => new Int32Array(x0), + _59: x0 => new Uint32Array(x0), + _61: x0 => new Float32Array(x0), + _63: x0 => new Float64Array(x0), + _65: (x0,x1,x2) => x0.call(x1,x2), + _66: f => finalizeWrapper(f, function(x0,x1) { return dartInstance.exports._66(f,arguments.length,x0,x1) }), + _67: (x0,x1) => x0.call(x1), + _69: () => Symbol("jsBoxedDartObjectProperty"), + _70: (decoder, codeUnits) => decoder.decode(codeUnits), + _71: () => new TextDecoder("utf-8", {fatal: true}), + _72: () => new TextDecoder("utf-8", {fatal: false}), + _73: (s) => +s, + _74: x0 => new Uint8Array(x0), + _75: (x0,x1,x2) => x0.set(x1,x2), + _76: (x0,x1) => x0.transferFromImageBitmap(x1), + _77: x0 => x0.arrayBuffer(), + _78: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._78(f,arguments.length,x0) }), + _79: x0 => new window.FinalizationRegistry(x0), + _80: (x0,x1,x2,x3) => x0.register(x1,x2,x3), + _81: (x0,x1) => x0.unregister(x1), + _82: (x0,x1,x2) => x0.slice(x1,x2), + _83: (x0,x1) => x0.decode(x1), + _84: (x0,x1) => x0.segment(x1), + _85: () => new TextDecoder(), + _87: x0 => x0.click(), + _88: x0 => x0.buffer, + _89: x0 => x0.wasmMemory, + _90: () => globalThis.window._flutter_skwasmInstance, + _91: x0 => x0.rasterStartMilliseconds, + _92: x0 => x0.rasterEndMilliseconds, + _93: x0 => x0.imageBitmaps, + _120: x0 => x0.remove(), + _121: (x0,x1) => x0.append(x1), + _122: (x0,x1,x2) => x0.insertBefore(x1,x2), + _123: (x0,x1) => x0.querySelector(x1), + _125: (x0,x1) => x0.removeChild(x1), + _203: x0 => x0.stopPropagation(), + _204: x0 => x0.preventDefault(), + _206: (x0,x1,x2,x3) => x0.addEventListener(x1,x2,x3), + _251: x0 => x0.unlock(), + _252: x0 => x0.getReader(), + _253: (x0,x1,x2) => x0.addEventListener(x1,x2), + _254: (x0,x1,x2) => x0.removeEventListener(x1,x2), + _255: (x0,x1) => x0.item(x1), + _256: x0 => x0.next(), + _257: x0 => x0.now(), + _258: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._258(f,arguments.length,x0) }), + _259: (x0,x1) => x0.addListener(x1), + _260: (x0,x1) => x0.removeListener(x1), + _261: (x0,x1) => x0.matchMedia(x1), + _262: (x0,x1) => x0.revokeObjectURL(x1), + _263: x0 => x0.close(), + _264: (x0,x1,x2,x3,x4) => ({type: x0,data: x1,premultiplyAlpha: x2,colorSpaceConversion: x3,preferAnimation: x4}), + _265: x0 => new window.ImageDecoder(x0), + _266: x0 => ({frameIndex: x0}), + _267: (x0,x1) => x0.decode(x1), + _268: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._268(f,arguments.length,x0) }), + _269: (x0,x1) => x0.getModifierState(x1), + _270: (x0,x1) => x0.removeProperty(x1), + _271: (x0,x1) => x0.prepend(x1), + _272: x0 => x0.disconnect(), + _273: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._273(f,arguments.length,x0) }), + _274: (x0,x1) => x0.getAttribute(x1), + _275: (x0,x1) => x0.contains(x1), + _276: x0 => x0.blur(), + _277: x0 => x0.hasFocus(), + _278: (x0,x1) => x0.hasAttribute(x1), + _279: (x0,x1) => x0.getModifierState(x1), + _280: (x0,x1) => x0.appendChild(x1), + _281: (x0,x1) => x0.createTextNode(x1), + _282: (x0,x1) => x0.removeAttribute(x1), + _283: x0 => x0.getBoundingClientRect(), + _284: (x0,x1) => x0.observe(x1), + _285: x0 => x0.disconnect(), + _286: (x0,x1) => x0.closest(x1), + _696: () => globalThis.window.flutterConfiguration, + _697: x0 => x0.assetBase, + _703: x0 => x0.debugShowSemanticsNodes, + _704: x0 => x0.hostElement, + _705: x0 => x0.multiViewEnabled, + _706: x0 => x0.nonce, + _708: x0 => x0.fontFallbackBaseUrl, + _712: x0 => x0.console, + _713: x0 => x0.devicePixelRatio, + _714: x0 => x0.document, + _715: x0 => x0.history, + _716: x0 => x0.innerHeight, + _717: x0 => x0.innerWidth, + _718: x0 => x0.location, + _719: x0 => x0.navigator, + _720: x0 => x0.visualViewport, + _721: x0 => x0.performance, + _723: x0 => x0.URL, + _725: (x0,x1) => x0.getComputedStyle(x1), + _726: x0 => x0.screen, + _727: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._727(f,arguments.length,x0) }), + _728: (x0,x1) => x0.requestAnimationFrame(x1), + _733: (x0,x1) => x0.warn(x1), + _735: (x0,x1) => x0.debug(x1), + _736: x0 => globalThis.parseFloat(x0), + _737: () => globalThis.window, + _738: () => globalThis.Intl, + _739: () => globalThis.Symbol, + _740: (x0,x1,x2,x3,x4) => globalThis.createImageBitmap(x0,x1,x2,x3,x4), + _742: x0 => x0.clipboard, + _743: x0 => x0.maxTouchPoints, + _744: x0 => x0.vendor, + _745: x0 => x0.language, + _746: x0 => x0.platform, + _747: x0 => x0.userAgent, + _748: (x0,x1) => x0.vibrate(x1), + _749: x0 => x0.languages, + _750: x0 => x0.documentElement, + _751: (x0,x1) => x0.querySelector(x1), + _754: (x0,x1) => x0.createElement(x1), + _757: (x0,x1) => x0.createEvent(x1), + _758: x0 => x0.activeElement, + _761: x0 => x0.head, + _762: x0 => x0.body, + _764: (x0,x1) => { x0.title = x1 }, + _767: x0 => x0.visibilityState, + _768: () => globalThis.document, + _769: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._769(f,arguments.length,x0) }), + _770: (x0,x1) => x0.dispatchEvent(x1), + _778: x0 => x0.target, + _780: x0 => x0.timeStamp, + _781: x0 => x0.type, + _783: (x0,x1,x2,x3) => x0.initEvent(x1,x2,x3), + _789: x0 => x0.baseURI, + _790: x0 => x0.firstChild, + _794: x0 => x0.parentElement, + _796: (x0,x1) => { x0.textContent = x1 }, + _797: x0 => x0.parentNode, + _799: x0 => x0.isConnected, + _803: x0 => x0.firstElementChild, + _805: x0 => x0.nextElementSibling, + _806: x0 => x0.clientHeight, + _807: x0 => x0.clientWidth, + _808: x0 => x0.offsetHeight, + _809: x0 => x0.offsetWidth, + _810: x0 => x0.id, + _811: (x0,x1) => { x0.id = x1 }, + _814: (x0,x1) => { x0.spellcheck = x1 }, + _815: x0 => x0.tagName, + _816: x0 => x0.style, + _818: (x0,x1) => x0.querySelectorAll(x1), + _819: (x0,x1,x2) => x0.setAttribute(x1,x2), + _820: x0 => x0.tabIndex, + _821: (x0,x1) => { x0.tabIndex = x1 }, + _822: (x0,x1) => x0.focus(x1), + _823: x0 => x0.scrollTop, + _824: (x0,x1) => { x0.scrollTop = x1 }, + _825: x0 => x0.scrollLeft, + _826: (x0,x1) => { x0.scrollLeft = x1 }, + _827: x0 => x0.classList, + _829: (x0,x1) => { x0.className = x1 }, + _831: (x0,x1) => x0.getElementsByClassName(x1), + _832: (x0,x1) => x0.attachShadow(x1), + _835: x0 => x0.computedStyleMap(), + _836: (x0,x1) => x0.get(x1), + _842: (x0,x1) => x0.getPropertyValue(x1), + _843: (x0,x1,x2,x3) => x0.setProperty(x1,x2,x3), + _844: x0 => x0.offsetLeft, + _845: x0 => x0.offsetTop, + _846: x0 => x0.offsetParent, + _848: (x0,x1) => { x0.name = x1 }, + _849: x0 => x0.content, + _850: (x0,x1) => { x0.content = x1 }, + _854: (x0,x1) => { x0.src = x1 }, + _855: x0 => x0.naturalWidth, + _856: x0 => x0.naturalHeight, + _860: (x0,x1) => { x0.crossOrigin = x1 }, + _862: (x0,x1) => { x0.decoding = x1 }, + _863: x0 => x0.decode(), + _868: (x0,x1) => { x0.nonce = x1 }, + _873: (x0,x1) => { x0.width = x1 }, + _875: (x0,x1) => { x0.height = x1 }, + _878: (x0,x1) => x0.getContext(x1), + _937: x0 => x0.width, + _938: x0 => x0.height, + _940: (x0,x1) => x0.fetch(x1), + _941: x0 => x0.status, + _943: x0 => x0.body, + _944: x0 => x0.arrayBuffer(), + _947: x0 => x0.read(), + _948: x0 => x0.value, + _949: x0 => x0.done, + _951: x0 => x0.name, + _952: x0 => x0.x, + _953: x0 => x0.y, + _956: x0 => x0.top, + _957: x0 => x0.right, + _958: x0 => x0.bottom, + _959: x0 => x0.left, + _971: x0 => x0.height, + _972: x0 => x0.width, + _973: x0 => x0.scale, + _974: (x0,x1) => { x0.value = x1 }, + _977: (x0,x1) => { x0.placeholder = x1 }, + _979: (x0,x1) => { x0.name = x1 }, + _980: x0 => x0.selectionDirection, + _981: x0 => x0.selectionStart, + _982: x0 => x0.selectionEnd, + _985: x0 => x0.value, + _987: (x0,x1,x2) => x0.setSelectionRange(x1,x2), + _988: x0 => x0.readText(), + _989: (x0,x1) => x0.writeText(x1), + _991: x0 => x0.altKey, + _992: x0 => x0.code, + _993: x0 => x0.ctrlKey, + _994: x0 => x0.key, + _995: x0 => x0.keyCode, + _996: x0 => x0.location, + _997: x0 => x0.metaKey, + _998: x0 => x0.repeat, + _999: x0 => x0.shiftKey, + _1000: x0 => x0.isComposing, + _1002: x0 => x0.state, + _1003: (x0,x1) => x0.go(x1), + _1005: (x0,x1,x2,x3) => x0.pushState(x1,x2,x3), + _1006: (x0,x1,x2,x3) => x0.replaceState(x1,x2,x3), + _1007: x0 => x0.pathname, + _1008: x0 => x0.search, + _1009: x0 => x0.hash, + _1013: x0 => x0.state, + _1016: (x0,x1) => x0.createObjectURL(x1), + _1018: x0 => new Blob(x0), + _1020: x0 => new MutationObserver(x0), + _1021: (x0,x1,x2) => x0.observe(x1,x2), + _1022: f => finalizeWrapper(f, function(x0,x1) { return dartInstance.exports._1022(f,arguments.length,x0,x1) }), + _1025: x0 => x0.attributeName, + _1026: x0 => x0.type, + _1027: x0 => x0.matches, + _1028: x0 => x0.matches, + _1032: x0 => x0.relatedTarget, + _1034: x0 => x0.clientX, + _1035: x0 => x0.clientY, + _1036: x0 => x0.offsetX, + _1037: x0 => x0.offsetY, + _1040: x0 => x0.button, + _1041: x0 => x0.buttons, + _1042: x0 => x0.ctrlKey, + _1046: x0 => x0.pointerId, + _1047: x0 => x0.pointerType, + _1048: x0 => x0.pressure, + _1049: x0 => x0.tiltX, + _1050: x0 => x0.tiltY, + _1051: x0 => x0.getCoalescedEvents(), + _1054: x0 => x0.deltaX, + _1055: x0 => x0.deltaY, + _1056: x0 => x0.wheelDeltaX, + _1057: x0 => x0.wheelDeltaY, + _1058: x0 => x0.deltaMode, + _1065: x0 => x0.changedTouches, + _1068: x0 => x0.clientX, + _1069: x0 => x0.clientY, + _1072: x0 => x0.data, + _1075: (x0,x1) => { x0.disabled = x1 }, + _1077: (x0,x1) => { x0.type = x1 }, + _1078: (x0,x1) => { x0.max = x1 }, + _1079: (x0,x1) => { x0.min = x1 }, + _1080: x0 => x0.value, + _1081: (x0,x1) => { x0.value = x1 }, + _1082: x0 => x0.disabled, + _1083: (x0,x1) => { x0.disabled = x1 }, + _1085: (x0,x1) => { x0.placeholder = x1 }, + _1087: (x0,x1) => { x0.name = x1 }, + _1089: (x0,x1) => { x0.autocomplete = x1 }, + _1090: x0 => x0.selectionDirection, + _1092: x0 => x0.selectionStart, + _1093: x0 => x0.selectionEnd, + _1096: (x0,x1,x2) => x0.setSelectionRange(x1,x2), + _1097: (x0,x1) => x0.add(x1), + _1100: (x0,x1) => { x0.noValidate = x1 }, + _1101: (x0,x1) => { x0.method = x1 }, + _1102: (x0,x1) => { x0.action = x1 }, + _1103: (x0,x1) => new OffscreenCanvas(x0,x1), + _1109: (x0,x1) => x0.getContext(x1), + _1111: x0 => x0.convertToBlob(), + _1128: x0 => x0.orientation, + _1129: x0 => x0.width, + _1130: x0 => x0.height, + _1131: (x0,x1) => x0.lock(x1), + _1150: x0 => new ResizeObserver(x0), + _1153: f => finalizeWrapper(f, function(x0,x1) { return dartInstance.exports._1153(f,arguments.length,x0,x1) }), + _1161: x0 => x0.length, + _1162: x0 => x0.iterator, + _1163: x0 => x0.Segmenter, + _1164: x0 => x0.v8BreakIterator, + _1165: (x0,x1) => new Intl.Segmenter(x0,x1), + _1166: x0 => x0.done, + _1167: x0 => x0.value, + _1168: x0 => x0.index, + _1172: (x0,x1) => new Intl.v8BreakIterator(x0,x1), + _1173: (x0,x1) => x0.adoptText(x1), + _1174: x0 => x0.first(), + _1175: x0 => x0.next(), + _1176: x0 => x0.current(), + _1182: x0 => x0.hostElement, + _1183: x0 => x0.viewConstraints, + _1186: x0 => x0.maxHeight, + _1187: x0 => x0.maxWidth, + _1188: x0 => x0.minHeight, + _1189: x0 => x0.minWidth, + _1190: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1190(f,arguments.length,x0) }), + _1191: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1191(f,arguments.length,x0) }), + _1192: (x0,x1) => ({addView: x0,removeView: x1}), + _1193: x0 => x0.loader, + _1194: () => globalThis._flutter, + _1195: (x0,x1) => x0.didCreateEngineInitializer(x1), + _1196: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1196(f,arguments.length,x0) }), + _1197: f => finalizeWrapper(f, function() { return dartInstance.exports._1197(f,arguments.length) }), + _1198: (x0,x1) => ({initializeEngine: x0,autoStart: x1}), + _1199: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1199(f,arguments.length,x0) }), + _1200: x0 => ({runApp: x0}), + _1201: f => finalizeWrapper(f, function(x0,x1) { return dartInstance.exports._1201(f,arguments.length,x0,x1) }), + _1202: x0 => x0.length, + _1203: () => globalThis.window.ImageDecoder, + _1204: x0 => x0.tracks, + _1206: x0 => x0.completed, + _1208: x0 => x0.image, + _1214: x0 => x0.displayWidth, + _1215: x0 => x0.displayHeight, + _1216: x0 => x0.duration, + _1219: x0 => x0.ready, + _1220: x0 => x0.selectedTrack, + _1221: x0 => x0.repetitionCount, + _1222: x0 => x0.frameCount, + _1265: x0 => x0.createRange(), + _1266: (x0,x1) => x0.selectNode(x1), + _1267: x0 => x0.getSelection(), + _1268: x0 => x0.removeAllRanges(), + _1269: (x0,x1) => x0.addRange(x1), + _1270: (x0,x1) => x0.createElement(x1), + _1271: (x0,x1) => x0.append(x1), + _1272: (x0,x1,x2) => x0.insertRule(x1,x2), + _1273: (x0,x1) => x0.add(x1), + _1274: x0 => x0.preventDefault(), + _1275: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1275(f,arguments.length,x0) }), + _1276: (x0,x1,x2) => x0.addEventListener(x1,x2), + _1277: x0 => x0.remove(), + _1278: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1278(f,arguments.length,x0) }), + _1279: x0 => ({createScriptURL: x0}), + _1280: (x0,x1,x2) => x0.createPolicy(x1,x2), + _1281: (x0,x1,x2) => x0.createScriptURL(x1,x2), + _1282: x0 => x0.hasChildNodes(), + _1283: (x0,x1,x2) => x0.insertBefore(x1,x2), + _1284: (x0,x1) => x0.append(x1), + _1285: (x0,x1) => x0.querySelectorAll(x1), + _1286: (x0,x1) => x0.item(x1), + _1287: () => globalThis.window.navigator.userAgent, + _1288: f => finalizeWrapper(f, function(x0,x1,x2) { return dartInstance.exports._1288(f,arguments.length,x0,x1,x2) }), + _1290: (x0,x1,x2) => x0.setAttribute(x1,x2), + _1291: (x0,x1) => x0.removeAttribute(x1), + _1293: (x0,x1) => x0.getResponseHeader(x1), + _1316: (x0,x1) => x0.item(x1), + _1319: (x0,x1) => { x0.csp = x1 }, + _1320: x0 => x0.csp, + _1321: (x0,x1) => x0.getCookieExpirationDate(x1), + _1322: x0 => globalThis.Sentry.init(x0), + _1323: () => new Sentry.getClient(), + _1324: x0 => x0.getOptions(), + _1325: () => new Sentry.getIsolationScope(), + _1326: x0 => x0.getSession(), + _1327: (x0,x1) => x0.setSession(x1), + _1328: () => globalThis.Sentry.globalHandlersIntegration(), + _1329: () => globalThis.Sentry.dedupeIntegration(), + _1330: () => globalThis.Sentry.close(), + _1331: (x0,x1) => x0.sendEnvelope(x1), + _1332: x0 => globalThis.Sentry.startSession(x0), + _1333: () => globalThis.Sentry.captureSession(), + _1334: () => globalThis.globalThis, + _1335: x0 => globalThis.URL.revokeObjectURL(x0), + _1337: x0 => globalThis.URL.createObjectURL(x0), + _1343: (x0,x1) => x0.querySelector(x1), + _1344: (x0,x1) => x0.createElement(x1), + _1346: x0 => x0.click(), + _1347: x0 => x0.load(), + _1348: x0 => x0.play(), + _1349: x0 => x0.pause(), + _1350: x0 => x0.preventDefault(), + _1352: (x0,x1,x2) => x0.addEventListener(x1,x2), + _1353: (x0,x1,x2) => x0.removeEventListener(x1,x2), + _1354: (x0,x1) => x0.start(x1), + _1355: (x0,x1) => x0.end(x1), + _1356: (x0,x1,x2,x3) => x0.addEventListener(x1,x2,x3), + _1357: (x0,x1,x2,x3) => x0.removeEventListener(x1,x2,x3), + _1358: (x0,x1) => x0.getAttribute(x1), + _1359: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1359(f,arguments.length,x0) }), + _1360: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1360(f,arguments.length,x0) }), + _1361: (x0,x1) => x0.closest(x1), + _1362: (x0,x1,x2,x3) => x0.open(x1,x2,x3), + _1364: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1364(f,arguments.length,x0) }), + _1365: f => finalizeWrapper(f, function() { return dartInstance.exports._1365(f,arguments.length) }), + _1366: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1366(f,arguments.length,x0) }), + _1367: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1367(f,arguments.length,x0) }), + _1368: f => finalizeWrapper(f, function(x0,x1) { return dartInstance.exports._1368(f,arguments.length,x0,x1) }), + _1369: f => finalizeWrapper(f, function(x0,x1) { return dartInstance.exports._1369(f,arguments.length,x0,x1) }), + _1370: f => finalizeWrapper(f, function(x0,x1) { return dartInstance.exports._1370(f,arguments.length,x0,x1) }), + _1371: f => finalizeWrapper(f, function(x0,x1) { return dartInstance.exports._1371(f,arguments.length,x0,x1) }), + _1372: f => finalizeWrapper(f, function(x0,x1) { return dartInstance.exports._1372(f,arguments.length,x0,x1) }), + _1373: f => finalizeWrapper(f, function(x0,x1) { return dartInstance.exports._1373(f,arguments.length,x0,x1) }), + _1374: f => finalizeWrapper(f, function(x0,x1) { return dartInstance.exports._1374(f,arguments.length,x0,x1) }), + _1375: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1375(f,arguments.length,x0) }), + _1376: (x0,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11) => globalThis.flutter_dropzone_web.create(x0,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11), + _1377: (x0,x1) => globalThis.flutter_dropzone_web.setMIME(x0,x1), + _1378: (x0,x1) => globalThis.flutter_dropzone_web.setOperation(x0,x1), + _1379: (x0,x1) => globalThis.flutter_dropzone_web.setCursor(x0,x1), + _1380: (x0,x1) => x0.item(x1), + _1383: x0 => x0.arrayBuffer(), + _1385: x0 => x0.decode(), + _1386: (x0,x1,x2,x3) => x0.open(x1,x2,x3), + _1387: (x0,x1,x2) => x0.setRequestHeader(x1,x2), + _1388: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1388(f,arguments.length,x0) }), + _1389: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1389(f,arguments.length,x0) }), + _1390: x0 => x0.send(), + _1391: () => new XMLHttpRequest(), + _1392: (x0,x1) => x0.getItem(x1), + _1393: (x0,x1) => x0.removeItem(x1), + _1394: (x0,x1,x2) => x0.setItem(x1,x2), + _1409: x0 => ({type: x0}), + _1410: (x0,x1) => new Blob(x0,x1), + _1411: () => new FileReader(), + _1413: (x0,x1) => x0.readAsArrayBuffer(x1), + _1414: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1414(f,arguments.length,x0) }), + _1415: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1415(f,arguments.length,x0) }), + _1416: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1416(f,arguments.length,x0) }), + _1417: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1417(f,arguments.length,x0) }), + _1418: (x0,x1) => x0.removeChild(x1), + _1419: x0 => new Blob(x0), + _1421: x0 => x0.read(), + _1422: (x0,x1) => x0.getType(x1), + _1423: x0 => x0.text(), + _1426: (x0,x1) => x0.key(x1), + _1427: (x0,x1,x2,x3,x4,x5,x6,x7) => x0.unwrapKey(x1,x2,x3,x4,x5,x6,x7), + _1428: (x0,x1,x2,x3,x4,x5) => x0.importKey(x1,x2,x3,x4,x5), + _1429: (x0,x1,x2,x3) => x0.generateKey(x1,x2,x3), + _1430: (x0,x1,x2,x3,x4) => x0.wrapKey(x1,x2,x3,x4), + _1431: (x0,x1,x2) => x0.exportKey(x1,x2), + _1432: (x0,x1) => x0.getRandomValues(x1), + _1433: (x0,x1,x2,x3) => x0.encrypt(x1,x2,x3), + _1434: (x0,x1,x2,x3) => x0.decrypt(x1,x2,x3), + _1435: () => globalThis.removeSplashFromWeb(), + _1436: () => globalThis.catalyst_cardano.getWallets(), + _1437: Date.now, + _1438: secondsSinceEpoch => { + const date = new Date(secondsSinceEpoch * 1000); + const match = /\((.*)\)/.exec(date.toString()); + if (match == null) { + // This should never happen on any recent browser. + return ''; + } + return match[1]; + }, + _1439: s => new Date(s * 1000).getTimezoneOffset() * 60, + _1440: s => { + if (!/^\s*[+-]?(?:Infinity|NaN|(?:\.\d+|\d+(?:\.\d*)?)(?:[eE][+-]?\d+)?)\s*$/.test(s)) { + return NaN; + } + return parseFloat(s); + }, + _1441: () => { + let stackString = new Error().stack.toString(); + let frames = stackString.split('\n'); + let drop = 2; + if (frames[0] === 'Error') { + drop += 1; + } + return frames.slice(drop).join('\n'); + }, + _1442: () => typeof dartUseDateNowForTicks !== "undefined", + _1443: () => 1000 * performance.now(), + _1444: () => Date.now(), + _1445: () => { + // On browsers return `globalThis.location.href` + if (globalThis.location != null) { + return globalThis.location.href; + } + return null; + }, + _1446: () => { + return typeof process != "undefined" && + Object.prototype.toString.call(process) == "[object process]" && + process.platform == "win32" + }, + _1447: () => new WeakMap(), + _1448: (map, o) => map.get(o), + _1449: (map, o, v) => map.set(o, v), + _1450: x0 => new WeakRef(x0), + _1451: x0 => x0.deref(), + _1452: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1452(f,arguments.length,x0) }), + _1453: x0 => new FinalizationRegistry(x0), + _1454: (x0,x1,x2,x3) => x0.register(x1,x2,x3), + _1456: (x0,x1) => x0.unregister(x1), + _1458: () => globalThis.WeakRef, + _1459: () => globalThis.FinalizationRegistry, + _1461: x0 => x0.call(), + _1462: s => JSON.stringify(s), + _1463: s => printToConsole(s), + _1464: (o, p, r) => o.replaceAll(p, () => r), + _1465: (o, p, r) => o.replace(p, () => r), + _1466: Function.prototype.call.bind(String.prototype.toLowerCase), + _1467: s => s.toUpperCase(), + _1468: s => s.trim(), + _1469: s => s.trimLeft(), + _1470: s => s.trimRight(), + _1471: (string, times) => string.repeat(times), + _1472: Function.prototype.call.bind(String.prototype.indexOf), + _1473: (s, p, i) => s.lastIndexOf(p, i), + _1474: (string, token) => string.split(token), + _1475: Object.is, + _1476: o => o instanceof Array, + _1477: (a, i) => a.push(i), + _1478: (a, i) => a.splice(i, 1)[0], + _1480: (a, l) => a.length = l, + _1481: a => a.pop(), + _1482: (a, i) => a.splice(i, 1), + _1483: (a, s) => a.join(s), + _1484: (a, s, e) => a.slice(s, e), + _1485: (a, s, e) => a.splice(s, e), + _1486: (a, b) => a == b ? 0 : (a > b ? 1 : -1), + _1487: a => a.length, + _1488: (a, l) => a.length = l, + _1489: (a, i) => a[i], + _1490: (a, i, v) => a[i] = v, + _1492: o => { + if (o instanceof ArrayBuffer) return 0; + if (globalThis.SharedArrayBuffer !== undefined && + o instanceof SharedArrayBuffer) { + return 1; + } + return 2; + }, + _1493: (o, offsetInBytes, lengthInBytes) => { + var dst = new ArrayBuffer(lengthInBytes); + new Uint8Array(dst).set(new Uint8Array(o, offsetInBytes, lengthInBytes)); + return new DataView(dst); + }, + _1494: o => o instanceof DataView, + _1495: o => o instanceof Uint8Array, + _1496: (o, start, length) => new Uint8Array(o.buffer, o.byteOffset + start, length), + _1497: o => o instanceof Int8Array, + _1498: (o, start, length) => new Int8Array(o.buffer, o.byteOffset + start, length), + _1499: o => o instanceof Uint8ClampedArray, + _1500: (o, start, length) => new Uint8ClampedArray(o.buffer, o.byteOffset + start, length), + _1501: o => o instanceof Uint16Array, + _1502: (o, start, length) => new Uint16Array(o.buffer, o.byteOffset + start, length), + _1503: o => o instanceof Int16Array, + _1504: (o, start, length) => new Int16Array(o.buffer, o.byteOffset + start, length), + _1505: o => o instanceof Uint32Array, + _1506: (o, start, length) => new Uint32Array(o.buffer, o.byteOffset + start, length), + _1507: o => o instanceof Int32Array, + _1508: (o, start, length) => new Int32Array(o.buffer, o.byteOffset + start, length), + _1510: (o, start, length) => new BigInt64Array(o.buffer, o.byteOffset + start, length), + _1511: o => o instanceof Float32Array, + _1512: (o, start, length) => new Float32Array(o.buffer, o.byteOffset + start, length), + _1513: o => o instanceof Float64Array, + _1514: (o, start, length) => new Float64Array(o.buffer, o.byteOffset + start, length), + _1515: (t, s) => t.set(s), + _1516: l => new DataView(new ArrayBuffer(l)), + _1517: (o) => new DataView(o.buffer, o.byteOffset, o.byteLength), + _1518: o => o.byteLength, + _1519: o => o.buffer, + _1520: o => o.byteOffset, + _1521: Function.prototype.call.bind(Object.getOwnPropertyDescriptor(DataView.prototype, 'byteLength').get), + _1522: (b, o) => new DataView(b, o), + _1523: (b, o, l) => new DataView(b, o, l), + _1524: Function.prototype.call.bind(DataView.prototype.getUint8), + _1525: Function.prototype.call.bind(DataView.prototype.setUint8), + _1526: Function.prototype.call.bind(DataView.prototype.getInt8), + _1527: Function.prototype.call.bind(DataView.prototype.setInt8), + _1528: Function.prototype.call.bind(DataView.prototype.getUint16), + _1529: Function.prototype.call.bind(DataView.prototype.setUint16), + _1530: Function.prototype.call.bind(DataView.prototype.getInt16), + _1531: Function.prototype.call.bind(DataView.prototype.setInt16), + _1532: Function.prototype.call.bind(DataView.prototype.getUint32), + _1533: Function.prototype.call.bind(DataView.prototype.setUint32), + _1534: Function.prototype.call.bind(DataView.prototype.getInt32), + _1535: Function.prototype.call.bind(DataView.prototype.setInt32), + _1538: Function.prototype.call.bind(DataView.prototype.getBigInt64), + _1539: Function.prototype.call.bind(DataView.prototype.setBigInt64), + _1540: Function.prototype.call.bind(DataView.prototype.getFloat32), + _1541: Function.prototype.call.bind(DataView.prototype.setFloat32), + _1542: Function.prototype.call.bind(DataView.prototype.getFloat64), + _1543: Function.prototype.call.bind(DataView.prototype.setFloat64), + _1545: () => globalThis.performance, + _1546: () => globalThis.JSON, + _1547: x0 => x0.measure, + _1548: x0 => x0.mark, + _1549: x0 => x0.clearMeasures, + _1550: x0 => x0.clearMarks, + _1551: (x0,x1,x2,x3) => x0.measure(x1,x2,x3), + _1552: (x0,x1,x2) => x0.mark(x1,x2), + _1553: x0 => x0.clearMeasures(), + _1554: x0 => x0.clearMarks(), + _1555: (x0,x1) => x0.parse(x1), + _1556: (ms, c) => + setTimeout(() => dartInstance.exports.$invokeCallback(c),ms), + _1557: (handle) => clearTimeout(handle), + _1558: (ms, c) => + setInterval(() => dartInstance.exports.$invokeCallback(c), ms), + _1559: (handle) => clearInterval(handle), + _1560: (c) => + queueMicrotask(() => dartInstance.exports.$invokeCallback(c)), + _1561: () => Date.now(), + _1566: o => Object.keys(o), + _1567: (x0,x1) => x0.postMessage(x1), + _1569: x0 => new Worker(x0), + _1571: x0 => x0.getDirectory(), + _1572: x0 => ({create: x0}), + _1573: (x0,x1,x2) => x0.getFileHandle(x1,x2), + _1574: x0 => x0.createSyncAccessHandle(), + _1575: x0 => x0.close(), + _1578: x0 => x0.close(), + _1581: (x0,x1,x2) => x0.open(x1,x2), + _1587: x0 => x0.start(), + _1588: x0 => x0.close(), + _1589: x0 => x0.terminate(), + _1590: (x0,x1) => new SharedWorker(x0,x1), + _1591: (x0,x1,x2) => x0.postMessage(x1,x2), + _1592: (x0,x1,x2) => x0.postMessage(x1,x2), + _1593: () => new MessageChannel(), + _1598: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1598(f,arguments.length,x0) }), + _1599: x0 => x0.continue(), + _1600: () => globalThis.indexedDB, + _1602: x0 => x0.sqlite3_initialize, + _1604: (x0,x1,x2,x3,x4) => x0.sqlite3_open_v2(x1,x2,x3,x4), + _1605: (x0,x1) => x0.sqlite3_close_v2(x1), + _1606: (x0,x1,x2) => x0.sqlite3_extended_result_codes(x1,x2), + _1607: (x0,x1) => x0.sqlite3_extended_errcode(x1), + _1608: (x0,x1) => x0.sqlite3_errmsg(x1), + _1609: (x0,x1) => x0.sqlite3_errstr(x1), + _1610: x0 => x0.sqlite3_error_offset, + _1614: (x0,x1) => x0.sqlite3_last_insert_rowid(x1), + _1615: (x0,x1) => x0.sqlite3_changes(x1), + _1616: (x0,x1,x2,x3,x4,x5) => x0.sqlite3_exec(x1,x2,x3,x4,x5), + _1619: (x0,x1,x2,x3,x4,x5,x6) => x0.sqlite3_prepare_v3(x1,x2,x3,x4,x5,x6), + _1620: (x0,x1) => x0.sqlite3_finalize(x1), + _1621: (x0,x1) => x0.sqlite3_step(x1), + _1622: (x0,x1) => x0.sqlite3_reset(x1), + _1623: (x0,x1) => x0.sqlite3_stmt_isexplain(x1), + _1625: (x0,x1) => x0.sqlite3_column_count(x1), + _1626: (x0,x1) => x0.sqlite3_bind_parameter_count(x1), + _1628: (x0,x1,x2) => x0.sqlite3_column_name(x1,x2), + _1629: (x0,x1,x2,x3,x4,x5) => x0.sqlite3_bind_blob64(x1,x2,x3,x4,x5), + _1630: (x0,x1,x2,x3) => x0.sqlite3_bind_double(x1,x2,x3), + _1631: (x0,x1,x2,x3) => x0.sqlite3_bind_int64(x1,x2,x3), + _1632: (x0,x1,x2) => x0.sqlite3_bind_null(x1,x2), + _1633: (x0,x1,x2,x3,x4,x5) => x0.sqlite3_bind_text(x1,x2,x3,x4,x5), + _1634: (x0,x1,x2) => x0.sqlite3_column_blob(x1,x2), + _1635: (x0,x1,x2) => x0.sqlite3_column_double(x1,x2), + _1636: (x0,x1,x2) => x0.sqlite3_column_int64(x1,x2), + _1637: (x0,x1,x2) => x0.sqlite3_column_text(x1,x2), + _1638: (x0,x1,x2) => x0.sqlite3_column_bytes(x1,x2), + _1639: (x0,x1,x2) => x0.sqlite3_column_type(x1,x2), + _1640: (x0,x1) => x0.sqlite3_value_blob(x1), + _1641: (x0,x1) => x0.sqlite3_value_double(x1), + _1642: (x0,x1) => x0.sqlite3_value_type(x1), + _1643: (x0,x1) => x0.sqlite3_value_int64(x1), + _1644: (x0,x1) => x0.sqlite3_value_text(x1), + _1645: (x0,x1) => x0.sqlite3_value_bytes(x1), + _1648: (x0,x1) => x0.sqlite3_user_data(x1), + _1649: (x0,x1,x2,x3,x4) => x0.sqlite3_result_blob64(x1,x2,x3,x4), + _1650: (x0,x1,x2) => x0.sqlite3_result_double(x1,x2), + _1651: (x0,x1,x2,x3) => x0.sqlite3_result_error(x1,x2,x3), + _1652: (x0,x1,x2) => x0.sqlite3_result_int64(x1,x2), + _1653: (x0,x1) => x0.sqlite3_result_null(x1), + _1654: (x0,x1,x2,x3,x4) => x0.sqlite3_result_text(x1,x2,x3,x4), + _1655: x0 => x0.sqlite3_result_subtype, + _1674: (x0,x1) => x0.dart_sqlite3_malloc(x1), + _1675: (x0,x1) => x0.dart_sqlite3_free(x1), + _1676: (x0,x1,x2,x3) => x0.dart_sqlite3_register_vfs(x1,x2,x3), + _1677: (x0,x1,x2,x3,x4,x5) => x0.dart_sqlite3_create_scalar_function(x1,x2,x3,x4,x5), + _1680: x0 => x0.dart_sqlite3_updates, + _1681: x0 => x0.dart_sqlite3_commits, + _1682: x0 => x0.dart_sqlite3_rollbacks, + _1686: x0 => ({initial: x0}), + _1687: x0 => new WebAssembly.Memory(x0), + _1688: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1688(f,arguments.length,x0) }), + _1689: f => finalizeWrapper(f, function(x0,x1,x2,x3,x4) { return dartInstance.exports._1689(f,arguments.length,x0,x1,x2,x3,x4) }), + _1690: f => finalizeWrapper(f, function(x0,x1,x2) { return dartInstance.exports._1690(f,arguments.length,x0,x1,x2) }), + _1691: f => finalizeWrapper(f, function(x0,x1,x2,x3) { return dartInstance.exports._1691(f,arguments.length,x0,x1,x2,x3) }), + _1692: f => finalizeWrapper(f, function(x0,x1,x2,x3) { return dartInstance.exports._1692(f,arguments.length,x0,x1,x2,x3) }), + _1693: f => finalizeWrapper(f, function(x0,x1,x2) { return dartInstance.exports._1693(f,arguments.length,x0,x1,x2) }), + _1694: f => finalizeWrapper(f, function(x0,x1) { return dartInstance.exports._1694(f,arguments.length,x0,x1) }), + _1695: f => finalizeWrapper(f, function(x0,x1) { return dartInstance.exports._1695(f,arguments.length,x0,x1) }), + _1696: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1696(f,arguments.length,x0) }), + _1697: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1697(f,arguments.length,x0) }), + _1698: f => finalizeWrapper(f, function(x0,x1,x2,x3) { return dartInstance.exports._1698(f,arguments.length,x0,x1,x2,x3) }), + _1699: f => finalizeWrapper(f, function(x0,x1,x2,x3) { return dartInstance.exports._1699(f,arguments.length,x0,x1,x2,x3) }), + _1700: f => finalizeWrapper(f, function(x0,x1) { return dartInstance.exports._1700(f,arguments.length,x0,x1) }), + _1701: f => finalizeWrapper(f, function(x0,x1) { return dartInstance.exports._1701(f,arguments.length,x0,x1) }), + _1702: f => finalizeWrapper(f, function(x0,x1) { return dartInstance.exports._1702(f,arguments.length,x0,x1) }), + _1703: f => finalizeWrapper(f, function(x0,x1) { return dartInstance.exports._1703(f,arguments.length,x0,x1) }), + _1704: f => finalizeWrapper(f, function(x0,x1) { return dartInstance.exports._1704(f,arguments.length,x0,x1) }), + _1705: f => finalizeWrapper(f, function(x0,x1) { return dartInstance.exports._1705(f,arguments.length,x0,x1) }), + _1706: f => finalizeWrapper(f, function(x0,x1,x2) { return dartInstance.exports._1706(f,arguments.length,x0,x1,x2) }), + _1707: f => finalizeWrapper(f, function(x0,x1,x2) { return dartInstance.exports._1707(f,arguments.length,x0,x1,x2) }), + _1708: f => finalizeWrapper(f, function(x0,x1,x2) { return dartInstance.exports._1708(f,arguments.length,x0,x1,x2) }), + _1709: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1709(f,arguments.length,x0) }), + _1710: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1710(f,arguments.length,x0) }), + _1711: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1711(f,arguments.length,x0) }), + _1712: f => finalizeWrapper(f, function(x0,x1,x2,x3,x4) { return dartInstance.exports._1712(f,arguments.length,x0,x1,x2,x3,x4) }), + _1713: f => finalizeWrapper(f, function(x0,x1,x2,x3,x4) { return dartInstance.exports._1713(f,arguments.length,x0,x1,x2,x3,x4) }), + _1714: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1714(f,arguments.length,x0) }), + _1715: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1715(f,arguments.length,x0) }), + _1716: f => finalizeWrapper(f, function(x0,x1) { return dartInstance.exports._1716(f,arguments.length,x0,x1) }), + _1717: f => finalizeWrapper(f, function(x0,x1) { return dartInstance.exports._1717(f,arguments.length,x0,x1) }), + _1718: f => finalizeWrapper(f, function(x0,x1,x2) { return dartInstance.exports._1718(f,arguments.length,x0,x1,x2) }), + _1720: (x0,x1,x2,x3) => x0.call(x1,x2,x3), + _1725: x0 => new URL(x0), + _1726: (x0,x1) => new URL(x0,x1), + _1727: (x0,x1) => globalThis.fetch(x0,x1), + _1729: (x0,x1) => ({i: x0,p: x1}), + _1730: (x0,x1) => ({c: x0,r: x1}), + _1731: x0 => x0.i, + _1732: x0 => x0.p, + _1733: x0 => x0.c, + _1734: x0 => x0.r, + _1735: x0 => new SharedArrayBuffer(x0), + _1736: x0 => ({at: x0}), + _1737: x0 => x0.getSize(), + _1738: (x0,x1) => x0.truncate(x1), + _1739: x0 => x0.flush(), + _1742: x0 => x0.synchronizationBuffer, + _1743: x0 => x0.communicationBuffer, + _1744: (x0,x1,x2,x3) => ({clientVersion: x0,root: x1,synchronizationBuffer: x2,communicationBuffer: x3}), + _1745: (x0,x1) => globalThis.IDBKeyRange.bound(x0,x1), + _1746: x0 => ({autoIncrement: x0}), + _1747: (x0,x1,x2) => x0.createObjectStore(x1,x2), + _1748: x0 => ({unique: x0}), + _1749: (x0,x1,x2,x3) => x0.createIndex(x1,x2,x3), + _1750: (x0,x1) => x0.createObjectStore(x1), + _1751: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1751(f,arguments.length,x0) }), + _1752: (x0,x1,x2) => x0.transaction(x1,x2), + _1753: (x0,x1) => x0.objectStore(x1), + _1755: (x0,x1) => x0.index(x1), + _1756: x0 => x0.openKeyCursor(), + _1757: (x0,x1) => x0.getKey(x1), + _1758: (x0,x1) => ({name: x0,length: x1}), + _1759: (x0,x1) => x0.put(x1), + _1760: (x0,x1) => x0.get(x1), + _1761: (x0,x1) => x0.openCursor(x1), + _1762: x0 => globalThis.IDBKeyRange.only(x0), + _1763: (x0,x1,x2) => x0.put(x1,x2), + _1764: (x0,x1) => x0.update(x1), + _1765: (x0,x1) => x0.delete(x1), + _1766: x0 => x0.name, + _1767: x0 => x0.length, + _1770: x0 => globalThis.BigInt(x0), + _1771: x0 => globalThis.Number(x0), + _1778: () => globalThis.navigator, + _1779: (x0,x1) => x0.read(x1), + _1780: (x0,x1,x2) => x0.read(x1,x2), + _1781: (x0,x1) => x0.write(x1), + _1782: (x0,x1,x2) => x0.write(x1,x2), + _1783: x0 => ({create: x0}), + _1784: (x0,x1,x2) => x0.getDirectoryHandle(x1,x2), + _1785: x0 => new BroadcastChannel(x0), + _1786: x0 => globalThis.Array.isArray(x0), + _1787: (x0,x1) => x0.postMessage(x1), + _1788: x0 => x0.close(), + _1789: (x0,x1) => ({kind: x0,table: x1}), + _1790: x0 => x0.kind, + _1791: x0 => x0.table, + _1793: x0 => x0.getBalance(), + _1794: x0 => x0.getChangeAddress(), + _1796: x0 => x0.getNetworkId(), + _1797: x0 => x0.getRewardAddresses(), + _1801: x0 => x0.getUtxos(), + _1805: (x0,x1,x2) => x0.signTx(x1,x2), + _1806: (x0,x1) => x0.submitTx(x1), + _1811: (x0,x1) => x0.enable(x1), + _1814: x0 => x0.icon, + _1815: x0 => x0.name, + _1828: (x0,x1,x2,x3,x4,x5) => x0.frb_pde_ffi_dispatcher_primary(x1,x2,x3,x4,x5), + _1829: (x0,x1,x2,x3,x4) => x0.frb_pde_ffi_dispatcher_sync(x1,x2,x3,x4), + _1831: x0 => x0.frb_get_rust_content_hash(), + _1835: (x0,x1) => x0.rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBip32Ed25519Signature(x1), + _1836: (x0,x1) => x0.rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBip32Ed25519Signature(x1), + _1837: (x0,x1) => x0.rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBip32Ed25519XPrivateKey(x1), + _1838: (x0,x1) => x0.rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBip32Ed25519XPrivateKey(x1), + _1839: (x0,x1) => x0.rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBip32Ed25519XPublicKey(x1), + _1840: (x0,x1) => x0.rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBip32Ed25519XPublicKey(x1), + _1841: () => globalThis.key_derivation_wasm_bindgen, + _1842: x0 => x0.deviceMemory, + _1844: (x0,x1,x2,x3) => ({name: x0,iv: x1,additionalData: x2,tagLength: x3}), + _1861: (x0,x1) => x0.call(x1), + _1862: (x0,x1) => x0.warn(x1), + _1863: () => new AbortController(), + _1864: x0 => x0.abort(), + _1865: (x0,x1,x2,x3,x4,x5) => ({method: x0,headers: x1,body: x2,credentials: x3,redirect: x4,signal: x5}), + _1866: (x0,x1) => globalThis.fetch(x0,x1), + _1867: (x0,x1) => x0.get(x1), + _1868: f => finalizeWrapper(f, function(x0,x1,x2) { return dartInstance.exports._1868(f,arguments.length,x0,x1,x2) }), + _1869: (x0,x1) => x0.forEach(x1), + _1870: x0 => x0.getReader(), + _1871: x0 => x0.read(), + _1872: x0 => x0.cancel(), + _1873: () => new XMLHttpRequest(), + _1874: (x0,x1,x2,x3) => x0.open(x1,x2,x3), + _1875: x0 => x0.send(), + _1879: (x0,x1,x2) => x0.setRequestHeader(x1,x2), + _1880: (x0,x1) => x0.send(x1), + _1882: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1882(f,arguments.length,x0) }), + _1883: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._1883(f,arguments.length,x0) }), + _1888: x0 => x0.exports, + _1889: (x0,x1) => globalThis.WebAssembly.instantiateStreaming(x0,x1), + _1890: x0 => x0.instance, + _1892: x0 => x0.buffer, + _1895: (x0,x1) => x0.matchMedia(x1), + _1897: () => globalThis.crypto.subtle, + _1898: (x0,x1,x2) => globalThis.crypto.subtle.decrypt(x0,x1,x2), + _1902: (x0,x1,x2) => globalThis.crypto.subtle.encrypt(x0,x1,x2), + _1905: (x0,x1,x2,x3,x4) => globalThis.crypto.subtle.importKey(x0,x1,x2,x3,x4), + _1943: () => globalThis.window.flutter_inappwebview, + _1947: (x0,x1) => { x0.nativeCommunication = x1 }, + _1957: (s, m) => { + try { + return new RegExp(s, m); + } catch (e) { + return String(e); + } + }, + _1958: (x0,x1) => x0.exec(x1), + _1959: (x0,x1) => x0.test(x1), + _1960: x0 => x0.pop(), + _1962: o => o === undefined, + _1964: o => typeof o === 'function' && o[jsWrappedDartFunctionSymbol] === true, + _1966: o => { + const proto = Object.getPrototypeOf(o); + return proto === Object.prototype || proto === null; + }, + _1967: o => o instanceof RegExp, + _1968: (l, r) => l === r, + _1969: o => o, + _1970: o => o, + _1971: o => o, + _1972: b => !!b, + _1973: o => o.length, + _1975: (o, i) => o[i], + _1976: f => f.dartFunction, + _1977: () => ({}), + _1978: () => [], + _1980: () => globalThis, + _1981: (constructor, args) => { + const factoryFunction = constructor.bind.apply( + constructor, [null, ...args]); + return new factoryFunction(); + }, + _1982: (o, p) => p in o, + _1983: (o, p) => o[p], + _1984: (o, p, v) => o[p] = v, + _1985: (o, m, a) => o[m].apply(o, a), + _1987: o => String(o), + _1988: (p, s, f) => p.then(s, (e) => f(e, e === undefined)), + _1989: o => { + if (o === undefined) return 1; + var type = typeof o; + if (type === 'boolean') return 2; + if (type === 'number') return 3; + if (type === 'string') return 4; + if (o instanceof Array) return 5; + if (ArrayBuffer.isView(o)) { + if (o instanceof Int8Array) return 6; + if (o instanceof Uint8Array) return 7; + if (o instanceof Uint8ClampedArray) return 8; + if (o instanceof Int16Array) return 9; + if (o instanceof Uint16Array) return 10; + if (o instanceof Int32Array) return 11; + if (o instanceof Uint32Array) return 12; + if (o instanceof Float32Array) return 13; + if (o instanceof Float64Array) return 14; + if (o instanceof DataView) return 15; + } + if (o instanceof ArrayBuffer) return 16; + // Feature check for `SharedArrayBuffer` before doing a type-check. + if (globalThis.SharedArrayBuffer !== undefined && + o instanceof SharedArrayBuffer) { + return 17; + } + return 18; + }, + _1990: o => [o], + _1991: (o0, o1) => [o0, o1], + _1992: (o0, o1, o2) => [o0, o1, o2], + _1993: (o0, o1, o2, o3) => [o0, o1, o2, o3], + _1994: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { + const getValue = dartInstance.exports.$wasmI8ArrayGet; + for (let i = 0; i < length; i++) { + jsArray[jsArrayOffset + i] = getValue(wasmArray, wasmArrayOffset + i); + } + }, + _1995: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { + const setValue = dartInstance.exports.$wasmI8ArraySet; + for (let i = 0; i < length; i++) { + setValue(wasmArray, wasmArrayOffset + i, jsArray[jsArrayOffset + i]); + } + }, + _1996: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { + const getValue = dartInstance.exports.$wasmI16ArrayGet; + for (let i = 0; i < length; i++) { + jsArray[jsArrayOffset + i] = getValue(wasmArray, wasmArrayOffset + i); + } + }, + _1997: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { + const setValue = dartInstance.exports.$wasmI16ArraySet; + for (let i = 0; i < length; i++) { + setValue(wasmArray, wasmArrayOffset + i, jsArray[jsArrayOffset + i]); + } + }, + _1998: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { + const getValue = dartInstance.exports.$wasmI32ArrayGet; + for (let i = 0; i < length; i++) { + jsArray[jsArrayOffset + i] = getValue(wasmArray, wasmArrayOffset + i); + } + }, + _1999: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { + const setValue = dartInstance.exports.$wasmI32ArraySet; + for (let i = 0; i < length; i++) { + setValue(wasmArray, wasmArrayOffset + i, jsArray[jsArrayOffset + i]); + } + }, + _2000: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { + const getValue = dartInstance.exports.$wasmF32ArrayGet; + for (let i = 0; i < length; i++) { + jsArray[jsArrayOffset + i] = getValue(wasmArray, wasmArrayOffset + i); + } + }, + _2001: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { + const setValue = dartInstance.exports.$wasmF32ArraySet; + for (let i = 0; i < length; i++) { + setValue(wasmArray, wasmArrayOffset + i, jsArray[jsArrayOffset + i]); + } + }, + _2002: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { + const getValue = dartInstance.exports.$wasmF64ArrayGet; + for (let i = 0; i < length; i++) { + jsArray[jsArrayOffset + i] = getValue(wasmArray, wasmArrayOffset + i); + } + }, + _2003: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { + const setValue = dartInstance.exports.$wasmF64ArraySet; + for (let i = 0; i < length; i++) { + setValue(wasmArray, wasmArrayOffset + i, jsArray[jsArrayOffset + i]); + } + }, + _2004: x0 => new ArrayBuffer(x0), + _2005: s => { + if (/[[\]{}()*+?.\\^$|]/.test(s)) { + s = s.replace(/[[\]{}()*+?.\\^$|]/g, '\\$&'); + } + return s; + }, + _2006: x0 => x0.input, + _2007: x0 => x0.index, + _2008: x0 => x0.groups, + _2009: x0 => x0.flags, + _2010: x0 => x0.multiline, + _2011: x0 => x0.ignoreCase, + _2012: x0 => x0.unicode, + _2013: x0 => x0.dotAll, + _2014: (x0,x1) => { x0.lastIndex = x1 }, + _2015: (o, p) => p in o, + _2016: (o, p) => o[p], + _2017: (o, p, v) => o[p] = v, + _2019: (x0,x1,x2) => globalThis.Atomics.wait(x0,x1,x2), + _2021: (x0,x1,x2) => globalThis.Atomics.notify(x0,x1,x2), + _2022: (x0,x1,x2) => globalThis.Atomics.store(x0,x1,x2), + _2023: (x0,x1) => globalThis.Atomics.load(x0,x1), + _2024: () => globalThis.Int32Array, + _2026: () => globalThis.Uint8Array, + _2028: () => globalThis.DataView, + _2030: x0 => x0.byteLength, + _2031: x0 => x0.random(), + _2032: (x0,x1) => x0.getRandomValues(x1), + _2033: () => globalThis.crypto, + _2034: () => globalThis.Math, + _2035: Function.prototype.call.bind(Number.prototype.toString), + _2036: Function.prototype.call.bind(BigInt.prototype.toString), + _2037: Function.prototype.call.bind(Number.prototype.toString), + _2038: (d, digits) => d.toFixed(digits), + _2042: x0 => new Function(x0), + _2043: x0 => x0.call(), + _2044: () => globalThis.crossOriginIsolated, + _2046: () => globalThis.document, + _2047: () => globalThis.window, + _2052: (x0,x1) => { x0.height = x1 }, + _2054: (x0,x1) => { x0.width = x1 }, + _2057: x0 => x0.head, + _2058: x0 => x0.classList, + _2062: (x0,x1) => { x0.innerText = x1 }, + _2063: x0 => x0.style, + _2065: x0 => x0.sheet, + _2066: x0 => x0.src, + _2067: (x0,x1) => { x0.src = x1 }, + _2068: x0 => x0.naturalWidth, + _2069: x0 => x0.naturalHeight, + _2076: x0 => x0.offsetX, + _2077: x0 => x0.offsetY, + _2078: x0 => x0.button, + _2085: x0 => x0.status, + _2086: (x0,x1) => { x0.responseType = x1 }, + _2088: x0 => x0.response, + _2133: x0 => x0.status, + _2136: (x0,x1) => { x0.responseType = x1 }, + _2137: x0 => x0.response, + _2138: x0 => x0.responseText, + _2197: (x0,x1) => { x0.draggable = x1 }, + _2203: (x0,x1) => { x0.innerText = x1 }, + _2213: x0 => x0.style, + _2570: (x0,x1) => { x0.target = x1 }, + _2572: (x0,x1) => { x0.download = x1 }, + _2597: (x0,x1) => { x0.href = x1 }, + _2689: x0 => x0.src, + _2690: (x0,x1) => { x0.src = x1 }, + _2693: x0 => x0.name, + _2694: (x0,x1) => { x0.name = x1 }, + _2695: x0 => x0.sandbox, + _2696: x0 => x0.allow, + _2697: (x0,x1) => { x0.allow = x1 }, + _2698: x0 => x0.allowFullscreen, + _2699: (x0,x1) => { x0.allowFullscreen = x1 }, + _2704: x0 => x0.referrerPolicy, + _2705: (x0,x1) => { x0.referrerPolicy = x1 }, + _2785: x0 => x0.videoWidth, + _2786: x0 => x0.videoHeight, + _2790: (x0,x1) => { x0.playsInline = x1 }, + _2816: x0 => x0.error, + _2818: (x0,x1) => { x0.src = x1 }, + _2827: x0 => x0.buffered, + _2830: x0 => x0.currentTime, + _2831: (x0,x1) => { x0.currentTime = x1 }, + _2832: x0 => x0.duration, + _2837: (x0,x1) => { x0.playbackRate = x1 }, + _2844: (x0,x1) => { x0.autoplay = x1 }, + _2846: (x0,x1) => { x0.loop = x1 }, + _2848: (x0,x1) => { x0.controls = x1 }, + _2850: (x0,x1) => { x0.volume = x1 }, + _2852: (x0,x1) => { x0.muted = x1 }, + _2867: x0 => x0.code, + _2868: x0 => x0.message, + _2941: x0 => x0.length, + _3137: (x0,x1) => { x0.accept = x1 }, + _3151: x0 => x0.files, + _3177: (x0,x1) => { x0.multiple = x1 }, + _3195: (x0,x1) => { x0.type = x1 }, + _3445: (x0,x1) => { x0.src = x1 }, + _3447: (x0,x1) => { x0.type = x1 }, + _3453: (x0,x1) => { x0.defer = x1 }, + _3455: (x0,x1) => { x0.crossOrigin = x1 }, + _3459: (x0,x1) => { x0.integrity = x1 }, + _3913: () => globalThis.window, + _3952: x0 => x0.document, + _3955: x0 => x0.location, + _3974: x0 => x0.navigator, + _3978: x0 => x0.screen, + _3990: x0 => x0.devicePixelRatio, + _4229: x0 => x0.isSecureContext, + _4232: x0 => x0.crypto, + _4236: x0 => x0.trustedTypes, + _4237: x0 => x0.sessionStorage, + _4238: x0 => x0.localStorage, + _4248: x0 => x0.origin, + _4257: x0 => x0.pathname, + _4342: x0 => x0.clipboard, + _4344: x0 => x0.geolocation, + _4347: x0 => x0.mediaDevices, + _4349: x0 => x0.permissions, + _4350: x0 => x0.maxTouchPoints, + _4360: x0 => x0.platform, + _4363: x0 => x0.userAgent, + _4364: x0 => x0.vendor, + _4369: x0 => x0.onLine, + _4376: x0 => x0.storage, + _4414: x0 => x0.data, + _4444: x0 => x0.port1, + _4445: x0 => x0.port2, + _4447: (x0,x1) => { x0.onmessage = x1 }, + _4525: x0 => x0.port, + _4560: x0 => x0.length, + _6464: x0 => x0.type, + _6465: x0 => x0.target, + _6505: x0 => x0.signal, + _6514: x0 => x0.length, + _6557: x0 => x0.baseURI, + _6563: x0 => x0.firstChild, + _6574: () => globalThis.document, + _6655: x0 => x0.body, + _6657: x0 => x0.head, + _6984: x0 => x0.tagName, + _6985: x0 => x0.id, + _6986: (x0,x1) => { x0.id = x1 }, + _7013: x0 => x0.children, + _7216: x0 => x0.length, + _7420: x0 => x0.ctrlKey, + _7421: x0 => x0.shiftKey, + _7422: x0 => x0.altKey, + _7423: x0 => x0.metaKey, + _8331: x0 => x0.value, + _8333: x0 => x0.done, + _8495: x0 => x0.size, + _8496: x0 => x0.type, + _8503: x0 => x0.name, + _8509: x0 => x0.length, + _8514: x0 => x0.result, + _9010: x0 => x0.url, + _9012: x0 => x0.status, + _9014: x0 => x0.statusText, + _9015: x0 => x0.headers, + _9016: x0 => x0.body, + _9091: x0 => x0.types, + _9281: x0 => x0.type, + _9296: x0 => x0.matches, + _9307: x0 => x0.availWidth, + _9308: x0 => x0.availHeight, + _9309: x0 => x0.width, + _9310: x0 => x0.height, + _9313: x0 => x0.orientation, + _10465: x0 => x0.result, + _10466: x0 => x0.error, + _10477: (x0,x1) => { x0.onupgradeneeded = x1 }, + _10479: x0 => x0.oldVersion, + _10558: x0 => x0.key, + _10559: x0 => x0.primaryKey, + _10561: x0 => x0.value, + _11021: (x0,x1) => { x0.alignSelf = x1 }, + _11039: (x0,x1) => { x0.animationDuration = x1 }, + _11045: (x0,x1) => { x0.animationName = x1 }, + _11121: (x0,x1) => { x0.border = x1 }, + _11389: (x0,x1) => { x0.cursor = x1 }, + _11399: (x0,x1) => { x0.display = x1 }, + _11563: (x0,x1) => { x0.height = x1 }, + _11649: (x0,x1) => { x0.margin = x1 }, + _11775: (x0,x1) => { x0.opacity = x1 }, + _11887: (x0,x1) => { x0.pointerEvents = x1 }, + _12253: (x0,x1) => { x0.width = x1 }, + _12621: x0 => x0.name, + _12622: x0 => x0.message, + _12625: x0 => x0.subtle, + _13328: () => globalThis.console, + _13354: () => globalThis.window.flutterCanvasKit, + _13355: () => globalThis.window._flutter_skwasmInstance, + + }; + + const baseImports = { + dart2wasm: dart2wasm, + Math: Math, + Date: Date, + Object: Object, + Array: Array, + Reflect: Reflect, + S: new Proxy({}, { get(_, prop) { return prop; } }), + + }; + + const jsStringPolyfill = { + "charCodeAt": (s, i) => s.charCodeAt(i), + "compare": (s1, s2) => { + if (s1 < s2) return -1; + if (s1 > s2) return 1; + return 0; + }, + "concat": (s1, s2) => s1 + s2, + "equals": (s1, s2) => s1 === s2, + "fromCharCode": (i) => String.fromCharCode(i), + "length": (s) => s.length, + "substring": (s, a, b) => s.substring(a, b), + "fromCharCodeArray": (a, start, end) => { + if (end <= start) return ''; + + const read = dartInstance.exports.$wasmI16ArrayGet; + let result = ''; + let index = start; + const chunkLength = Math.min(end - index, 500); + let array = new Array(chunkLength); + while (index < end) { + const newChunkLength = Math.min(end - index, 500); + for (let i = 0; i < newChunkLength; i++) { + array[i] = read(a, index++); + } + if (newChunkLength < chunkLength) { + array = array.slice(0, newChunkLength); + } + result += String.fromCharCode(...array); + } + return result; + }, + "intoCharCodeArray": (s, a, start) => { + if (s === '') return 0; + + const write = dartInstance.exports.$wasmI16ArraySet; + for (var i = 0; i < s.length; ++i) { + write(a, start++, s.charCodeAt(i)); + } + return s.length; + }, + "test": (s) => typeof s == "string", + }; + + + + + dartInstance = await WebAssembly.instantiate(this.module, { + ...baseImports, + ...additionalImports, + + "wasm:js-string": jsStringPolyfill, + }); + + return new InstantiatedApp(this, dartInstance); + } +} + +class InstantiatedApp { + constructor(compiledApp, instantiatedModule) { + this.compiledApp = compiledApp; + this.instantiatedModule = instantiatedModule; + } + + // Call the main function with the given arguments. + invokeMain(...args) { + this.instantiatedModule.exports.$invokeMain(args); + } +} diff --git a/catalyst_voices/scripts/version_web_assets/test/helper/build_wasm/main.dart.wasm b/catalyst_voices/scripts/version_web_assets/test/helper/build_wasm/main.dart.wasm new file mode 100644 index 000000000000..6494bc3175f8 Binary files /dev/null and b/catalyst_voices/scripts/version_web_assets/test/helper/build_wasm/main.dart.wasm differ diff --git a/catalyst_voices/scripts/version_web_assets/test/helper/build_wasm/sqlite3.v1.wasm b/catalyst_voices/scripts/version_web_assets/test/helper/build_wasm/sqlite3.v1.wasm new file mode 100644 index 000000000000..85cb10e48ecf Binary files /dev/null and b/catalyst_voices/scripts/version_web_assets/test/helper/build_wasm/sqlite3.v1.wasm differ diff --git a/catalyst_voices/scripts/version_web_assets/test/helper/build_wasm/version.json b/catalyst_voices/scripts/version_web_assets/test/helper/build_wasm/version.json new file mode 100644 index 000000000000..8d6e20dcf7ed --- /dev/null +++ b/catalyst_voices/scripts/version_web_assets/test/helper/build_wasm/version.json @@ -0,0 +1 @@ +{"app_name":"catalyst_voices","version":"0.1.0","build_number":"1","package_name":"catalyst_voices"} \ No newline at end of file diff --git a/catalyst_voices/scripts/version_web_assets/test/web_asset_versioner_test.dart b/catalyst_voices/scripts/version_web_assets/test/web_asset_versioner_test.dart new file mode 100644 index 000000000000..9f656a150dc7 --- /dev/null +++ b/catalyst_voices/scripts/version_web_assets/test/web_asset_versioner_test.dart @@ -0,0 +1,102 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:path/path.dart' as path; +import 'package:test/test.dart'; + +void main() { + group('WASM build type', () { + const String mainPath = "scripts/version_web_assets/test"; + final src = Directory('$mainPath/helper/build_wasm'); + final dst = Directory('$mainPath/helper/tmp'); + late final Map assetVersionData; + + setUpAll(() async { + if (dst.existsSync()) { + await dst.delete(recursive: true); + } + dst.createSync(recursive: true); + + await for (var entity in src.list(recursive: false)) { + final name = path.basename(entity.path); + if (entity is File) { + await entity.copy('${dst.path}/$name'); + } else if (entity is Directory) { + await _copyDir(entity, Directory('${dst.path}/$name')); + } + } + + await Process.run('dart', [ + 'run', + 'scripts/version_web_assets/version_web_assets.dart', + '--wasm=true', + '--build-dir=${dst.path}', + ]); + + final file = File('${dst.path}/asset_version.json'); + final fileData = file.readAsStringSync(); + assetVersionData = jsonDecode(fileData) as Map; + }); + + tearDownAll(() async { + if (await dst.exists()) { + await dst.delete(recursive: true); + } + }); + + test("WebAssetVersioner find manually version files", () async { + expect(assetVersionData['asset_hashes']['sqlite3.wasm'], '1'); + expect(assetVersionData['asset_hashes']['drift_worker.js'], '1'); + }); + + test( + "WebAssetVersioner add same hash for symbols file as parent has", + () async { + final parentHash = + assetVersionData['asset_hashes']['canvaskit/skwasm_heavy.js'] // cspell:disable-line + as String; + final symbolHash = + assetVersionData['asset_hashes']['canvaskit/skwasm_heavy.js.symbols'] // cspell:disable-line + as String; + + expect(parentHash, symbolHash); + }, + ); + + test('DirectRefUpdater updates correctly refs', () { + final versionedCanvaskit = + assetVersionData['versioned_assets']['canvaskit/canvaskit.js'] + as String; + final file = File('${dst.path}/$versionedCanvaskit'); + final content = file.readAsStringSync(); + + final versionedCanvaskitWasm = + assetVersionData['asset_hashes']['canvaskit/canvaskit.wasm'] + as String; + + final wasmRef = content.contains(versionedCanvaskitWasm); + final notContainsNoVersionWasm = !content.contains( + 'canvaskit/canvaskit.wasm', + ); + + expect(wasmRef, isTrue); + expect(notContainsNoVersionWasm, isTrue); + }); + }); +} + +Future _copyDir(Directory source, Directory destination) async { + if (!destination.existsSync()) { + destination.createSync(recursive: true); + } + + await for (var entity in source.list(recursive: false)) { + final name = path.basename(entity.path); + + if (entity is File) { + await entity.copy('${destination.path}/$name'); + } else if (entity is Directory) { + await _copyDir(entity, Directory('${destination.path}/$name')); + } + } +} diff --git a/catalyst_voices/scripts/version_web_assets/version_web_assets.dart b/catalyst_voices/scripts/version_web_assets/version_web_assets.dart new file mode 100644 index 000000000000..6af13ad37766 --- /dev/null +++ b/catalyst_voices/scripts/version_web_assets/version_web_assets.dart @@ -0,0 +1,87 @@ +import 'dart:io'; + +import 'package:args/args.dart'; + +import 'web_asset_versioner.dart'; + +/// Versions Flutter web assets by renaming files with content-based MD5 hashes +/// to prevent browser caching issues. +/// +/// Usage: dart run version_web_assets.dart [build-dir] +/// - build-dir: Path to the build/web directory (default: apps/voices/build/web) +/// +/// This script: +/// 1. Calculates MD5 hash for each auto-versioned file and renames them +/// 2. Updates all asset references in HTML and JavaScript files +/// 3. Generates a manifest file with all versioned assets +Future main(List args) async { + final parser = ArgParser() + ..addOption( + 'build-dir', + abbr: 'b', + defaultsTo: 'apps/voices/build/web', + help: 'Path to the build/web directory', + ) + ..addFlag( + 'verbose', + abbr: 'v', + defaultsTo: true, + help: 'Enable verbose logging', + ) + ..addOption( + 'wasm', + abbr: 'w', + defaultsTo: 'true', + allowed: ['true', 'false'], + help: 'Include main.dart.wasm in versioning (true/false)', + ) + ..addFlag( + 'help', + abbr: 'h', + negatable: false, + help: 'Show usage information', + ); + + ArgResults argResults; + try { + argResults = parser.parse(args); + } catch (e) { + stderr.writeln('Error parsing arguments: $e'); + _printUsage(parser); + exitCode = 1; + return; + } + + if (argResults['help'] as bool) { + _printUsage(parser); + return; + } + + final buildDir = argResults['build-dir'] as String; + final verbose = argResults['verbose'] as bool; + final wasm = (argResults['wasm'] as String) == 'true'; + + final versioner = WebAssetVersioner( + buildDir: buildDir, + verbose: verbose, + wasm: wasm, + ); + + try { + await versioner.versionAssets(); + stdout.writeln('\n✓ Web asset versioning completed successfully!'); + } catch (e, stackTrace) { + stderr.writeln('\n✗ Error versioning web assets: $e'); + if (verbose) { + stderr.writeln(stackTrace); + } + exitCode = 1; + } +} + +void _printUsage(ArgParser parser) { + stdout.writeln('Usage: dart run version_web_assets.dart [options]'); + stdout.writeln('\nVersions Flutter web assets with content-based MD5 hashes'); + stdout.writeln('\nOptions:'); + stdout.writeln(parser.usage); +} diff --git a/catalyst_voices/scripts/version_web_assets/web_asset_versioner.dart b/catalyst_voices/scripts/version_web_assets/web_asset_versioner.dart new file mode 100644 index 000000000000..5aa4155a3110 --- /dev/null +++ b/catalyst_voices/scripts/version_web_assets/web_asset_versioner.dart @@ -0,0 +1,392 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:crypto/crypto.dart'; +import 'package:path/path.dart' as path; + +import 'asset_version_manifest.dart'; +import 'ref_updater.dart'; + +class WebAssetVersioner { + //version of these files should be: v1, v2, v3 + static const manuallyVersionedFiles = [ + 'sqlite3.wasm', + 'drift_worker.js', + 'assets/packages/catalyst_key_derivation/assets/js/*/catalyst_key_derivation_bg.wasm', + 'assets/packages/catalyst_key_derivation/assets/js/*/catalyst_key_derivation.js', + 'assets/packages/catalyst_compression/assets/js/*/catalyst_compression_bg.wasm', + 'assets/packages/catalyst_compression/assets/js/*/catalyst_compression.js', + ]; + + final String buildDir; + final bool verbose; + final bool wasm; + + /// Maps original filename to versioned filename + final Map _versionMap = {}; + + /// Maps original filename to hash + final Map _hashMap = {}; + + WebAssetVersioner({ + required this.buildDir, + this.verbose = true, + required this.wasm, + }); + + /// Files that will be automatically versioned with content-based MD5 hashes + List get autoVersionedFiles => [ + 'flutter_bootstrap.js', + 'main.dart.js', + if (wasm) 'main.dart.wasm', + if (wasm) 'main.dart.mjs', + 'canvaskit/canvaskit.wasm', + 'canvaskit/canvaskit.js', + 'canvaskit/canvaskit.js.symbols', + 'canvaskit/skwasm_heavy.wasm', //cspell:disable-line + 'canvaskit/skwasm_heavy.js', //cspell:disable-line + 'canvaskit/skwasm_heavy.js.symbols', //cspell:disable-line + 'canvaskit/skwasm.wasm', //cspell:disable-line + 'canvaskit/skwasm.js', //cspell:disable-line + 'canvaskit/skwasm.js.symbols', //cspell:disable-line + 'canvaskit/chromium/canvaskit.wasm', + 'canvaskit/chromium/canvaskit.js', + 'canvaskit/chromium/canvaskit.js.symbols', + ]; + + Future versionAssets() async { + _log('Starting web asset versioning...'); + _log('Build directory: $buildDir\n'); + + final buildDirectory = Directory(buildDir); + if (!buildDirectory.existsSync()) { + throw Exception('Build directory not found: $buildDir'); + } + + _log('Find manually versioned files...'); + _findManuallyVersionFiles(); + + _log('Calculating MD5 hashes and rename files...'); + await _calculateHashesAndRenameFiles(); + + _log('\nUpdating asset references in HTML and JavaScript files...'); + await _updateAssetReferences(); + + _log('\nGenerating Asset Version File...'); + _generateAssetVersionFile(); + + _log('\nGenerating Caddyfile Snippet'); + _generateCaddyfileSnippet(); + } + + Future _calculateHashesAndRenameFiles() async { + for (final filePath in autoVersionedFiles) { + final file = File(path.join(buildDir, filePath)); + + // Skip symbol files as they need have same hash as they "parent" + if (path.extension(filePath) == '.symbols') { + continue; + } + + if (!file.existsSync()) { + throw Exception( + 'Required file not found: $filePath\n' + 'This file is expected to exist after flutter build web. ' + 'If this file has been removed or renamed in Flutter, ' + 'update the autoVersionedFiles list in web_asset_versioner.dart', + ); + } + + await _versionFile(file, filePath); + + if (filePath.endsWith('.js')) { + await _versionPartFiles(filePath); + } + + // Find all symbols files and add same hash as their parent + await _versionSymbolFiles(filePath); + } + } + + /// Creates a versioned filename by inserting the hash before the extension. + /// Example: 'flutter_bootstrap.js' -> 'flutter_bootstrap.abc123.js' + String _createVersionedFilename(String filename, String hash) { + final dir = path.dirname(filename); + final basename = path.basenameWithoutExtension(filename); + final ext = path.extension(filename); + final versioned = '$basename.$hash$ext'; + return dir == '.' ? versioned : path.join(dir, versioned); + } + + void _findManuallyVersionFile(String file) { + final fullPath = path.join(buildDir, path.dirname(file)); + final basename = path.basenameWithoutExtension(file); + final ext = path.extension(file); + + final directory = Directory(fullPath); + if (!directory.existsSync()) { + return; + } + + final versionedPattern = RegExp( + '^${RegExp.escape(basename)}(?:\\.v(\\d+))?${RegExp.escape(ext)}\$', + ); + + final versionedFiles = directory + .listSync() + .whereType() + .where((file) => versionedPattern.hasMatch(path.basename(file.path))) + .toList(); + + if (versionedFiles.isEmpty) { + return; + } + + //Find out what version is assigned to this file + final versionedFile = versionedFiles.first; + final versionedFileName = path.basename(versionedFile.path); + + final hashMatch = versionedPattern.firstMatch(versionedFileName); + final hash = hashMatch?.group(1) ?? ''; + + final relativePath = path.relative(versionedFile.path, from: buildDir); + _versionMap[file] = relativePath; + _hashMap[file] = hash; + } + + void _findManuallyVersionFiles() { + for (final file in manuallyVersionedFiles) { + if (file.contains('*')) { + _findManuallyVersionFilesWithWildcard(file); + } else { + _findManuallyVersionFile(file); + } + } + } + + void _findManuallyVersionFilesWithWildcard(String pattern) { + final parts = pattern.split('/'); + final wildcardIndex = parts.indexWhere((part) => part.contains('*')); + + if (wildcardIndex == -1) { + return; + } + + final basePath = path.joinAll(parts.sublist(0, wildcardIndex)); + + final fullBasePath = path.join(buildDir, basePath); + + final baseDirectory = Directory(fullBasePath); + if (!baseDirectory.existsSync()) { + return; + } + + final wildcardPart = parts[wildcardIndex]; + final wildcardRegex = RegExp('^${wildcardPart.replaceAll('*', '.*')}\$'); + + // Find all directories matching the wildcard + final matchingDirs = baseDirectory + .listSync() + .whereType() + .where((dir) => wildcardRegex.hasMatch(path.basename(dir.path))) + .toList(); + + for (final dir in matchingDirs) { + final actualDirName = path.basename(dir.path); + final remainingParts = parts.sublist(wildcardIndex + 1); + + final filePath = path.joinAll([dir.path, ...remainingParts]); + final file = File(filePath); + + if (!file.existsSync()) { + continue; + } + + // Extract version from directory name (e.g., v1, v2, v3) + final versionMatch = RegExp(r'v(\d+)').firstMatch(actualDirName); + final hash = versionMatch?.group(1) ?? ''; + + final relativePath = path.relative(file.path, from: buildDir); + + // Build the actual pattern (original pattern with * replaced) + final actualPattern = parts + .asMap() + .entries + .map( + (entry) => entry.key == wildcardIndex ? actualDirName : entry.value, + ) + .join('/'); + + _versionMap[actualPattern] = relativePath; + _hashMap[actualPattern] = hash; + + _log('✓ Found $pattern -> $relativePath (matched: $actualDirName)'); + + // Only map the first match + break; + } + } + + Future _generateAssetVersionFile() async { + final assetVersionsFile = File(path.join(buildDir, 'asset_version.json')); + + final assetVersion = AssetVersionManifest( + generated: DateTime.now().toUtc().toIso8601String(), + versionedAssets: _versionMap, + assetHashes: _hashMap, + manuallyVersionedFiles: manuallyVersionedFiles, + ); + + const encoder = JsonEncoder.withIndent(' '); + final jsonString = encoder.convert(assetVersion.toJson()); + + await assetVersionsFile.writeAsString('$jsonString\n', flush: true); + _log('Generated asset version file'); + } + + Future _generateCaddyfileSnippet() async { + final snippetFile = File(path.join(buildDir, 'versioned_assets.caddy')); + + final buffer = StringBuffer(); + buffer.writeln('# Auto-generated by version_web_assets.dart'); + buffer.writeln('# DO NOT EDIT: This file is regenerated on each build'); + buffer.writeln('# Generated: ${DateTime.now().toUtc().toIso8601String()}'); + buffer.writeln(); + buffer.writeln( + '# Versioned assets (with content hashes) can be cached indefinitely', + ); + buffer.writeln('@versioned_assets {'); + + // Add path matchers for auto-versioned files + for (final versionedPath in _versionMap.values) { + buffer.writeln(' path /$versionedPath'); + } + buffer.writeln('}'); + buffer.writeln( + 'header @versioned_assets Cache-Control "public, max-age=31536000, immutable"', + ); + + await snippetFile.writeAsString(buffer.toString(), flush: true); + _log('✓ Generated Caddyfile snippet: versioned_assets.caddy'); + } + + void _log(String message) { + if (verbose) { + stdout.writeln(message); + } + } + + Future _updateAssetReferences() async { + final allFiles = await Directory(buildDir) + .list(recursive: true) + .where( + (file) => + file is File && + (path.extension(file.path).contains('.html') || + path.extension(file.path) == '.js'), + ) + .cast() + .toList(); + + final assetsBasename = _versionMap.keys + .map((key) => path.basename(key)) + .toSet(); + + // Filter files to only those that contain references to versioned assets + final targetFiles = []; + for (final file in allFiles) { + final content = await file.readAsString(); + + final containsAssetReference = assetsBasename.any( + (basename) => content.contains(basename), + ); + + if (containsAssetReference) { + targetFiles.add(file); + } + } + + final updateRefRegistry = RefUpdaterRegistry(); + + for (final file in targetFiles) { + updateRefRegistry.updateFileReferences( + file: file, + versionMap: _versionMap, + ); + } + } + + Future _versionFile(File file, String filePath) async { + final bytes = await file.readAsBytes(); + final hash = md5.convert(bytes).toString(); + final shortHash = hash.substring(0, 8); + + final versionedFilename = _createVersionedFilename(filePath, shortHash); + final versionedFile = File(path.join(buildDir, versionedFilename)); + + await file.rename(versionedFile.path); + + // Store mapping: original -> versioned + _versionMap[filePath] = versionedFilename; + _hashMap[filePath] = shortHash; + + _log('✓ $filePath -> $versionedFilename'); + } + + Future _versionPartFiles(String originalFilePath) async { + final fileDir = path.dirname(path.join(buildDir, originalFilePath)); + final baseFileName = path.basename(originalFilePath); + + final partPattern = RegExp( + '^${RegExp.escape(baseFileName)}_\\d+\\.part\\.js\$', + ); + + final directory = Directory(fileDir); + if (!directory.existsSync()) return; + + final partFiles = directory + .listSync() + .whereType() + .where((file) => partPattern.hasMatch(path.basename(file.path))) + .toList(); + + for (final partFile in partFiles) { + final partFileName = path.basename(partFile.path); + + final originalDir = path.dirname(originalFilePath); + final relativePath = originalDir.isNotEmpty && originalDir != '.' + ? path.join(originalDir, partFileName) + : partFileName; + + await _versionFile(partFile, relativePath); + } + } + + Future _versionSymbolFiles(String parentFilePath) async { + final symbolFilePath = '$parentFilePath.symbols'; + final symbolFile = File(path.join(buildDir, symbolFilePath)); + + if (!symbolFile.existsSync()) { + return; + } + + final parentHash = _hashMap[parentFilePath]; + if (parentHash == null) { + _log('Warning: No hash found for parent file: $parentFilePath'); + return; + } + + final versionedFilename = _createVersionedFilename( + symbolFilePath, + parentHash, + ); + final versionedFile = File(path.join(buildDir, versionedFilename)); + + await symbolFile.rename(versionedFile.path); + + _versionMap[symbolFilePath] = versionedFilename; + _hashMap[symbolFilePath] = parentHash; + + _log('✓ $symbolFilePath -> $versionedFilename (using parent hash)'); + } +} diff --git a/catalyst_voices/utilities/poc_local_storage/pubspec.yaml b/catalyst_voices/utilities/poc_local_storage/pubspec.yaml index 857569873836..a373e1b1dbca 100644 --- a/catalyst_voices/utilities/poc_local_storage/pubspec.yaml +++ b/catalyst_voices/utilities/poc_local_storage/pubspec.yaml @@ -22,8 +22,5 @@ dependencies: dev_dependencies: catalyst_analysis: ^3.0.0 -dependency_overrides: - flutter_secure_storage_web: ^2.0.0-beta.1 - flutter: uses-material-design: true diff --git a/cspell.json b/cspell.json index 5a041ded2db8..cae21e000912 100644 --- a/cspell.json +++ b/cspell.json @@ -172,6 +172,7 @@ "**/historic_data/**/*.yaml", "**/historic_data/**/*.json", "**/test_data/**/*.sql", + "**/test_data/rbac_regs/*.jsonc", "styles.min.css", "web-components.min.js", "**/generated/**", @@ -185,7 +186,7 @@ "**/*.svg", "catalyst_voices/docs/licenses/**", "catalyst_voices/docs/dependency_graph.dot", - "catalyst-gateway/tests/api_tests/test_data/**", + "catalyst_voices/scripts/version_web_assets/test/helper" ], "enableFiletypes": [ "earthfile",