Skip to content
Snippets Groups Projects
shared_prefs_helper.dart 6.95 KiB
Newer Older
vjrj's avatar
vjrj committed
import 'dart:convert';
vjrj's avatar
vjrj committed

import 'package:durt/durt.dart';
import 'package:flutter/foundation.dart';
vjrj's avatar
vjrj committed
import 'package:shared_preferences/shared_preferences.dart';
poka's avatar
poka committed
import 'package:tuple/tuple.dart';
vjrj's avatar
vjrj committed
import 'data/models/cesium_card.dart';
vjrj's avatar
vjrj committed
import 'data/models/credit_card_themes.dart';
vjrj's avatar
vjrj committed
import 'g1/g1_helper.dart';
vjrj's avatar
vjrj committed
import 'ui/logger.dart';
class SharedPreferencesHelper with ChangeNotifier {
vjrj's avatar
vjrj committed
  factory SharedPreferencesHelper() {
    return _instance;
  }

  SharedPreferencesHelper._internal() {
    SharedPreferences.getInstance().then((SharedPreferences value) {
      _prefs = value;
    });
  }

vjrj's avatar
vjrj committed
  List<CesiumCard> cesiumCards = <CesiumCard>[];

  Map<String, CesiumWallet> cesiumVolatileCards = <String, CesiumWallet>{};

vjrj's avatar
vjrj committed
  static final SharedPreferencesHelper _instance =
      SharedPreferencesHelper._internal();

  late SharedPreferences _prefs;

  static const String _seedKey = 'seed';
  static const String _pubKey = 'pub';

  Future<void> init() async {
    _prefs = await SharedPreferences.getInstance();
vjrj's avatar
vjrj committed

    final String? json = _prefs.getString('cesiumCards');
    if (json != null) {
      final List<dynamic> list = jsonDecode(json) as List<dynamic>;
      cesiumCards = list
          .map((dynamic e) => CesiumCard.fromJson(e as Map<String, dynamic>))
          .toList();
    }
vjrj's avatar
vjrj committed

    // Migrate the current pair if exists
    await migrateCurrentPair();
  }

  Future<void> migrateCurrentPair() async {
    if (_prefs.containsKey(_seedKey) &&
        _prefs.containsKey(_pubKey) &&
        cesiumCards.isEmpty) {
      final String seed = _prefs.getString(_seedKey)!;
      final String pubKey = _prefs.getString(_pubKey)!;
      final CesiumCard card = buildCesiumCard(seed: seed, pubKey: pubKey);
      addCesiumCard(card);
      // Let's do this later
      await _prefs.remove(_seedKey);
      await _prefs.remove(_pubKey);
vjrj's avatar
vjrj committed
      setCurrentWalletIndex(0);
    }
  }

  Future<void> importAstroIDWallet(String disco, String password) async {
    try {
      final Tuple2<String, String>? secrets =
          await decryptAstroID(disco, password);
      if (secrets != null) {
poka's avatar
poka committed
        final CesiumWallet wallet = CesiumWallet(secrets.item1, secrets.item2);
        final CesiumCard card =
            buildCesiumCard(seed: '', pubKey: wallet.pubkey);
        addCesiumCard(card);
        addCesiumVolatileCard(wallet);
        await selectCurrentWallet(card);
      } else {
        throw Exception('Failed to decrypt AstroID');
      }
    } catch (e) {
      logger('Error importing AstroID wallet: $e');
      rethrow;
    }
  }

vjrj's avatar
vjrj committed
  CesiumCard buildCesiumCard({required String seed, required String pubKey}) {
    return CesiumCard(
        seed: seed, pubKey: pubKey, theme: CreditCardThemes.theme1, name: '');
vjrj's avatar
vjrj committed
  void addCesiumCard(CesiumCard cesiumCard) {
    cesiumCards.add(cesiumCard);
    saveCesiumCards();
  }

vjrj's avatar
vjrj committed
  void removeCesiumCard() {
vjrj's avatar
vjrj committed
    // Don't allow the last card to be removed
vjrj's avatar
vjrj committed
    final int index = getCurrentWalletIndex();
    logger('Removing card at index $index');
vjrj's avatar
vjrj committed
    if (cesiumCards.length > 1) {
      cesiumCards.removeAt(index);
      saveCesiumCards();
    }
vjrj's avatar
vjrj committed
  }

  Future<void> saveCesiumCards([bool notify = true]) async {
vjrj's avatar
vjrj committed
    final String json =
        jsonEncode(cesiumCards.map((CesiumCard e) => e.toJson()).toList());
    await _prefs.setString('cesiumCards', json);
    if (notify) {
      notifyListeners();
    }
vjrj's avatar
vjrj committed
  }

vjrj's avatar
vjrj committed
  // Get the wallet from the specified index (default to first wallet)
  Future<CesiumWallet> getWallet() async {
    if (cesiumCards.isNotEmpty) {
      final CesiumCard card = cesiumCards[getCurrentWalletIndex()];
      if (isG1nkgoCard()) {
        return CesiumWallet.fromSeed(seedFromString(card.seed));
      } else {
        // Check if the wallet is in the volatile map
        final String pubKey = extractPublicKey(card.pubKey);
        if (cesiumVolatileCards.containsKey(pubKey)) {
          return cesiumVolatileCards[pubKey]!;
        } else {
          throw Exception('Imported wallet not found');
        }
vjrj's avatar
vjrj committed
    } else {
      // Generate a new wallet if no wallets exist
vjrj's avatar
vjrj committed
      final Uint8List uS = generateUintSeed();
vjrj's avatar
vjrj committed
      final String seed = seedToString(uS);
vjrj's avatar
vjrj committed
      final CesiumWallet wallet = CesiumWallet.fromSeed(uS);
      logger('Generated public key: ${wallet.pubkey}');
vjrj's avatar
vjrj committed
      addCesiumCard(buildCesiumCard(seed: seed, pubKey: wallet.pubkey));
vjrj's avatar
vjrj committed
      return wallet;
    }
  }

vjrj's avatar
vjrj committed
  // Get the public key from the specified index (default to first wallet)
  String getPubKey() {
    final CesiumCard card = cesiumCards[getCurrentWalletIndex()];
vjrj's avatar
vjrj committed
    final String pubKey = card.pubKey;
    final String checksum = pkChecksum(extractPublicKey(pubKey));
vjrj's avatar
vjrj committed
    return '$pubKey:$checksum';
vjrj's avatar
vjrj committed
  }

vjrj's avatar
vjrj committed
  String getName() {
    final CesiumCard card = cesiumCards[getCurrentWalletIndex()];
    return card.name;
  }

  CreditCardTheme getTheme() {
    final CesiumCard card = cesiumCards[getCurrentWalletIndex()];
    return card.theme;
  }

  void setName({required String name, bool notify = true}) {
    final CesiumCard card = cesiumCards[getCurrentWalletIndex()];
    cesiumCards[getCurrentWalletIndex()] = card.copyWith(name: name);
    saveCesiumCards(notify);
  }

  void setTheme({required CreditCardTheme theme}) {
    final CesiumCard card = cesiumCards[getCurrentWalletIndex()];
    cesiumCards[getCurrentWalletIndex()] = card.copyWith(theme: theme);
    saveCesiumCards();
  }

vjrj's avatar
vjrj committed
  List<CesiumCard> get cards => cesiumCards;

  static const String _currentWalletIndexKey = 'current_wallet_index';

  // Get the current wallet index from shared preferences
  int getCurrentWalletIndex() {
    return _prefs.getInt(_currentWalletIndexKey) ?? 0;
  }

  // Set the current wallet index in shared preferences
  Future<void> setCurrentWalletIndex(int index) async {
    await _prefs.setInt(_currentWalletIndexKey, index);
    notifyListeners();
  }

  Future<void> selectCurrentWallet(CesiumCard card) async {
    // TODO(vjrj): this should be a find with pubkey
    final int index = cards.indexOf(card);
vjrj's avatar
vjrj committed
    if (index >= 0) {
      await _prefs.setInt(_currentWalletIndexKey, index);
      notifyListeners();
    } else {
      throw Exception('Invalid wallet index: $index');
    }
vjrj's avatar
vjrj committed
  }

  // Select the current wallet and save its index in shared preferences
  Future<void> selectCurrentWalletIndex(int index) async {
vjrj's avatar
vjrj committed
    if (index < cesiumCards.length) {
      await setCurrentWalletIndex(index);
    } else {
      throw Exception('Invalid wallet index: $index');
vjrj's avatar
vjrj committed
    }
  bool has(String pubKey) {
vjrj's avatar
vjrj committed
    for (final CesiumCard card in cesiumCards) {
      if (card.pubKey == extractPublicKey(pubKey) || card.pubKey == pubKey) {
vjrj's avatar
vjrj committed
        return true;
      }
    }
    return false;
  }
  bool hasVolatile() {
    return cesiumVolatileCards.containsKey(extractPublicKey(getPubKey()));
  }

  void addCesiumVolatileCard(CesiumWallet cesiumWallet) {
    cesiumVolatileCards[cesiumWallet.pubkey] = cesiumWallet;
  }

  bool isG1nkgoCard([CesiumCard? otherCard]) {
    final CesiumCard card = otherCard ?? cesiumCards[getCurrentWalletIndex()];
    return card.seed.isNotEmpty;
  }