Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PreLoadedPlugin mixin for pre-initialized plugins #19

Merged
merged 8 commits into from
Sep 18, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import 'package:flutter/widgets.dart';
import 'package:vyuh_core/vyuh_core.dart';

/// The default implementation for an Analytics Plugin.
final class AnalyticsPlugin extends Plugin implements AnalyticsProvider {
final class AnalyticsPlugin extends Plugin
with PreLoadedPlugin
implements AnalyticsProvider {
/// The list of providers for the plugin.
final List<AnalyticsProvider> providers;

Expand All @@ -19,7 +21,6 @@ final class AnalyticsPlugin extends Plugin implements AnalyticsProvider {
: super(
name: 'vyuh.plugin.analytics',
title: 'Analytics Plugin',
pluginType: PluginType.analytics,
);

@override
Expand Down
2 changes: 1 addition & 1 deletion packages/system/vyuh_core/lib/plugin/auth/auth_plugin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ abstract class AuthPlugin<TUser extends User> extends Plugin {

/// Creates an instance of [AuthPlugin].
AuthPlugin({required super.name, required super.title})
: super(pluginType: PluginType.auth);
: super();

/// The current user that is signed in.
TUser get currentUser => throw UnimplementedError();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ abstract class ContentPlugin extends Plugin {
required this.provider,
required super.name,
required super.title,
}) : super(pluginType: PluginType.content);
}) : super();

/// The type registry that maps types to their descriptors
Map<Type, Map<String, TypeDescriptor>> get typeRegistry;
Expand Down
4 changes: 2 additions & 2 deletions packages/system/vyuh_core/lib/plugin/di/di_plugin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import 'package:vyuh_core/vyuh_core.dart';
/// It is associated with the Dependency Injection (DI) Plugin type and
/// includes methods for registering, unregistering, retrieving, and
/// checking the existence of instances in the DI container.
abstract class DIPlugin extends Plugin {
abstract class DIPlugin extends Plugin with PreLoadedPlugin {
/// The `DIPlugin` constructor accepts two required parameters: `name` and `title`.
DIPlugin({required super.name, required super.title})
: super(pluginType: PluginType.di);
: super();

/// Registers an instance of any Object type with the DI container.
void register<T extends Object>(T instance, {String? name});
Expand Down
2 changes: 1 addition & 1 deletion packages/system/vyuh_core/lib/plugin/feature_flag.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ abstract class FeatureFlagPlugin<TSettings, TContext> extends Plugin {
required this.settings,
required super.name,
required super.title,
}) : super(pluginType: PluginType.featureFlag);
}) : super();

/// Returns the value of the feature flag as a boolean.
Future<bool> getBool(String featureName, {bool defaultValue = false});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'package:vyuh_core/vyuh_core.dart';

abstract class LoggerPlugin extends Plugin {
LoggerPlugin({required super.name, required super.title})
: super(pluginType: PluginType.logger);
: super();

/// trace
void t(dynamic message);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ abstract class NavigationPlugin extends Plugin {
);

NavigationPlugin({required super.name, required super.title})
: super(pluginType: PluginType.navigation);
: super();

void initRouter({
String? initialLocation,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import 'dart:convert';
import 'package:http/http.dart';
import 'package:vyuh_core/vyuh_core.dart';

abstract class NetworkPlugin extends Plugin implements Client {
abstract class NetworkPlugin extends Plugin
with PreLoadedPlugin
implements Client {
NetworkPlugin({required super.name, required super.title})
: super(pluginType: PluginType.network);
: super();

@override
Future<Response> get(Uri url, {Map<String, String>? headers});
Expand Down
23 changes: 7 additions & 16 deletions packages/system/vyuh_core/lib/plugin/plugin.dart
Original file line number Diff line number Diff line change
@@ -1,24 +1,15 @@
enum PluginType {
content,
di,
analytics,
featureFlag,
navigation,
logger,
storage,
secureStorage,
network,
auth,
notifications,
ads,
}
/// A mixin to mark any plugin to be loaded before the Platform.
///
/// This mixin should be applied to plugins that need to be initialized
/// before the main platform initialization. It ensures that the
/// plugin is loaded at the correct time in the initialization sequence.
mixin PreLoadedPlugin on Plugin {}

pavanpodila marked this conversation as resolved.
Show resolved Hide resolved
abstract class Plugin {
final String name;
final String title;
final PluginType pluginType;

Plugin({required this.pluginType, required this.name, required this.title});
Plugin({required this.name, required this.title});

Future<void> init();
Future<void> dispose();
Expand Down
48 changes: 48 additions & 0 deletions packages/system/vyuh_core/lib/plugin/plugin_descriptor.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import 'package:vyuh_core/plugin/content/noop_content_plugin.dart';
import 'package:vyuh_core/plugin/di/di_plugin.dart';
import 'package:vyuh_core/plugin/di/plugin_di_get_it.dart';
import 'package:vyuh_core/plugin/plugin.dart';

import 'analytics/analytics_plugin.dart';
import 'analytics/noop_analytics_provider.dart';
import 'auth/anonymous_auth_plugin.dart';
import 'auth/auth_plugin.dart';
import 'content/content_plugin.dart';
import 'navigation/default_navigation_plugin.dart';
import 'navigation/navigation.dart';
import 'network/http_network_plugin.dart';
import 'network/network_plugin.dart';

class PluginDescriptor {
pavanpodila marked this conversation as resolved.
Show resolved Hide resolved
final Set<Plugin> _plugins = {};

List<Plugin> get plugins => List.unmodifiable(_plugins);

PluginDescriptor({
required final DIPlugin di,
required final ContentPlugin content,
required final AnalyticsPlugin analytics,
required final NetworkPlugin network,
required final AuthPlugin auth,
required final NavigationPlugin navigation,
final List<Plugin>? others,
}) {
_plugins.add(di);
_plugins.add(content);
_plugins.add(analytics);
_plugins.add(network);
_plugins.add(auth);
_plugins.add(navigation);
_plugins.add(navigation);
_plugins.addAll(others ?? []);
}
}

PluginDescriptor defaultPlugins() => PluginDescriptor(
pavanpodila marked this conversation as resolved.
Show resolved Hide resolved
di: GetItDIPlugin(),
content: NoOpContentPlugin(),
analytics: AnalyticsPlugin(providers: [NoOpAnalyticsProvider()]),
network: HttpNetworkPlugin(),
auth: UnknownAuthPlugin(),
navigation: DefaultNavigationPlugin(),
);
4 changes: 2 additions & 2 deletions packages/system/vyuh_core/lib/plugin/storage_plugin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'package:vyuh_core/vyuh_core.dart';

abstract class StoragePlugin extends Plugin {
StoragePlugin({required super.name, required super.title})
: super(pluginType: PluginType.storage);
: super();

Future<dynamic> read(String key);
Future<dynamic> write(String key, dynamic value);
Expand All @@ -12,7 +12,7 @@ abstract class StoragePlugin extends Plugin {

abstract class SecureStoragePlugin extends Plugin {
SecureStoragePlugin({required super.name, required super.title})
: super(pluginType: PluginType.secureStorage);
: super();

Future<dynamic> read(String key);
Future<dynamic> write(String key, dynamic value);
Expand Down
112 changes: 57 additions & 55 deletions packages/system/vyuh_core/lib/runtime/platform/default_platform.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,39 @@
part of '../run_app.dart';

class _CacheableLookupPluginList {
pavanpodila marked this conversation as resolved.
Show resolved Hide resolved
final Set<Plugin> _plugins = {};

_CacheableLookupPluginList();

final Map<Type, Plugin?> _pluginsMap = {};

T? getPlugin<T extends Plugin>() {
if (_pluginsMap.containsKey(T)) {
return _pluginsMap[T] as T?;
}

final plugin = _plugins.firstWhereOrNull((final plugin) => plugin is T);

_pluginsMap[T] = plugin;

return plugin as T?;
}

/// Add Items to List.
///
/// NOTE: This remove the Lookup cache.
void addAll(Iterable<Plugin> plugins) {
_plugins.addAll(plugins);
_pluginsMap.clear();
}

// Get the list
List<Plugin> get items => List.unmodifiable(_plugins);
}

final class _DefaultVyuhPlatform extends VyuhPlatform {
final Map<PluginType, Plugin> _pluginMap = {};
final _CacheableLookupPluginList _plugins = _CacheableLookupPluginList();

final Map<Type, ExtensionBuilder> _featureExtensionBuilderMap = {};

/// Initialize first time to avoid any late-init errors.
Expand Down Expand Up @@ -31,17 +63,24 @@ final class _DefaultVyuhPlatform extends VyuhPlatform {
@override
Future<void>? featureReady(String featureName) => _readyFeatures[featureName];

@override
final FeaturesBuilder featuresBuilder;

@override
List<Plugin> get plugins => _plugins.items;

@override
final PlatformWidgetBuilder widgetBuilder;

_DefaultVyuhPlatform({
required super.featuresBuilder,
required List<Plugin> plugins,
required super.widgetBuilder,
required this.featuresBuilder,
required PluginDescriptor pluginDescriptor,
required this.widgetBuilder,
this.initialLocation,
}) : super(plugins: _ensureRequiredPlugins(plugins)) {
_tracker = _PlatformInitTracker(this);
}) {
_plugins.addAll(pluginDescriptor.plugins);

for (final plugin in this.plugins) {
_pluginMap[plugin.pluginType] = plugin;
}
_tracker = _PlatformInitTracker(this);

reaction((_) => _tracker.currentState.value == InitState.notStarted,
(notStarted) {
Expand All @@ -53,36 +92,12 @@ final class _DefaultVyuhPlatform extends VyuhPlatform {
});
}

static List<Plugin> _ensureRequiredPlugins(List<Plugin> plugins) {
// Ensure there is only one plugin for a PluginType
final pluginTypes = plugins.groupListsBy((element) => element.pluginType);
for (final entry in pluginTypes.entries) {
assert(entry.value.length == 1,
'There can be only one plugin for a pluginType. We found ${entry.value.length} for ${entry.key}');
}

final allPlugins = [...plugins];
for (final type in VyuhPlatform.requiredPlugins) {
// Put a default plugin instance when a required plugin is not explicitly given
if (!allPlugins.any((element) => element.pluginType == type)) {
final instance = type.defaultInstance();

assert(instance != null,
'The default instance for the required plugin: $type has not been provided.');

if (instance != null) {
allPlugins.add(instance);
}
}
}

return allPlugins;
}

@override
Future<void> run() async {
for (final plugin in VyuhPlatform.preloadedPlugins) {
await _pluginMap[plugin]?.init();
final preLoadedPlugins = plugins.whereType<vt.PreLoadedPlugin>();

for (final plugin in preLoadedPlugins) {
await plugin.init();
}

_userInitialLocation = PlatformDispatcher.instance.defaultRouteName;
Expand All @@ -98,16 +113,16 @@ final class _DefaultVyuhPlatform extends VyuhPlatform {
parentTrace: parentTrace,
fn: (trace) async {
// Run a cleanup first
final disposeFns = _pluginMap.entries.map((e) => e.value.dispose());
final disposeFns = plugins.map((e) => e.dispose());
await Future.wait(disposeFns, eagerError: true);

// Check
final initFns = _pluginMap.entries.map((e) {
final initFns = plugins.map((e) {
return analytics.runWithTrace<void>(
name: 'Plugin: ${e.value.title}',
name: 'Plugin: ${e.title}',
operation: 'Init',
parentTrace: trace,
fn: (_) => e.value.init(),
fn: (_) => e.init(),
);
});

Expand Down Expand Up @@ -225,7 +240,7 @@ final class _DefaultVyuhPlatform extends VyuhPlatform {
}

@override
Plugin? getPlugin(PluginType type) => _pluginMap[type];
T? getPlugin<T extends vt.Plugin>() => _plugins.getPlugin<T>();
}

final class RoutingConfigNotifier extends ValueNotifier<g.RoutingConfig> {
Expand All @@ -236,16 +251,3 @@ final class RoutingConfigNotifier extends ValueNotifier<g.RoutingConfig> {
value = g.RoutingConfig(routes: routes);
}
}

extension on PluginType {
Plugin? defaultInstance() => switch (this) {
PluginType.analytics =>
AnalyticsPlugin(providers: [NoOpAnalyticsProvider()]),
PluginType.content => NoOpContentPlugin(),
PluginType.di => GetItDIPlugin(),
PluginType.network => HttpNetworkPlugin(),
PluginType.auth => UnknownAuthPlugin(),
PluginType.navigation => DefaultNavigationPlugin(),
_ => null
};
}
Loading
Loading