Browse Source

Merge pull request #1030 from nextcloud/doc/neon_settings

docs(neon): document neon settings
pull/1031/head
Nikolas Rimikis 1 year ago committed by GitHub
parent
commit
16afa09c02
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      packages/neon/neon/lib/src/settings/models/option.dart
  2. 4
      packages/neon/neon/lib/src/settings/models/options_category.dart
  3. 4
      packages/neon/neon/lib/src/settings/models/options_collection.dart
  4. 116
      packages/neon/neon/lib/src/settings/models/storage.dart
  5. 3
      packages/neon/neon/lib/src/settings/utils/settings_export_helper.dart
  6. 6
      packages/neon/neon/lib/src/settings/widgets/account_settings_tile.dart

5
packages/neon/neon/lib/src/settings/models/option.dart

@ -97,7 +97,7 @@ sealed class Option<T> extends ChangeNotifier implements ValueListenable<T>, Dis
notifyListeners();
}
/// Resets the option to its [default] value.
/// Resets the option to its [defaultValue] value.
@mustBeOverridden
void reset() {
value = defaultValue;
@ -216,6 +216,9 @@ class SelectOption<T> extends Option<T> {
/// * [value] for the currently selected one
Map<T, LabelBuilder> get values => _values;
/// Updates the collection of possible values.
///
/// It is up to the caller to also change the [value] if it is no longer supported.
set values(final Map<T, LabelBuilder> newValues) {
if (_values == newValues) {
return;

4
packages/neon/neon/lib/src/settings/models/options_category.dart

@ -1,11 +1,15 @@
import 'package:meta/meta.dart';
import 'package:neon/src/models/label_builder.dart';
import 'package:neon/src/settings/models/option.dart';
/// Category of an [Option].
@immutable
class OptionsCategory {
/// Creates a new Category.
const OptionsCategory({
required this.name,
});
/// Builder function for the category name.
final LabelBuilder name;
}

4
packages/neon/neon/lib/src/settings/models/options_collection.dart

@ -7,6 +7,7 @@ import 'package:neon/src/settings/models/storage.dart';
/// Collection of [Option]s.
abstract class OptionsCollection implements Exportable, Disposable {
/// Creates a new collection of options.
OptionsCollection(this.storage);
/// Storage backend to use.
@ -56,8 +57,9 @@ abstract class OptionsCollection implements Exportable, Disposable {
}
}
/// OptionsCollection for a neon app.
/// OptionsCollection primarily used by `AppImplementation`s.
abstract class NextcloudAppOptions extends OptionsCollection {
/// Creates a new Nextcloud options collection.
NextcloudAppOptions(super.storage);
/// Collection of categories to display the options in the settings.

116
packages/neon/neon/lib/src/settings/models/storage.dart

@ -2,32 +2,87 @@ import 'package:meta/meta.dart';
import 'package:nextcloud/ids.dart';
import 'package:shared_preferences/shared_preferences.dart';
/// Storage interface used by `Option`s.
///
/// Mimics the interface of [SharedPreferences].
///
/// See:
/// * [SingleValueStorage] for a storage that saves a single value.
/// * [AppStorage] for a storage that fully implements the [SharedPreferences] interface.
/// * [NeonStorage] that manages the storage backend.
@internal
abstract interface class SettingsStorage {
/// {@template NeonStorage.getString}
/// Reads a value from persistent storage, throwing an `Exception` if it's not a `String`.
/// {@endtemplate}
String? getString(final String key);
/// {@template NeonStorage.setString}
/// Saves a `String` [value] to persistent storage in the background.
///
/// Note: Due to limitations in Android's SharedPreferences,
/// values cannot start with any one of the following:
///
/// - 'VGhpcyBpcyB0aGUgcHJlZml4IGZvciBhIGxpc3Qu'
/// - 'VGhpcyBpcyB0aGUgcHJlZml4IGZvciBCaWdJbnRlZ2Vy'
/// - 'VGhpcyBpcyB0aGUgcHJlZml4IGZvciBEb3VibGUu'
/// {@endtemplate}
Future<bool> setString(final String key, final String value);
/// {@template NeonStorage.getBool}
/// Reads a value from persistent storage, throwing an `Exception` if it's not a `bool`.
/// {@endtemplate}
bool? getBool(final String key);
/// {@template NeonStorage.setBool}
/// Saves a `bool` [value] to persistent storage in the background.
/// {@endtemplate}
// ignore: avoid_positional_boolean_parameters
// ignore: avoid_positional_boolean_parameters
Future<bool> setBool(final String key, final bool value);
/// {@template NeonStorage.remove}
/// Removes an entry from persistent storage.
/// {@endtemplate}
Future<bool> remove(final String key);
}
/// Interface of a storable element.
///
/// Usually used in enhanced enums to ensure uniqueness of the storage keys.
abstract interface class Storable {
/// The key of this storage element.
String get value;
}
/// Unique storage keys.
///
/// Required by the users of the [NeonStorage] storage backend.
///
/// See:
/// * [AppStorage] for a storage that fully implements the [SharedPreferences] interface.
/// * [SettingsStorage] for the public interface used in `Option`s.
@internal
enum StorageKeys implements Storable {
/// The key for the `AppImplementation`s.
apps._('app'),
/// The key for the `Account`s and their `AccountSpecificOptions`.
accounts._('accounts'),
/// The key for the `GlobalOptions`.
global._('global'),
/// The key for the `AccountsBloc` last used account.
lastUsedAccount._('last-used-account'),
/// The key used by the `PushNotificationsBloc` to persist the last used endpoint.
lastEndpoint._('last-endpoint'),
/// The key for the `FirstLaunchBloc`.
firstLaunch._('first-launch'),
/// The key for the `PushUtils`.
notifications._(AppIDs.notifications);
const StorageKeys._(this.value);
@ -36,6 +91,14 @@ enum StorageKeys implements Storable {
final String value;
}
/// Neon storage that manages the storage backend.
///
/// [init] must be called and completed before accessing individual storages.
///
/// See:
/// * [SingleValueStorage] for a storage that saves a single value.
/// * [AppStorage] for a storage that fully implements the [SharedPreferences] interface.
/// * [SettingsStorage] for the public interface used in `Option`s.
@internal
final class NeonStorage {
const NeonStorage._();
@ -46,6 +109,7 @@ final class NeonStorage {
/// Make sure it has been initialized with [init] before.
static SharedPreferences? _sharedPreferences;
/// Initializes the database instance with a mocked value.
@visibleForTesting
// ignore: use_setters_to_change_properties
static void mock(final SharedPreferences mock) => _sharedPreferences = mock;
@ -61,6 +125,9 @@ final class NeonStorage {
_sharedPreferences = await SharedPreferences.getInstance();
}
/// Returns the database instance.
///
/// Throws a `StateError` if [init] has not completed.
@visibleForTesting
static SharedPreferences get database {
if (_sharedPreferences == null) {
@ -73,45 +140,85 @@ final class NeonStorage {
}
}
/// A storage that saves a single value.
///
/// [NeonStorage.init] must be called and completed before accessing individual values.
///
/// See:
/// * [NeonStorage] to initialize the storage backend.
/// * [AppStorage] for a storage that fully implements the [SharedPreferences] interface.
/// * [SettingsStorage] for the public interface used in `Option`s.
@immutable
@internal
final class SingleValueStorage {
/// Creates a new storage for a single value.
const SingleValueStorage(this.key);
/// The key used by the storage backend.
final StorageKeys key;
/// {@macro NeonStorage.containsKey}
bool hasValue() => NeonStorage.database.containsKey(key.value);
/// {@macro NeonStorage.remove}
Future<bool> remove() => NeonStorage.database.remove(key.value);
/// {@macro NeonStorage.getString}
String? getString() => NeonStorage.database.getString(key.value);
/// {@macro NeonStorage.setString}
Future<bool> setString(final String value) => NeonStorage.database.setString(key.value, value);
/// {@macro NeonStorage.getBool}
bool? getBool() => NeonStorage.database.getBool(key.value);
/// {@macro NeonStorage.setBool}
// ignore: avoid_positional_boolean_parameters
Future<bool> setBool(final bool value) => NeonStorage.database.setBool(key.value, value);
/// {@macro NeonStorage.getStringList}
List<String>? getStringList() => NeonStorage.database.getStringList(key.value);
/// {@macro NeonStorage.setStringList}
Future<bool> setStringList(final List<String> value) => NeonStorage.database.setStringList(key.value, value);
}
/// A storage that can save a group of values.
///
/// Implements the interface of [SharedPreferences].
/// [NeonStorage.init] must be called and completed before accessing individual values.
///
/// See:
/// * [NeonStorage] to initialize the storage backend.
/// * [SingleValueStorage] for a storage that saves a single value.
/// * [SettingsStorage] for the public interface used in `Option`s.
@immutable
@internal
final class AppStorage implements SettingsStorage {
/// Creates a new app storage.
const AppStorage(
this.groupKey, [
this.suffix,
]);
/// The group key for this app storage.
///
/// Keys are formatted with [formatKey]
final StorageKeys groupKey;
/// The optional suffix of the storage key.
///
/// Used to differentiate between multiple AppStorages with the same [groupKey].
final String? suffix;
/// Returns the id for this app storage.
///
/// Uses the [suffix] and falling back to the [groupKey] if not present.
/// This uniquely identifies the storage and is used in `Exportable` classes.
String get id => suffix ?? groupKey.value;
/// Concatenates the [groupKey], [suffix] and [key] to build a unique key
/// used in the storage backend.
@visibleForTesting
String formatKey(final String key) {
if (suffix != null) {
@ -121,6 +228,9 @@ final class AppStorage implements SettingsStorage {
return '${groupKey.value}-$key';
}
/// {@template NeonStorage.containsKey}
/// Returns true if the persistent storage contains the given [key].
/// {@endtemplate}
bool containsKey(final String key) => NeonStorage.database.containsKey(formatKey(key));
@override
@ -138,8 +248,14 @@ final class AppStorage implements SettingsStorage {
@override
Future<bool> setBool(final String key, final bool value) => NeonStorage.database.setBool(formatKey(key), value);
/// {@template NeonStorage.getStringList}
/// Reads a set of string values from persistent storage, throwing an `Exception` if it's not a `String` set.
/// {@endtemplate}
List<String>? getStringList(final String key) => NeonStorage.database.getStringList(formatKey(key));
/// {@template NeonStorage.setStringList}
/// Saves a list of `String` [value]s to persistent storage in the background.
/// {@endtemplate}
Future<bool> setStringList(final String key, final List<String> value) =>
NeonStorage.database.setStringList(formatKey(key), value);
}

3
packages/neon/neon/lib/src/settings/utils/settings_export_helper.dart

@ -22,6 +22,7 @@ import 'package:neon/src/settings/models/storage.dart';
@internal
@immutable
class SettingsExportHelper {
/// Creates a new settings exporter for the given [exportables].
const SettingsExportHelper({
required this.exportables,
});
@ -77,6 +78,7 @@ class SettingsExportHelper {
@internal
@immutable
class AppImplementationsExporter implements Exportable {
/// Creates a new [AppImplementation] exporter.
const AppImplementationsExporter(this.appImplementations);
/// List of apps to export.
@ -113,6 +115,7 @@ class AppImplementationsExporter implements Exportable {
@internal
@immutable
class AccountsBlocExporter implements Exportable {
/// Creates a new [Account] exporter.
const AccountsBlocExporter(this.accountsBloc);
/// AccountsBloc containing the accounts to export.

6
packages/neon/neon/lib/src/settings/widgets/account_settings_tile.dart

@ -4,8 +4,10 @@ import 'package:neon/src/models/account.dart';
import 'package:neon/src/settings/widgets/settings_tile.dart';
import 'package:neon/src/widgets/account_tile.dart';
/// An [NeonAccountTile] used inside a settings list.
@internal
class AccountSettingsTile extends SettingsTile {
/// Creates a new account settings tile.
const AccountSettingsTile({
required this.account,
this.trailing,
@ -13,9 +15,13 @@ class AccountSettingsTile extends SettingsTile {
super.key,
});
/// {@macro neon.AccountTile.account}
final Account account;
/// {@macro neon.AccountTile.trailing}
final Widget? trailing;
/// {@macro neon.AccountTile.onTap}
final GestureTapCallback? onTap;
@override

Loading…
Cancel
Save