Skip to content

Commit

Permalink
Update OctopusInformationProvider
Browse files Browse the repository at this point in the history
  • Loading branch information
PlugFox committed Dec 27, 2023
1 parent c3a7d60 commit 5d649ed
Show file tree
Hide file tree
Showing 5 changed files with 340 additions and 215 deletions.
239 changes: 29 additions & 210 deletions lib/src/controller/information_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart';
import 'package:octopus/src/state/state.dart';
import 'package:octopus/src/state/state_codec.dart';
import 'package:octopus/src/util/jenkins_hash.dart';
import 'package:octopus/src/util/logs.dart';
import 'package:octopus/src/util/system_navigator_util.dart';
import 'package:octopus/src/controller/information_provider_js.dart';
import 'package:octopus/src/controller/information_provider_vm.dart';

/// The route information provider that propagates
/// the platform route information changes.
Expand All @@ -25,35 +22,33 @@ import 'package:octopus/src/util/system_navigator_util.dart';
///
/// {@nodoc}
@internal
class OctopusInformationProvider extends RouteInformationProvider
abstract base class OctopusInformationProvider extends RouteInformationProvider
with WidgetsBindingObserver, ChangeNotifier {
factory OctopusInformationProvider({
RouteInformation? initialRouteInformation,
Listenable? refreshListenable,
}) {
final valueInEngine = _initialRouteInformation();
return OctopusInformationProvider._(
valueInEngine: valueInEngine,
value: initialRouteInformation ?? valueInEngine,
refreshListenable: refreshListenable,
);
}

/// {@nodoc}
OctopusInformationProvider._({
required RouteInformation valueInEngine,
required RouteInformation value,
OctopusInformationProvider({
Listenable? refreshListenable,
}) : _value = value,
_valueInEngine = valueInEngine,
_refreshListenable = refreshListenable {
/* if (kFlutterMemoryAllocationsEnabled) {
}) : _refreshListenable = refreshListenable {
if (kFlutterMemoryAllocationsEnabled) {
ChangeNotifier.maybeDispatchObjectCreation(this);
} */
}
_refreshListenable?.addListener(notifyListeners);
}

static RouteInformation _initialRouteInformation() {
factory OctopusInformationProvider.platform({
RouteInformation? initialRouteInformation,
Listenable? refreshListenable,
}) =>
kIsWeb
? OctopusInformationProvider$JS(
initialRouteInformation: initialRouteInformation,
refreshListenable: refreshListenable,
)
: OctopusInformationProvider$VM(
initialRouteInformation: initialRouteInformation,
refreshListenable: refreshListenable,
);

static RouteInformation initialRouteInformation() {
final platformDefault =
WidgetsBinding.instance.platformDispatcher.defaultRouteName;
Uri? uri;
Expand All @@ -62,213 +57,37 @@ class OctopusInformationProvider extends RouteInformationProvider
} else {
uri = Uri.tryParse(platformDefault);
}
return uri == null ? _kEmptyRouteInformation : RouteInformation(uri: uri);
return uri == null ? kEmptyRouteInformation : RouteInformation(uri: uri);
}

final Listenable? _refreshListenable;

static WidgetsBinding get _binding => WidgetsBinding.instance;
static final RouteInformation _kEmptyRouteInformation =
static final RouteInformation kEmptyRouteInformation =
RouteInformation(uri: Uri());

@override
void routerReportsNewRouteInformation(
RouteInformation routeInformation, {
RouteInformationReportingType type = RouteInformationReportingType.none,
}) {
/* if (neglectIf != null && neglectIf!(routeInformation.uri.toString())) {
return;
} */

fine('routerReportsNewRouteInformation(${routeInformation.uri}, '
'${routeInformation.state})');

if (routeInformation is OctopusRouteInformation) {
if (routeInformation.intention == OctopusStateIntention.cancel) return;
if (routeInformation.intention == OctopusStateIntention.neglect) return;
}

// Avoid adding a new history entry if the route is the same as before.
/* final replace = type == RouteInformationReportingType.neglect ||
(type == RouteInformationReportingType.none &&
_valueInEngine.uri == routeInformation.uri &&
_valueInEngine.state == routeInformation.state); */

var replace = false;
switch (routeInformation) {
case OctopusRouteInformation info
when info.intention == OctopusStateIntention.cancel ||
info.intention == OctopusStateIntention.neglect:
return;
case OctopusRouteInformation info
when info.intention == OctopusStateIntention.replace:
replace = true;
case OctopusRouteInformation info
when info.intention == OctopusStateIntention.navigate:
replace = false;
}

switch (type) {
case RouteInformationReportingType.none:
if (_valueInEngine.uri == routeInformation.uri) {
replace = true;
if (identical(_valueInEngine.state, routeInformation.state)) {
return;
} else {
final hashA = jenkinsHash(_valueInEngine.state);
final hashB = jenkinsHash(routeInformation.state);
if (hashA == hashB) return;
}
}
case RouteInformationReportingType.neglect:
replace = true;
case RouteInformationReportingType.navigate:
replace = false;
}

// If the route is different from the current route, then update the engine.
/* if (kIsWeb && routeInformation.uri == _value.uri) {
config('Uri: ${routeInformation.uri}');
} */
/* SystemNavigator.selectMultiEntryHistory(); // selectSingleEntryHistory
SystemNavigator.routeInformationUpdated(
uri: routeInformation.uri,
state: routeInformation.state,
replace: replace,
); */

if (replace) {
SystemNavigatorUtil.replaceState(
data: routeInformation.state,
url: routeInformation.uri,
/* title: , */
);
} else {
SystemNavigatorUtil.pushState(
data: routeInformation.state,
url: routeInformation.uri,
/* title: , */
);
}
_value = _valueInEngine = routeInformation;
}

@override
RouteInformation get value => _value;
RouteInformation _value;
set value(RouteInformation other) {
final shouldNotify = _value.uri != other.uri || _value.state != other.state;
_value = other;
if (shouldNotify) notifyListeners();
}

RouteInformation _valueInEngine;

void pushRoute(RouteInformation routeInformation) {
if (_value == routeInformation) return;
fine('pushRoute(${routeInformation.uri}, ${routeInformation.state})');
// If the route information is an OctopusRouteInformation,
// then handle it and set it as the current route information.
if (routeInformation is OctopusRouteInformation) {
_value = _valueInEngine = routeInformation;
notifyListeners();
return;
}
// If the route information has a state containing information about
// the children, then handle it, decode the state and set it as the
// current route information.
if (routeInformation.state case Map<String, Object?> json) {
if (json.containsKey('children')) {
final state = OctopusState.fromJson(json);
_value = _valueInEngine = OctopusRouteInformation(state);
notifyListeners();
return;
}
}
final uri = routeInformation.uri;
// If location does not start with a '/',
// then handle it as a pop operation.
if (!routeInformation.uri.path.startsWith('/'))
return popUri(routeInformation);

_value = RouteInformation(
uri: uri,
state: null,
);
_valueInEngine = _kEmptyRouteInformation;
notifyListeners();
}

void popUri(RouteInformation routeInformation) {
final popUri = routeInformation.uri;
fine('popFromUri($popUri)');
var popTo = popUri.path;
popTo = popTo.startsWith('/') ? popTo : '/$popTo';
var path = _value.uri.path;
if (path.endsWith(popTo)) {
path = path.substring(0, path.length - popTo.length);
} else {
final idx = path.lastIndexOf('$popTo/');
if (idx == -1) {
warning('Cannot pop to "$popTo" from "$path"');
} else {
path = path.substring(0, idx);
}
}
while (path.startsWith('//')) path = path.substring(1);
while (path.endsWith('/')) path = path.substring(0, path.length - 1);
final Listenable? _refreshListenable;

//if (path.isEmpty || path == '/')
_value = RouteInformation(
uri: _value.uri.replace(path: path),
state: null,
);
_valueInEngine = _kEmptyRouteInformation;
notifyListeners();
}
static WidgetsBinding get _binding => WidgetsBinding.instance;

@override
@mustCallSuper
void addListener(VoidCallback listener) {
if (!hasListeners) _binding.addObserver(this);
super.addListener(listener);
}

@override
@mustCallSuper
void removeListener(VoidCallback listener) {
super.removeListener(listener);
if (!hasListeners) _binding.removeObserver(this);
}

@override
@mustCallSuper
void dispose() {
if (hasListeners) _binding.removeObserver(this);
_refreshListenable?.removeListener(notifyListeners);
super.dispose();
}

@override
Future<bool> didPushRouteInformation(RouteInformation routeInformation) {
assert(
hasListeners,
'A OctopusInformationProvider must have '
'at least one listener before it can be used.');
pushRoute(routeInformation);
return SynchronousFuture<bool>(true);
}

@override
@Deprecated('Use didPushRouteInformation instead')
Future<bool> didPushRoute(String route) {
assert(
hasListeners,
'A OctopusInformationProvider must have '
'at least one listener before it can be used.');
pushRoute(RouteInformation(uri: Uri.tryParse(route)));
return SynchronousFuture<bool>(true);
}

@override
Future<bool> didPopRoute() => Future<bool>.value(false);
}

/* Useful methods for the package
Expand Down
Loading

0 comments on commit 5d649ed

Please sign in to comment.