Skip to content

Commit

Permalink
feat(graphql_flutter): work on making subscriptions more of a first-c…
Browse files Browse the repository at this point in the history
…lass citizen
  • Loading branch information
micimize committed May 23, 2020
1 parent 6d0b045 commit a0e0d5c
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 220 deletions.
108 changes: 0 additions & 108 deletions packages/graphql_flutter/lib/src/widgets/_subscription.dart

This file was deleted.

11 changes: 3 additions & 8 deletions packages/graphql_flutter/lib/src/widgets/mutation.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'package:flutter/widgets.dart';

import 'package:graphql/client.dart';
import 'package:graphql/internal.dart';

import 'package:graphql_flutter/src/widgets/graphql_provider.dart';

Expand Down Expand Up @@ -54,7 +53,7 @@ class MutationState extends State<Mutation> {
bool _setNewOptions() {
final _cached = __cachedOptions;
final _new = _providedOptions;
if (_cached == null || !_new.areEqualTo(_cached)) {
if (_cached == null || !_new.equal(_cached)) {
__cachedOptions = _new;
return true;
}
Expand Down Expand Up @@ -94,7 +93,7 @@ class MutationState extends State<Mutation> {
Map<String, dynamic> variables, {
Object optimisticResult,
}) {
final mutationCallbacks = MutationCallbacks(
final mutationCallbacks = MutationCallbackHandler(
cache: client.cache,
queryId: observableQuery.queryId,
options: widget.options,
Expand All @@ -118,11 +117,7 @@ class MutationState extends State<Mutation> {
@override
Widget build(BuildContext context) {
return StreamBuilder<QueryResult>(
// we give the stream builder a key so that
// toggling mutations at the same place in the tree,
// such as is done in the example, won't result in bugs
key: Key(observableQuery?.options?.toKey()),
initialData: observableQuery?.latestResult ?? QueryResult(),
initialData: observableQuery?.latestResult ?? QueryResult(source: null),
stream: observableQuery?.stream,
builder: (
BuildContext buildContext,
Expand Down
20 changes: 3 additions & 17 deletions packages/graphql_flutter/lib/src/widgets/query.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'package:flutter/widgets.dart';

import 'package:graphql/client.dart';
import 'package:graphql/internal.dart';

import 'package:graphql_flutter/src/widgets/graphql_provider.dart';

Expand Down Expand Up @@ -35,20 +34,8 @@ class Query extends StatefulWidget {
class QueryState extends State<Query> {
ObservableQuery observableQuery;
GraphQLClient _client;
WatchQueryOptions get _options {
final QueryOptions options = widget.options;

return WatchQueryOptions(
document: options.document,
variables: options.variables,
fetchPolicy: options.fetchPolicy,
errorPolicy: options.errorPolicy,
pollInterval: options.pollInterval,
fetchResults: true,
context: options.context,
optimisticResult: options.optimisticResult,
);
}

WatchQueryOptions get _options => widget.options.asWatchQueryOptions();

void _initQuery() {
observableQuery?.close();
Expand Down Expand Up @@ -76,7 +63,7 @@ class QueryState extends State<Query> {
optionsWithOverrides.policies = client.defaultPolicies.watchQuery
.withOverrides(optionsWithOverrides.policies);

if (!observableQuery.options.areEqualTo(optionsWithOverrides)) {
if (!observableQuery.options.equal(optionsWithOverrides)) {
_initQuery();
}
}
Expand All @@ -90,7 +77,6 @@ class QueryState extends State<Query> {
@override
Widget build(BuildContext context) {
return StreamBuilder<QueryResult>(
key: Key(observableQuery?.options?.toKey()),
initialData: observableQuery?.latestResult ?? QueryResult.loading(),
stream: observableQuery.stream,
builder: (
Expand Down
126 changes: 39 additions & 87 deletions packages/graphql_flutter/lib/src/widgets/subscription.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,99 +3,69 @@ import 'dart:io';

import 'package:connectivity/connectivity.dart';
import 'package:flutter/widgets.dart';
import 'package:gql/language.dart';
import 'package:gql_exec/gql_exec.dart';
import 'package:graphql/client.dart';
import 'package:graphql/internal.dart';
import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:graphql_flutter/src/widgets/graphql_provider.dart';

typedef OnSubscriptionCompleted = void Function();
typedef OnSubscriptionResult = void Function(
QueryResult subscriptionResult,
GraphQLClient client,
);

typedef SubscriptionBuilder<T> = Widget Function({
bool loading,
T payload,
dynamic error,
});
typedef SubscriptionBuilder = Widget Function(QueryResult result);

class Subscription<T> extends StatefulWidget {
const Subscription(
this.operationName,
this.query, {
this.variables = const <String, dynamic>{},
final Key key,
const Subscription({
@required this.options,
@required this.builder,
this.initial,
this.onCompleted,
this.onSubscriptionResult,
Key key,
}) : super(key: key);

final String operationName;
final String query;
final Map<String, dynamic> variables;
final SubscriptionBuilder<T> builder;
final OnSubscriptionCompleted onCompleted;
final T initial;
final SubscriptionOptions options;
final SubscriptionBuilder builder;
final OnSubscriptionResult onSubscriptionResult;

@override
_SubscriptionState<T> createState() => _SubscriptionState<T>();
}

class _SubscriptionState<T> extends State<Subscription<T>> {
bool _loading = true;
T _data;
dynamic _error;
StreamSubscription<Response> _subscription;
GraphQLClient _client;
Stream<QueryResult> stream;
GraphQLClient client;

ConnectivityResult _currentConnectivityResult;
StreamSubscription<ConnectivityResult> _networkSubscription;

void _initSubscription() {
final GraphQLClient client = GraphQLProvider.of(context).value;
assert(client != null);
final Request request = Request(
operation: Operation(
document: parseString(widget.query),
operationName: widget.operationName,
),
variables: widget.variables,
);

final Stream<Response> stream = _client.subscribe(request);
stream = client.subscribe(widget.options);

if (_subscription == null) {
// Set the initial value for the first time.
if (widget.initial != null) {
setState(() {
_loading = true;
_data = widget.initial;
_error = null;
});
}
if (widget.onSubscriptionResult != null) {
stream = stream.map((result) {
widget.onSubscriptionResult(result, client);
return result;
});
}

_subscription?.cancel();
_subscription = stream.listen(
_onData,
onError: _onError,
onDone: _onDone,
);
}

@override
void initState() {
_networkSubscription = Connectivity().onConnectivityChanged.listen(
(ConnectivityResult result) async => await _onNetworkChange(result));
_networkSubscription =
Connectivity().onConnectivityChanged.listen(_onNetworkChange);

super.initState();
}

@override
void didChangeDependencies() {
super.didChangeDependencies();
final GraphQLClient client = GraphQLProvider.of(context).value;
assert(client != null);
if (client != _client) {
_client = client;
final GraphQLClient newClient = GraphQLProvider.of(context).value;
assert(newClient != null);
if (client != newClient) {
client = newClient;
_initSubscription();
}
}
Expand All @@ -104,42 +74,17 @@ class _SubscriptionState<T> extends State<Subscription<T>> {
void didUpdateWidget(Subscription<T> oldWidget) {
super.didUpdateWidget(oldWidget);

if (widget.query != oldWidget.query ||
widget.operationName != oldWidget.operationName ||
areDifferentVariables(widget.variables, oldWidget.variables)) {
if (!widget.options.equal(oldWidget.options)) {
_initSubscription();
}
}

@override
void dispose() {
_subscription?.cancel();
_networkSubscription?.cancel();
super.dispose();
}

void _onData(final Response message) {
setState(() {
_loading = false;
_data = message.data as T;
_error = message.errors;
});
}

void _onError(final Object error) {
setState(() {
_loading = false;
_data = null;
_error = error;
});
}

void _onDone() {
if (widget.onCompleted != null) {
widget.onCompleted();
}
}

Future _onNetworkChange(ConnectivityResult result) async {
//if from offline to online
if (_currentConnectivityResult == ConnectivityResult.none &&
Expand Down Expand Up @@ -170,10 +115,17 @@ class _SubscriptionState<T> extends State<Subscription<T>> {

@override
Widget build(final BuildContext context) {
return widget.builder(
loading: _loading,
error: _error,
payload: _data,
return StreamBuilder<QueryResult>(
initialData: widget.options?.optimisticResult != null
? QueryResult.optimistic(data: widget.options?.optimisticResult)
: QueryResult.loading(),
stream: stream,
builder: (
BuildContext buildContext,
AsyncSnapshot<QueryResult> snapshot,
) {
return widget?.builder(snapshot.data);
},
);
}
}

0 comments on commit a0e0d5c

Please sign in to comment.