openStore method

Future<void> openStore()

Resolves the store URL and attempts to open it, falling back to a constructed store-search URL when the remote value is unusable or fails to launch.

Implementation

Future<void> openStore() async {
  if (!isClosed) emit(const ForceUpdateState(status: ForceUpdateStatus.opening));

  try {
    final String storeUrl = Platform.isAndroid
        ? _remoteConfig.getString(RemoteConfigKeys.storeUrlAndroid)
        : _remoteConfig.getString(RemoteConfigKeys.storeUrlIos);

    // Uri.tryParse accepts relative URIs, so also require isAbsolute to
    // ensure the URL is actually launchable (has scheme + host).
    final Uri? parsedUri = storeUrl.isNotEmpty ? Uri.tryParse(storeUrl) : null;
    final bool hasValidRemoteUri = parsedUri != null && parsedUri.isAbsolute;

    // Always build the package-specific fallback upfront — used when the
    // remote URL is missing, relative, or fails to launch.
    final PackageInfo packageInfo = await PackageInfo.fromPlatform();
    final Uri fallbackUri = Platform.isAndroid
        ? Uri.https('play.google.com', '/store/apps/details', <String, String>{'id': packageInfo.packageName})
        : Uri.https('apps.apple.com', '/search', <String, String>{'term': packageInfo.appName});

    if (!hasValidRemoteUri) {
      _logger.warn(
        '[ForceUpdateCubit] Store URL from Remote Config is missing, malformed, or not absolute — using fallback',
        extras: <String, Object?>{'original': storeUrl, 'fallback': fallbackUri.toString()},
        escalate: true,
      );
      if (!await launchUrl(fallbackUri, mode: LaunchMode.externalApplication)) {
        _logger.warn('[ForceUpdateCubit] Could not open fallback store URL', extras: <String, Object?>{'url': fallbackUri.toString()});
      }
      return;
    }

    if (!await launchUrl(parsedUri, mode: LaunchMode.externalApplication)) {
      _logger.warn(
        '[ForceUpdateCubit] Could not open remote store URL — retrying with fallback',
        extras: <String, Object?>{'url': parsedUri.toString()},
      );
      if (!await launchUrl(fallbackUri, mode: LaunchMode.externalApplication)) {
        _logger.warn('[ForceUpdateCubit] Could not open fallback store URL', extras: <String, Object?>{'url': fallbackUri.toString()});
      }
    }
  } catch (e, st) {
    // Non-fatal: PackageInfo.fromPlatform() or launchUrl() can throw
    // (e.g. a platform-channel failure) rather than returning false. Log
    // and surface a message on state rather than letting this propagate
    // uncaught out of the button's onPressed handler.
    _logger.error('[ForceUpdateCubit] Failed to open the store', error: e, stackTrace: st);
    if (!isClosed) emit(state.copyWith(errorMessage: e.toString()));
  } finally {
    if (!isClosed) emit(state.copyWith(status: ForceUpdateStatus.idle));
  }
}