import 'dart:collection'; import 'dart:ui'; import 'dart:core'; import 'package:flutter/material.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'opaque.dart'; const TapirGroupsExperiment = "tapir-groups-experiment"; /// Settings govern the *Globally* relevant settings like Locale, Theme and Experiments. /// We also provide access to the version information here as it is also accessed from the /// Settings Pane. class Settings extends ChangeNotifier { Locale locale; late PackageInfo packageInfo; OpaqueThemeType theme; // explicitly set experiments to false until told otherwise... bool experimentsEnabled = false; HashMap experiments = HashMap.identity(); late bool blockUnknownConnections; /// Set the dark theme. void setDark() { theme = Opaque.dark; notifyListeners(); } /// Set the Light theme. void setLight() { theme = Opaque.light; notifyListeners(); } /// Get access to the current theme. OpaqueThemeType current() { return theme; } /// isExperimentEnabled can be used to safely check whether a particular /// experiment is enabled bool isExperimentEnabled(String experiment) { if (this.experimentsEnabled) { if (this.experiments.containsKey(experiment)) { // We now know it cannot be null... return this.experiments[experiment]! == true; } } return false; } /// Called by the event bus. When new settings are loaded from a file the JSON will /// be sent to the function and new settings will be instantiated based on the contents. handleUpdate(dynamic settings) { // Set Theme and notify listeners if (settings["Theme"] == "light") { this.setLight(); } else { this.setDark(); } // Set Locale and notify listeners switchLocale(Locale(settings["Locale"])); // Decide whether to enable Experiments blockUnknownConnections = settings["BlockUnknownConnections"]; // Decide whether to enable Experiments experimentsEnabled = settings["ExperimentsEnabled"]; // Set the internal experiments map. Casting from the Map that we get from JSON experiments = new HashMap.from(settings["Experiments"]); // Push the experimental settings to Consumers of Settings notifyListeners(); } /// Initialize the Package Version information initPackageInfo() { PackageInfo.fromPlatform().then((PackageInfo newPackageInfo) { packageInfo = newPackageInfo; notifyListeners(); }); } /// Switch the Locale of the App switchLocale(Locale newLocale) { locale = newLocale; notifyListeners(); } /// Block Unknown Connections will autoblock connections if they authenticate with public key not in our contacts list. /// This is one of the best tools we have to combat abuse, while it isn't ideal it does allow a user to curate their contacts /// list without being bothered by spurious requests (either permanently, or as a short term measure). /// Note: This is not an *appear offline* setting which would explicitly close the listen port, rather than simply auto disconnecting unknown attempts. forbidUnknownConnections() { blockUnknownConnections = true; notifyListeners(); } /// Allow Unknown Connections will allow new contact requires from unknown public keys /// See above for more information. allowUnknownConnections() { blockUnknownConnections = false; notifyListeners(); } /// Turn Experiments On, this will also have the side effect of enabling any /// Experiments that have been previously activated. enableExperiments() { experimentsEnabled = true; notifyListeners(); } /// Turn Experiments Off. This will disable **all** active experiments. /// Note: This will not set the preference for individual experiments, if experiments are enabled /// any experiments that were active previously will become active again unless they are explicitly disabled. disableExperiments() { experimentsEnabled = false; notifyListeners(); } /// Turn on a specific experiment. enableExperiment(String key) { experiments.update(key, (value) => true, ifAbsent: () => true); notifyListeners(); } /// Turn off a specific experiment disableExperiment(String key) { experiments.update(key, (value) => false, ifAbsent: () => false); notifyListeners(); } /// Construct a default settings object. Settings(this.locale, this.theme); /// Convert this Settings object to a JSON representation for serialization on the /// event bus. dynamic asJson() { var themeString = "light"; if (theme == Opaque.dark) { themeString = "dark"; } return { "Locale": this.locale.languageCode, "Theme": themeString, "PreviousPid": -1, "BlockUnknownConnections": blockUnknownConnections, "ExperimentsEnabled": this.experimentsEnabled, "Experiments": experiments, "StateRootPane": 0, "FirstTime": false }; } }