Browse Source

docs(neon): document neon models

Signed-off-by: Nikolas Rimikis <leptopoda@users.noreply.github.com>
pull/1029/head
Nikolas Rimikis 1 year ago
parent
commit
d070feb2a5
No known key found for this signature in database
GPG Key ID: 85ED1DE9786A4FF2
  1. 21
      packages/neon/neon/lib/src/models/account.dart
  2. 1
      packages/neon/neon/lib/src/models/account_cache.dart
  3. 53
      packages/neon/neon/lib/src/models/app_implementation.dart
  4. 2
      packages/neon/neon/lib/src/models/disposable.dart
  5. 4
      packages/neon/neon/lib/src/models/label_builder.dart
  6. 11
      packages/neon/neon/lib/src/models/notifications_interface.dart

21
packages/neon/neon/lib/src/models/account.dart

@ -22,9 +22,11 @@ abstract interface class Credentials {
abstract final String? password; abstract final String? password;
} }
/// Account data.
@JsonSerializable() @JsonSerializable()
@immutable @immutable
class Account implements Credentials { class Account implements Credentials {
/// Creates a new account.
Account({ Account({
required this.serverURL, required this.serverURL,
required this.username, required this.username,
@ -39,8 +41,10 @@ class Account implements Credentials {
cookieJar: CookieJar(), cookieJar: CookieJar(),
); );
/// Creates a new account object from the given [json] data.
factory Account.fromJson(final Map<String, dynamic> json) => _$AccountFromJson(json); factory Account.fromJson(final Map<String, dynamic> json) => _$AccountFromJson(json);
/// Parses this object into a json like map.
Map<String, dynamic> toJson() => _$AccountToJson(this); Map<String, dynamic> toJson() => _$AccountToJson(this);
@override @override
@ -49,6 +53,8 @@ class Account implements Credentials {
final String username; final String username;
@override @override
final String? password; final String? password;
/// The user agent to use.
final String? userAgent; final String? userAgent;
@override @override
@ -62,8 +68,13 @@ class Account implements Credentials {
@override @override
int get hashCode => serverURL.hashCode + username.hashCode; int get hashCode => serverURL.hashCode + username.hashCode;
/// An authenticated API client.
final NextcloudClient client; final NextcloudClient client;
/// The unique ID of the account.
///
/// Implemented in a primitive way hashing the [username] and [serverURL].
/// IDs are globally cached in [_idCache].
String get id { String get id {
final key = '$username@$serverURL'; final key = '$username@$serverURL';
@ -108,10 +119,20 @@ class Account implements Credentials {
Uri stripUri(final Uri uri) => Uri.parse(uri.toString().replaceFirst(serverURL.toString(), '')); Uri stripUri(final Uri uri) => Uri.parse(uri.toString().replaceFirst(serverURL.toString(), ''));
} }
/// Global [Account.id] cache.
Map<String, String> _idCache = {}; Map<String, String> _idCache = {};
/// Extension to find an account by id in a Iterable.
extension AccountFind on Iterable<Account> { extension AccountFind on Iterable<Account> {
/// Returns the first [Account] matching [accountID] by [Account.id].
///
/// If no `Account` was found `null` is returned.
Account? tryFind(final String? accountID) => firstWhereOrNull((final account) => account.id == accountID); Account? tryFind(final String? accountID) => firstWhereOrNull((final account) => account.id == accountID);
/// Returns the first [Account] matching [accountID] by [Account.id].
///
/// Throws a [StateError] if no `Account` was found.
/// Use [tryFind] to get a nullable result.
Account find(final String accountID) => firstWhere((final account) => account.id == accountID); Account find(final String accountID) => firstWhere((final account) => account.id == accountID);
} }

1
packages/neon/neon/lib/src/models/account_cache.dart

@ -3,6 +3,7 @@ import 'package:neon/src/models/disposable.dart';
/// Cache for [Account] specific [Disposable] objects. /// Cache for [Account] specific [Disposable] objects.
class AccountCache<T extends Disposable> implements Disposable { class AccountCache<T extends Disposable> implements Disposable {
/// Creates a new account cache.
AccountCache(); AccountCache();
final Map<String, T> _cache = {}; final Map<String, T> _cache = {};

53
packages/neon/neon/lib/src/models/app_implementation.dart

@ -20,18 +20,39 @@ import 'package:provider/provider.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
import 'package:vector_graphics/vector_graphics.dart'; import 'package:vector_graphics/vector_graphics.dart';
/// Base implementation of a Neon app.
///
/// It is mandatory to provide a precompiled SVG under `assets/app.svg.vec`.
/// SVGs can be precompiled with `https://pub.dev/packages/vector_graphics_compiler`
@immutable @immutable
abstract class AppImplementation<T extends Bloc, R extends NextcloudAppOptions> implements Disposable { abstract class AppImplementation<T extends Bloc, R extends NextcloudAppOptions> implements Disposable {
/// The unique id of an app.
///
/// It is common to specify them in `AppIDs`.
String get id; String get id;
/// {@macro flutter.widgets.widgetsApp.localizationsDelegates}
LocalizationsDelegate<Object> get localizationsDelegate; LocalizationsDelegate<Object> get localizationsDelegate;
/// {@macro flutter.widgets.widgetsApp.supportedLocales}
Iterable<Locale> get supportedLocales; Iterable<Locale> get supportedLocales;
/// Default localized app name used in [name].
///
/// Defaults to the frameworks mapping of the [id] to a localized name.
String nameFromLocalization(final NeonLocalizations localizations) => localizations.appImplementationName(id); String nameFromLocalization(final NeonLocalizations localizations) => localizations.appImplementationName(id);
/// Localized name of this app.
String name(final BuildContext context) => nameFromLocalization(NeonLocalizations.of(context)); String name(final BuildContext context) => nameFromLocalization(NeonLocalizations.of(context));
/// The [SettingsStorage] for this app.
@protected @protected
late final AppStorage storage = AppStorage(StorageKeys.apps, id); late final AppStorage storage = AppStorage(StorageKeys.apps, id);
/// The options associated with this app.
///
/// Options will be added to the settings page providing a global place to
/// adjust the behavior of an app.
@mustBeOverridden @mustBeOverridden
R get options; R get options;
@ -46,13 +67,25 @@ abstract class AppImplementation<T extends Bloc, R extends NextcloudAppOptions>
) => ) =>
null; null;
/// Cache for all blocs.
///
/// To access a bloc use [getBloc] instead.
final blocsCache = AccountCache<T>(); final blocsCache = AccountCache<T>();
/// Returns a bloc [T] from the [blocsCache] or builds a new one if absent.
T getBloc(final Account account) => blocsCache[account] ??= buildBloc(account); T getBloc(final Account account) => blocsCache[account] ??= buildBloc(account);
/// Build the bloc [T] for the given [account].
///
/// Blocs are long lived and should not be rebuilt for subsequent calls.
/// Use [getBloc] which also handles caching.
@protected @protected
T buildBloc(final Account account); T buildBloc(final Account account);
/// The [Provider] building the bloc [T] the currently active account.
///
/// Blocs will not be disposed on disposal of the provider. You must handle
/// the [blocsCache] manually.
Provider<T> get blocProvider => Provider<T>( Provider<T> get blocProvider => Provider<T>(
create: (final context) { create: (final context) {
final accountsBloc = NeonProvider.of<AccountsBloc>(context); final accountsBloc = NeonProvider.of<AccountsBloc>(context);
@ -62,10 +95,17 @@ abstract class AppImplementation<T extends Bloc, R extends NextcloudAppOptions>
}, },
); );
/// The count of unread notifications.
///
/// If `null` no label will be displayed.
BehaviorSubject<int>? getUnreadCounter(final T bloc) => null; BehaviorSubject<int>? getUnreadCounter(final T bloc) => null;
/// The main page of this app.
///
/// The framework will insert [blocProvider] into the widget tree before.
Widget get page; Widget get page;
/// The drawer destination used in widgets like [NavigationDrawer].
NeonNavigationDestination destination(final BuildContext context) { NeonNavigationDestination destination(final BuildContext context) {
final accountsBloc = NeonProvider.of<AccountsBloc>(context); final accountsBloc = NeonProvider.of<AccountsBloc>(context);
final account = accountsBloc.activeAccount.value!; final account = accountsBloc.activeAccount.value!;
@ -106,6 +146,10 @@ abstract class AppImplementation<T extends Bloc, R extends NextcloudAppOptions>
throw FlutterError('No name for the initial route provided.'); throw FlutterError('No name for the initial route provided.');
} }
/// Builds the app icon.
///
/// It is mandatory to provide a precompiled SVG under `assets/app.svg.vec`.
/// SVGs can be precompiled with `https://pub.dev/packages/vector_graphics_compiler`
Widget buildIcon({ Widget buildIcon({
final double? size, final double? size,
final Color? color, final Color? color,
@ -145,7 +189,16 @@ abstract class AppImplementation<T extends Bloc, R extends NextcloudAppOptions>
int get hashCode => id.hashCode; int get hashCode => id.hashCode;
} }
/// Extension to find an app implementation by id in a Iterable.
extension AppImplementationFind on Iterable<AppImplementation> { extension AppImplementationFind on Iterable<AppImplementation> {
/// Returns the first [AppImplementation] matching [appID] by [AppImplementation.id].
///
/// If no `AppImplementation` was found `null` is returned.
AppImplementation? tryFind(final String? appID) => firstWhereOrNull((final app) => app.id == appID); AppImplementation? tryFind(final String? appID) => firstWhereOrNull((final app) => app.id == appID);
/// Returns the first [AppImplementation] matching [appID] by [AppImplementation.id].
///
/// Throws a [StateError] if no `AppImplementation` was found.
/// Use [tryFind] to get a nullable result.
AppImplementation find(final String appID) => firstWhere((final app) => app.id == appID); AppImplementation find(final String appID) => firstWhere((final app) => app.id == appID);
} }

2
packages/neon/neon/lib/src/models/disposable.dart

@ -11,6 +11,7 @@ abstract interface class Disposable {
void dispose(); void dispose();
} }
/// Extension on [Disposable] iterables.
extension DisposableIterableBloc on Iterable<Disposable> { extension DisposableIterableBloc on Iterable<Disposable> {
/// Calls [Disposable.dispose] on all entries. /// Calls [Disposable.dispose] on all entries.
/// ///
@ -22,6 +23,7 @@ extension DisposableIterableBloc on Iterable<Disposable> {
} }
} }
/// Extension on [Disposable] maps.
extension DisposableMapBloc on Map<dynamic, Disposable> { extension DisposableMapBloc on Map<dynamic, Disposable> {
/// Calls [Disposable.dispose] on all entries. /// Calls [Disposable.dispose] on all entries.
/// ///

4
packages/neon/neon/lib/src/models/label_builder.dart

@ -1,3 +1,7 @@
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
/// The signature of a function generating a label.
///
/// The `context` includes the [WidgetsApp]'s [Localizations] widget so that
/// this method can be used to produce a localized label.
typedef LabelBuilder = String Function(BuildContext); typedef LabelBuilder = String Function(BuildContext);

11
packages/neon/neon/lib/src/models/notifications_interface.dart

@ -3,8 +3,12 @@ import 'package:neon/src/bloc/bloc.dart';
import 'package:neon/src/models/app_implementation.dart'; import 'package:neon/src/models/app_implementation.dart';
import 'package:neon/src/settings/models/options_collection.dart'; import 'package:neon/src/settings/models/options_collection.dart';
/// The interface of the notifications client implementation.
///
/// Use this to access the notifications client from other Neon clients.
abstract interface class NotificationsAppInterface<T extends NotificationsBlocInterface, abstract interface class NotificationsAppInterface<T extends NotificationsBlocInterface,
R extends NotificationsOptionsInterface> extends AppImplementation<T, R> { R extends NotificationsOptionsInterface> extends AppImplementation<T, R> {
/// Creates a new notifications client.
NotificationsAppInterface(); NotificationsAppInterface();
@override @override
@ -12,13 +16,20 @@ abstract interface class NotificationsAppInterface<T extends NotificationsBlocIn
R get options => throw UnimplementedError(); R get options => throw UnimplementedError();
} }
/// The interface of the bloc used by the notifications client.
abstract interface class NotificationsBlocInterface extends InteractiveBloc { abstract interface class NotificationsBlocInterface extends InteractiveBloc {
/// Creates a new notifications bloc.
NotificationsBlocInterface(this.options); NotificationsBlocInterface(this.options);
/// The options for the notifications client.
final NotificationsOptionsInterface options; final NotificationsOptionsInterface options;
/// Deletes the notification with the given [id].
void deleteNotification(final int id); void deleteNotification(final int id);
} }
/// The interface of the app options used by the notifications client.
abstract interface class NotificationsOptionsInterface extends NextcloudAppOptions { abstract interface class NotificationsOptionsInterface extends NextcloudAppOptions {
/// Creates the nextcloud app options for the notifications client.
NotificationsOptionsInterface(super.storage); NotificationsOptionsInterface(super.storage);
} }

Loading…
Cancel
Save