Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.presentum.dev/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Storage tracks presentation events across sessions. This guide shows you how to implement the PresentumStorage interface.

Implementation

Use this generic go-to SharedPreferences storage implementation for any presentum:
import 'dart:async';

import 'package:presentum/presentum.dart';
import 'package:shared_preferences/shared_preferences.dart';

typedef PersistentPresentumStorageKey<
  S extends PresentumSurface,
  V extends PresentumVisualVariant
> = (String itemId, S surface, V variant);

extension type PersistentPresentumStorageKeys<
  S extends PresentumSurface,
  V extends PresentumVisualVariant
>(PersistentPresentumStorageKey<S, V> key) {
  String get shownCount =>
      '__shown_${key.$1}_${key.$2.name}_${key.$3.name}_count_key__';
  String get lastShown =>
      '__shown_${key.$1}_${key.$2.name}_${key.$3.name}_last_shown_key__';
  String get timestamps =>
      '__shown_${key.$1}_${key.$2.name}_${key.$3.name}_timestamps_key__';
  String get dismissedAt =>
      '__dismissed_${key.$1}_${key.$2.name}_${key.$3.name}_at_key__';
  String get convertedAt =>
      '__converted_${key.$1}_${key.$2.name}_${key.$3.name}_at_key__';

  List<String> get allKeys => [
    shownCount,
    lastShown,
    timestamps,
    dismissedAt,
    convertedAt,
  ];
}

class PersistentPresentumStorage<
  S extends PresentumSurface,
  V extends PresentumVisualVariant
>
    implements PresentumStorage<S, V> {
  PersistentPresentumStorage({required SharedPreferencesWithCache prefs})
    : _prefs = prefs;

  final SharedPreferencesWithCache _prefs;

  @override
  Future<void> clearItem(
    String itemId, {
    required S surface,
    required V variant,
  }) => Future.wait(
    PersistentPresentumStorageKeys((
      itemId,
      surface,
      variant,
    )).allKeys.map(_prefs.remove),
  );

  @override
  FutureOr<DateTime?> getLastShown(
    String itemId, {
    required S surface,
    required V variant,
  }) async {
    final key = PersistentPresentumStorageKeys((
      itemId,
      surface,
      variant,
    )).lastShown;
    final timestampStr = _prefs.getString(key);
    return timestampStr != null ? DateTime.parse(timestampStr) : null;
  }

  @override
  FutureOr<void> recordShown(
    String itemId, {
    required S surface,
    required V variant,
    required DateTime at,
  }) async {
    final keys = PersistentPresentumStorageKeys((itemId, surface, variant));
    final countKey = keys.shownCount;
    final lastShownKey = keys.lastShown;
    final timestampsKey = keys.timestamps;

    final currentCount = _prefs.getInt(countKey) ?? 0;
    final currentTimestamps = _prefs.getStringList(timestampsKey) ?? [];

    await _prefs.setInt(countKey, currentCount + 1);
    await _prefs.setString(lastShownKey, at.toIso8601String());
    await _prefs.setStringList(timestampsKey, [
      ...currentTimestamps,
      at.toIso8601String(),
    ]);
  }

  @override
  FutureOr<int> getShownCount(
    String itemId, {
    required Duration period,
    required S surface,
    required V variant,
  }) async {
    final keys = PersistentPresentumStorageKeys((itemId, surface, variant));
    final timestampsKey = keys.timestamps;
    final timestampStrings = _prefs.getStringList(timestampsKey) ?? [];
    final timestamps = timestampStrings.map(DateTime.parse).toList();
    final cutoff = DateTime.now().subtract(period);
    final count = timestamps.where((t) => t.isAfter(cutoff)).length;

    return count;
  }

  @override
  FutureOr<DateTime?> getDismissedAt(
    String itemId, {
    required S surface,
    required V variant,
  }) async {
    final keys = PersistentPresentumStorageKeys((itemId, surface, variant));
    final timestampStr = _prefs.getString(keys.dismissedAt);
    return timestampStr != null ? DateTime.parse(timestampStr) : null;
  }

  @override
  FutureOr<void> recordDismissed(
    String itemId, {
    required S surface,
    required V variant,
    required DateTime at,
  }) async {
    final keys = PersistentPresentumStorageKeys((itemId, surface, variant));
    await _prefs.setString(keys.dismissedAt, at.toIso8601String());
  }

  @override
  FutureOr<void> recordConverted(
    String itemId, {
    required S surface,
    required V variant,
    required DateTime at,
  }) async {
    final keys = PersistentPresentumStorageKeys((itemId, surface, variant));
    await _prefs.setString(keys.convertedAt, at.toIso8601String());
  }
}

See generic production storage

Complete SharedPreferences implementation