push method

Future<RelevaResponse> push(
  1. PushRequest request
)

Implementation

Future<RelevaResponse> push(PushRequest request) async {
  if (!_config.enableTracking) {
    // Return empty response if tracking is disabled
    return RelevaResponse(recommenders: [], banners: []);
  }
  final storage = HiveStorageService.instance;
  await SessionService.instance.initialize(npsManager: _npsManager);
  final sessionId = await SessionService.instance.getSessionId();
  final wishlistProducts = await storage.getWishlistData();

  // Use cart from request if explicitly set (e.g., for checkout success),
  // otherwise load from storage for regular screen views
  final String? cartToSend;
  if (request.cart != null) {
    // Cart explicitly provided in request (checkout success case)
    cartToSend = jsonEncode(request.cart!.toMap());
  } else {
    // Load from storage for regular tracking
    cartToSend = await storage.getCartData();
  }

  final deviceId = await storage.getDeviceId();
  if (deviceId == null) {
    throw Exception(
      'Please provide deviceId using client.setDeviceId() before using the client!',
    );
  }

  // Load merge profile IDs from storage so all instances share the same merge state
  final mergeProfileIds = await storage.getMergeProfileIds() ?? [];
  if (mergeProfileIds.isNotEmpty) {
    debugPrint('Releva: Sending merge profile IDs to API: $mergeProfileIds');
  }

  Map<String, dynamic> context = request.toMap();

  // Build device context fields
  final sessionCount = await storage.getDeviceSessionCount();
  final firstSeenAt = await storage.getDeviceFirstSeenAt();
  final views = await storage.getDeviceViewsCount() + 1;
  await storage.setDeviceViewsCount(views);
  String platform;
  try {
    platform = Platform.isIOS ? 'ios' : 'android';
  } catch (_) {
    platform = 'android';
  }

  context.addAll({
    'deviceId': deviceId,
    'deviceIdChanged': _deviceIdChanged,
    'sessionId': sessionId,
    'profile': {'id': await storage.getProfileId()},
    'profileChanged': _profileChanged,
    'cart': cartToSend == null ? null : jsonDecode(cartToSend),
    'cartChanged': _cartChanged,
    'wishlist': {
      'products': wishlistProducts == null
          ? []
          : wishlistProducts.map((p) => jsonDecode(p)).toList(),
    },
    'wishlistChanged': _wishlistChanged,
    'mergeProfileIds': mergeProfileIds,
    'device': {
      'sessions': sessionCount,
      'platform': platform,
      if (_appVersion != null) 'version': _appVersion,
      if (firstSeenAt != null) 'firstSeenAt': firstSeenAt,
      'views': views,
      'sdkVersion': version,
    },
  });

  try {
    final response = await http.post(
      Uri.parse('${_getEndpoint()}/api/v0/push'),
      headers: {
        'content-type': 'application/json',
        'authorization': 'Bearer $_accessToken',
      },
      body: jsonEncode({
        'context': context,
        'options': {
          'client': {
            'vendor': 'Releva',
            'platform': 'flutter',
            'version': version,
          },
        },
      }),
    );

    if (response.statusCode != 200) {
      throw Exception(
        'Backend API error: ${response.statusCode} - ${response.body}',
      );
    }

    _cartChanged = false;
    _wishlistChanged = false;
    _profileChanged = false;
    _deviceIdChanged = false;
    // Clear merge profile IDs from storage after successful push
    await storage.clearMergeProfileIds();

    final responseMap =
        jsonDecode(utf8.decode(response.bodyBytes)) as Map<String, dynamic>;
    final relevaResponse = RelevaResponse.fromMap(responseMap);

    // Initialize NPS manager with the config from this response.
    // The server has already evaluated app_open/session_count/screen_view
    // triggers — if nps is non-null, a server-side trigger has fired.
    _npsManager.initialize(relevaResponse.nps);

    return relevaResponse;
  } catch (e) {
    rethrow;
  }
}