diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 000000000..a8dd56dea
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,14 @@
+{
+ // 使用 IntelliSense 了解相关属性。
+ // 悬停以查看现有属性的描述。
+ // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Flutter",
+ "type": "dart",
+ "request": "launch",
+ "program": "example/lib/main.dart"
+ }
+ ]
+}
diff --git a/README.md b/README.md
index 495fb4bd8..827603fc0 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@
# Parse Server Dart
+
A rewrite of a library hosted on GitHub. This is not my own content but based on a library already created and looks to be abandoned.
https://github.com/lotux/parse_server_dart
-## Join in!
-Want to get involved? Join our Slack channel and help out! FlutterParseSDK.Slack.com
+## example 目录有完整测试
## Getting Started
diff --git a/example/README.md b/example/README.md
index 17ecae4d2..3ae09ee5f 100644
--- a/example/README.md
+++ b/example/README.md
@@ -1,8 +1,9 @@
-# flutter_plugin_example
+# flutter_parse_client_example
-Demonstrates how to use the flutter_plugin plugin.
+Demonstrates how to use the flutter parse client.
-## Getting Started
+## html ok
-For help getting started with Flutter, view our online
-[documentation](https://flutter.io/).
+## websoket ok
+
+## bloc
diff --git a/example/android/.project b/example/android/.project
new file mode 100644
index 000000000..3964dd3f5
--- /dev/null
+++ b/example/android/.project
@@ -0,0 +1,17 @@
+
+
+ android
+ Project android created by Buildship.
+
+
+
+
+ org.eclipse.buildship.core.gradleprojectbuilder
+
+
+
+
+
+ org.eclipse.buildship.core.gradleprojectnature
+
+
diff --git a/example/android/.settings/org.eclipse.buildship.core.prefs b/example/android/.settings/org.eclipse.buildship.core.prefs
new file mode 100644
index 000000000..e8895216f
--- /dev/null
+++ b/example/android/.settings/org.eclipse.buildship.core.prefs
@@ -0,0 +1,2 @@
+connection.project.dir=
+eclipse.preferences.version=1
diff --git a/example/android/app/.classpath b/example/android/app/.classpath
new file mode 100644
index 000000000..eb19361b5
--- /dev/null
+++ b/example/android/app/.classpath
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/example/android/app/.project b/example/android/app/.project
new file mode 100644
index 000000000..ac485d7c3
--- /dev/null
+++ b/example/android/app/.project
@@ -0,0 +1,23 @@
+
+
+ app
+ Project app created by Buildship.
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.buildship.core.gradleprojectbuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.buildship.core.gradleprojectnature
+
+
diff --git a/example/android/app/.settings/org.eclipse.buildship.core.prefs b/example/android/app/.settings/org.eclipse.buildship.core.prefs
new file mode 100644
index 000000000..b1886adb4
--- /dev/null
+++ b/example/android/app/.settings/org.eclipse.buildship.core.prefs
@@ -0,0 +1,2 @@
+connection.project.dir=..
+eclipse.preferences.version=1
diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle
index 9883bcabb..c88be5c97 100644
--- a/example/android/app/build.gradle
+++ b/example/android/app/build.gradle
@@ -11,6 +11,16 @@ if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+ flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+ flutterVersionName = '1.0'
+}
+
apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
@@ -24,10 +34,10 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.flutterpluginexample"
- minSdkVersion 16
+ minSdkVersion 21
targetSdkVersion 27
- versionCode 1
- versionName "1.0"
+ versionCode flutterVersionCode.toInteger()
+ versionName flutterVersionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
@@ -46,6 +56,8 @@ flutter {
dependencies {
testImplementation 'junit:junit:4.12'
- androidTestImplementation 'com.android.support.test:runner:1.0.1'
- androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
+ androidTestImplementation 'com.android.support.test:runner:1.0.2'
+ androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
+ // implementation "com.android.support:appcompat-v7:27.1.0"
}
+// apply plugin: 'com.google.gms.google-services'
diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml
index 9fbcae13f..c3f5a810c 100644
--- a/example/android/app/src/main/AndroidManifest.xml
+++ b/example/android/app/src/main/AndroidManifest.xml
@@ -14,7 +14,7 @@
FlutterApplication and put your custom class here. -->
+ if (details.requested.group == 'com.android.support'
+ && !details.requested.name.contains('multidex') ) {
+ details.useVersion "26.1.0"
+ }
+ }
+ }
+}
diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties
index aa901e1e0..9372d0f3f 100644
--- a/example/android/gradle/wrapper/gradle-wrapper.properties
+++ b/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
diff --git a/example/android/gradlew b/example/android/gradlew
old mode 100644
new mode 100755
diff --git a/example/lib/ApplicationBloc.dart b/example/lib/ApplicationBloc.dart
new file mode 100644
index 000000000..0b66028c3
--- /dev/null
+++ b/example/lib/ApplicationBloc.dart
@@ -0,0 +1,38 @@
+import 'dart:async';
+
+import 'package:Parse_example/blocProvider.dart';
+import 'package:parse_server_sdk/network/parse_livequery.dart';
+
+class ApplicationBloc implements BlocBase {
+ ///
+ /// Synchronous Stream to handle the provision of the movie genres
+ ///
+ StreamController _syncController = StreamController.broadcast();
+ Stream get outParseStream => _syncController.stream;
+
+ ///
+ StreamController _cmdController = StreamController.broadcast();
+ StreamSink get getMovieGenres => _cmdController.sink;
+
+ List _genresList;
+
+ ApplicationBloc(LiveQuery liveQuery) {
+ // _genresList = liveQuery.channel as List;
+ // Read all genres from Internet
+ // api.movieGenres().then((list) {
+ // _genresList = list;
+ // });
+ _syncController.stream.listen((_) {
+ _syncController.sink.addStream(liveQuery.channel.stream);
+ });
+ // _cmdController.stream.listen((_) {
+ // _syncController.sink
+ // .add(UnmodifiableListView(_genresList.genres));
+ // });
+ }
+
+ void dispose() {
+ _syncController.close();
+ _cmdController.close();
+ }
+}
diff --git a/example/lib/IncrementBloc.dart b/example/lib/IncrementBloc.dart
new file mode 100644
index 000000000..22d95db8c
--- /dev/null
+++ b/example/lib/IncrementBloc.dart
@@ -0,0 +1,32 @@
+import 'dart:async';
+
+import 'package:Parse_example/blocProvider.dart';
+
+class IncrementBloc implements BlocBase {
+ int _counter;
+
+ // 处理counter的stream
+ StreamController _counterController = StreamController();
+ StreamSink get _inAdd => _counterController.sink;
+ Stream get outCounter => _counterController.stream;
+
+ // 处理业务逻辑的stream
+ StreamController _actionController = StreamController();
+ StreamSink get incrementCounter => _actionController.sink;
+
+ // 构造器
+ IncrementBloc() {
+ _counter = 0;
+ _actionController.stream.listen(_handleLogic);
+ }
+
+ void dispose() {
+ _actionController.close();
+ _counterController.close();
+ }
+
+ void _handleLogic(data) {
+ _counter = _counter + 1;
+ _inAdd.add(_counter);
+ }
+}
diff --git a/example/lib/application_constants.dart b/example/lib/application_constants.dart
index 7ddb84685..917f4e902 100644
--- a/example/lib/application_constants.dart
+++ b/example/lib/application_constants.dart
@@ -1,6 +1,7 @@
abstract class ApplicationConstants {
- static const String APP_NAME = "";
- static const String PARSE_APPLICATION_ID = "";
- static const String PARSE_MASTER_KEY = "";
- static const String PARSE_SERVER_URL = "";
-}
\ No newline at end of file
+ static const String APP_NAME = "MyApp";
+ static const String PARSE_APPLICATION_ID = "myAppId";
+ static const String PARSE_MASTER_KEY = "123456";
+ static const String PARSE_SERVER_URL = "http://118.24.162.252:2018/parse";
+ static const String PARSE_LIVE_SERVER_URL = "ws://118.24.162.252:2018/parse";
+}
diff --git a/example/lib/blocProvider.dart b/example/lib/blocProvider.dart
new file mode 100644
index 000000000..5e268cbdd
--- /dev/null
+++ b/example/lib/blocProvider.dart
@@ -0,0 +1,40 @@
+import 'package:flutter/material.dart';
+
+abstract class BlocBase {
+ void dispose();
+}
+
+class BlocProvider extends StatefulWidget {
+ BlocProvider({
+ Key key,
+ @required this.child,
+ @required this.bloc,
+ }) : super(key: key);
+
+ final T bloc;
+ final Widget child;
+
+ @override
+ _BlocProviderState createState() => _BlocProviderState();
+
+ static T of(BuildContext context) {
+ final type = _typeOf>();
+ BlocProvider provider = context.ancestorWidgetOfExactType(type);
+ return provider.bloc;
+ }
+
+ static Type _typeOf() => T;
+}
+
+class _BlocProviderState extends State> {
+ @override
+ void dispose() {
+ widget.bloc.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return widget.child;
+ }
+}
diff --git a/example/lib/count_bloc.dart b/example/lib/count_bloc.dart
new file mode 100644
index 000000000..15ce9dc17
--- /dev/null
+++ b/example/lib/count_bloc.dart
@@ -0,0 +1,19 @@
+import 'dart:async';
+import 'package:Parse_example/blocProvider.dart';
+import 'package:rxdart/rxdart.dart';
+
+class CountBLoC implements BlocBase {
+ int _count = 0;
+ var _countController = StreamController.broadcast();
+ var _subject = BehaviorSubject();
+ Stream get stream => _subject.stream;
+ int get value => _count;
+
+ increment() {
+ _countController.sink.add(++_count);
+ }
+
+ dispose() {
+ _countController.close();
+ }
+}
diff --git a/example/lib/diet_plan.dart b/example/lib/diet_plan.dart
index b1ec90d51..bba2671f7 100644
--- a/example/lib/diet_plan.dart
+++ b/example/lib/diet_plan.dart
@@ -12,32 +12,32 @@ class DietPlan extends ParseObject {
num fat;
num status;
- static const String DIET_PLAN = 'Diet_Plans';
- static const String NAME = 'Name';
- static const String DESCRIPTION = 'Description';
- static const String PROTEIN = 'Protein';
- static const String CARBS = 'Carbs';
- static const String FAT = 'Fat';
- static const String STATUS = 'Status';
+ static const String DIET_PLAN = 'post';
+ static const String NAME = 'title';
+ // static const String DESCRIPTION = 'text';
+ // static const String PROTEIN = 'Protein';
+ // static const String CARBS = 'Carbs';
+ // static const String FAT = 'Fat';
+ // static const String STATUS = 'Status';
@override
dynamic fromJson(Map objectData) {
this.name = objectData[NAME];
- this.description = objectData[DESCRIPTION];
- this.protein = objectData[PROTEIN];
- this.carbs = objectData[CARBS];
- this.fat = objectData[FAT];
- this.status = objectData[STATUS];
+ // this.description = objectData[DESCRIPTION];
+ // this.protein = objectData[PROTEIN];
+ // this.carbs = objectData[CARBS];
+ // this.fat = objectData[FAT];
+ // this.status = objectData[STATUS];
return this;
}
Map toJson() => {
NAME: name,
- DESCRIPTION: description,
- PROTEIN: protein,
- CARBS: carbs,
- FAT: fat,
- STATUS: status,
+ // DESCRIPTION: description,
+ // PROTEIN: protein,
+ // CARBS: carbs,
+ // FAT: fat,
+ // STATUS: status,
};
@override
diff --git a/example/lib/gametest/GameCommunication.dart b/example/lib/gametest/GameCommunication.dart
new file mode 100644
index 000000000..ecd4ea8a3
--- /dev/null
+++ b/example/lib/gametest/GameCommunication.dart
@@ -0,0 +1,116 @@
+import 'dart:convert';
+
+import 'package:flutter/foundation.dart';
+import 'WebSocketsNotifications.dart';
+
+///
+/// Again, application-level global variable
+///
+GameCommunication game = new GameCommunication();
+
+class GameCommunication {
+ static final GameCommunication _game = new GameCommunication._internal();
+
+ ///
+ /// At first initialization, the player has not yet provided any name
+ ///
+ String _playerName = "";
+
+ ///
+ /// Before the "join" action, the player has no unique ID
+ ///
+ String _playerID = "";
+
+ factory GameCommunication() {
+ return _game;
+ }
+
+ GameCommunication._internal() {
+ ///
+ /// Let's initialize the WebSockets communication
+ ///
+ sockets.initCommunication();
+
+ ///
+ /// and ask to be notified as soon as a message comes in
+ ///
+ sockets.addListener(_onMessageReceived);
+ }
+
+ ///
+ /// Getter to return the player's name
+ ///
+ String get playerName => _playerName;
+
+ /// ----------------------------------------------------------
+ /// Common handler for all received messages, from the server
+ /// ----------------------------------------------------------
+ _onMessageReceived(serverMessage) {
+ ///
+ /// As messages are sent as a String
+ /// let's deserialize it to get the corresponding
+ /// JSON object
+ ///
+ Map message = json.decode(serverMessage);
+
+ switch (message["action"]) {
+
+ ///
+ /// When the communication is established, the server
+ /// returns the unique identifier of the player.
+ /// Let's record it
+ ///
+ case 'connect':
+ _playerID = message["data"];
+ break;
+
+ ///
+ /// For any other incoming message, we need to
+ /// dispatch it to all the listeners
+ ///
+ default:
+ _listeners.forEach((Function callback) {
+ callback(message);
+ });
+ break;
+ }
+ }
+
+ /// ----------------------------------------------------------
+ /// Common method to send requests to the server
+ /// ----------------------------------------------------------
+ send(String action, String data) {
+ ///
+ /// When a player joins, we need to record the name
+ /// he provides
+ ///
+ if (action == 'join') {
+ _playerName = data;
+ }
+
+ ///
+ /// Send the action to the server
+ /// To send the message, we need to serialize the JSON
+ ///
+ sockets.send(json.encode({"action": action, "data": data}));
+ }
+
+ /// ==========================================================
+ ///
+ /// Listeners to allow the different pages to be notified
+ /// when messages come in
+ ///
+ ObserverList _listeners = new ObserverList();
+
+ /// ---------------------------------------------------------
+ /// Adds a callback to be invoked in case of incoming
+ /// notification
+ /// ---------------------------------------------------------
+ addListener(Function callback) {
+ _listeners.add(callback);
+ }
+
+ removeListener(Function callback) {
+ _listeners.remove(callback);
+ }
+}
diff --git a/example/lib/gametest/GamePage.dart b/example/lib/gametest/GamePage.dart
new file mode 100644
index 000000000..82d081475
--- /dev/null
+++ b/example/lib/gametest/GamePage.dart
@@ -0,0 +1,163 @@
+import 'package:Parse_example/gametest/GameCommunication.dart';
+import 'package:flutter/material.dart';
+
+class GamePage extends StatefulWidget {
+ GamePage({
+ Key key,
+ this.opponentName,
+ this.character,
+ }) : super(key: key);
+
+ ///
+ /// Name of the opponent
+ ///
+ final String opponentName;
+
+ ///
+ /// Character to be used by the player for his/her moves ("X" or "O")
+ ///
+ final String character;
+
+ @override
+ _GamePageState createState() => _GamePageState();
+}
+
+class _GamePageState extends State {
+ ///
+ /// One game in terms of grid cells.
+ /// When the user plays, one of this cells is filled with "X" or "O"
+ ///
+ List grid = ["", "", "", "", "", "", "", "", ""];
+
+ @override
+ void initState() {
+ super.initState();
+
+ ///
+ /// Ask to be notified when a message from the server
+ /// comes in.
+ ///
+ game.addListener(_onAction);
+ }
+
+ @override
+ void dispose() {
+ game.removeListener(_onAction);
+ super.dispose();
+ }
+
+ /// ---------------------------------------------------------
+ /// The opponent took an action
+ /// Handler of these actions
+ /// ---------------------------------------------------------
+ _onAction(message) {
+ switch (message["action"]) {
+
+ ///
+ /// The opponent resigned, so let's leave this screen
+ ///
+ case 'resigned':
+ Navigator.of(context).pop();
+ break;
+
+ ///
+ /// The opponent played a move.
+ /// So record it and rebuild the board
+ ///
+ case 'play':
+ var data = (message["data"] as String).split(';');
+ grid[int.parse(data[0])] = data[1];
+
+ // Force rebuild
+ setState(() {});
+ break;
+ }
+ }
+
+ /// ---------------------------------------------------------
+ /// This player resigns
+ /// We need to send this notification to the other player
+ /// Then, leave this screen
+ /// ---------------------------------------------------------
+ _doResign() {
+ game.send('resign', '');
+ Navigator.of(context).pop();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return new SafeArea(
+ top: false,
+ bottom: false,
+ child: new Scaffold(
+ appBar: new AppBar(
+ title: new Text('Game against: ${widget.opponentName}',
+ style: new TextStyle(fontSize: 16.0)),
+ actions: [
+ new RaisedButton(
+ onPressed: _doResign,
+ child: new Text('Resign'),
+ ),
+ ]),
+ body: _buildBoard(),
+ ),
+ );
+ }
+
+ /// --------------------------------------------------------
+ /// Builds the Game Board.
+ /// --------------------------------------------------------
+ Widget _buildBoard() {
+ return new SafeArea(
+ top: false,
+ bottom: false,
+ child: new GridView.builder(
+ gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(
+ crossAxisCount: 3,
+ ),
+ itemCount: 9,
+ itemBuilder: (BuildContext context, int index) {
+ return _gridItem(index);
+ },
+ ),
+ );
+ }
+
+ Widget _gridItem(int index) {
+ Color color = grid[index] == "X" ? Colors.blue : Colors.red;
+
+ return new InkWell(
+ onTap: () {
+ ///
+ /// The user taps a cell.
+ /// If the latter is empty, let's put this player's character
+ /// and notify the other player.
+ /// Repaint the board
+ ///
+ if (grid[index] == "") {
+ grid[index] = widget.character;
+
+ ///
+ /// To send a move, we provide the cell index
+ /// and the character of this player
+ ///
+ game.send('play', '$index;${widget.character}');
+
+ /// Force the board repaint
+ setState(() {});
+ }
+ },
+ child: new GridTile(
+ child: new Card(
+ child: new FittedBox(
+ fit: BoxFit.contain,
+ child: new Text(grid[index],
+ style: new TextStyle(
+ fontSize: 50.0,
+ color: color,
+ ))),
+ ),
+ ),
+ );
+ }
+}
diff --git a/example/lib/gametest/StartPage.dart b/example/lib/gametest/StartPage.dart
new file mode 100644
index 000000000..8b3bd15e8
--- /dev/null
+++ b/example/lib/gametest/StartPage.dart
@@ -0,0 +1,197 @@
+import 'package:Parse_example/gametest/GameCommunication.dart';
+import 'package:Parse_example/gametest/GamePage.dart';
+import 'package:flutter/material.dart';
+
+class StartPage extends StatefulWidget {
+ @override
+ _StartPageState createState() => _StartPageState();
+}
+
+class _StartPageState extends State {
+ static final TextEditingController _name = new TextEditingController();
+ String playerName;
+ List playersList = [];
+
+ @override
+ void initState() {
+ super.initState();
+
+ ///
+ /// Ask to be notified when messages related to the game
+ /// are sent by the server
+ ///
+ game.addListener(_onGameDataReceived);
+ }
+
+ @override
+ void dispose() {
+ game.removeListener(_onGameDataReceived);
+ super.dispose();
+ }
+
+ /// -------------------------------------------------------------------
+ /// This routine handles all messages that are sent by the server.
+ /// In this page, only the following 2 actions have to be processed
+ /// - players_list
+ /// - new_game
+ /// -------------------------------------------------------------------
+ _onGameDataReceived(message) {
+ switch (message["action"]) {
+
+ ///
+ /// Each time a new player joins, we need to
+ /// * record the new list of players
+ /// * rebuild the list of all the players
+ ///
+ case "players_list":
+ playersList = message["data"];
+
+ // force rebuild
+ setState(() {});
+ break;
+
+ ///
+ /// When a game is launched by another player,
+ /// we accept the new game and automatically redirect
+ /// to the game board.
+ /// As we are not the new game initiator, we will be
+ /// using the "O"
+ ///
+ case 'new_game':
+ Navigator.push(
+ context,
+ new MaterialPageRoute(
+ builder: (BuildContext context) => new GamePage(
+ opponentName: message["data"], // Name of the opponent
+ character: 'O',
+ ),
+ ));
+ break;
+ }
+ }
+
+ /// -----------------------------------------------------------
+ /// If the user has not yet joined, let the user enter
+ /// his/her name and join the list of players
+ /// -----------------------------------------------------------
+ Widget _buildJoin() {
+ if (game.playerName != "") {
+ return new Container();
+ }
+ return new Container(
+ padding: const EdgeInsets.all(16.0),
+ child: new Column(
+ children: [
+ new TextField(
+ controller: _name,
+ keyboardType: TextInputType.text,
+ decoration: new InputDecoration(
+ hintText: 'Enter your name',
+ contentPadding: const EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
+ border: new OutlineInputBorder(
+ borderRadius: new BorderRadius.circular(32.0),
+ ),
+ icon: const Icon(Icons.person),
+ ),
+ ),
+ new Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: new RaisedButton(
+ onPressed: _onGameJoin,
+ child: new Text('Join...'),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+
+ /// ------------------------------------------------------
+ /// The user wants to join, so let's send his/her name
+ /// As the user has a name, we may now show the other players
+ /// ------------------------------------------------------
+ _onGameJoin() {
+ game.send('join', _name.text);
+
+ /// Force a rebuild
+ setState(() {});
+ }
+
+ /// ------------------------------------------------------
+ /// Builds the list of players
+ /// ------------------------------------------------------
+ Widget _playersList() {
+ ///
+ /// If the user has not yet joined, do not display
+ /// the list of players
+ ///
+ if (game.playerName == "") {
+ return new Container();
+ }
+
+ ///
+ /// Display the list of players.
+ /// For each of them, put a Button that could be used
+ /// to launch a new game
+ ///
+ List children = playersList.map((playerInfo) {
+ return new ListTile(
+ title: new Text(playerInfo["name"]),
+ trailing: new RaisedButton(
+ onPressed: () {
+ _onPlayGame(playerInfo["name"], playerInfo["id"]);
+ },
+ child: new Text('Play'),
+ ),
+ );
+ }).toList();
+
+ return new Column(
+ children: children,
+ );
+ }
+
+ /// --------------------------------------------------------------
+ /// We launch a new Game, we need to:
+ /// * send the action "new_game", together with the ID
+ /// of the opponent we choosed
+ /// * redirect to the game board
+ /// As we are the game initiator, we will play with the "X"
+ /// --------------------------------------------------------------
+ _onPlayGame(String opponentName, String opponentId) {
+ // We need to send the opponentId to initiate a new game
+ game.send('new_game', opponentId);
+
+ Navigator.push(
+ context,
+ new MaterialPageRoute(
+ builder: (BuildContext context) => new GamePage(
+ opponentName: opponentName,
+ character: 'X',
+ ),
+ ));
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return new SafeArea(
+ bottom: false,
+ top: false,
+ child: Scaffold(
+ appBar: new AppBar(
+ title: new Text('TicTacToe'),
+ ),
+ body: SingleChildScrollView(
+ child: new Column(
+ mainAxisAlignment: MainAxisAlignment.start,
+ children: [
+ _buildJoin(),
+ new Text('List of players:'),
+ _playersList(),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/example/lib/gametest/WebSocketsNotifications.dart b/example/lib/gametest/WebSocketsNotifications.dart
new file mode 100644
index 000000000..09b6938d3
--- /dev/null
+++ b/example/lib/gametest/WebSocketsNotifications.dart
@@ -0,0 +1,115 @@
+import 'package:flutter/foundation.dart';
+import 'package:web_socket_channel/io.dart';
+
+///
+/// Application-level global variable to access the WebSockets
+///
+WebSocketsNotifications sockets = new WebSocketsNotifications();
+
+const String _SERVER_ADDRESS = "ws://118.24.162.252:2018/parse";
+
+class WebSocketsNotifications {
+ static final WebSocketsNotifications _sockets =
+ new WebSocketsNotifications._internal();
+ factory WebSocketsNotifications() {
+ return _sockets;
+ }
+
+ WebSocketsNotifications._internal();
+
+ ///
+ /// The WebSocket "open" channel
+ ///
+ IOWebSocketChannel _channel;
+
+ ///
+ /// Is the connection established?
+ ///
+ bool _isOn = false;
+
+ ///
+ /// Listeners
+ /// List of methods to be called when a new message
+ /// comes in.
+ ///
+ ObserverList _listeners = new ObserverList();
+
+ /// ----------------------------------------------------------
+ /// Initialization the WebSockets connection with the server
+ /// ----------------------------------------------------------
+ initCommunication() async {
+ ///
+ /// Just in case, close any previous communication
+ ///
+ reset();
+
+ ///
+ /// Open a new WebSocket communication
+ ///
+ try {
+ _channel = new IOWebSocketChannel.connect(_SERVER_ADDRESS);
+ _isOn = true;
+
+ ///
+ /// Start listening to new notifications / messages
+ ///
+ _channel.stream.listen(_onReceptionOfMessageFromServer,
+ onError: (error, StackTrace stackTrace) {
+ // error handling
+ }, onDone: () {
+ // communication has been closed
+ _isOn = false;
+ });
+ } catch (e) {
+ ///
+ /// General error handling
+ /// TODO
+ ///
+ }
+ }
+
+ /// ----------------------------------------------------------
+ /// Closes the WebSocket communication
+ /// ----------------------------------------------------------
+ reset() {
+ if (_channel != null) {
+ if (_channel.sink != null) {
+ _channel.sink.close();
+ _isOn = false;
+ }
+ }
+ }
+
+ /// ---------------------------------------------------------
+ /// Sends a message to the server
+ /// ---------------------------------------------------------
+ send(String message) {
+ if (_channel != null) {
+ if (_channel.sink != null && _isOn) {
+ _channel.sink.add(message);
+ }
+ }
+ }
+
+ /// ---------------------------------------------------------
+ /// Adds a callback to be invoked in case of incoming
+ /// notification
+ /// ---------------------------------------------------------
+ addListener(Function callback) {
+ _listeners.add(callback);
+ }
+
+ removeListener(Function callback) {
+ _listeners.remove(callback);
+ }
+
+ /// ----------------------------------------------------------
+ /// Callback which is invoked each time that we are receiving
+ /// a message from the server
+ /// ----------------------------------------------------------
+ _onReceptionOfMessageFromServer(message) {
+ _listeners.forEach((Function callback) {
+ callback(message);
+ });
+ }
+}
diff --git a/example/lib/liveQueryBloc.dart b/example/lib/liveQueryBloc.dart
new file mode 100644
index 000000000..bf679b278
--- /dev/null
+++ b/example/lib/liveQueryBloc.dart
@@ -0,0 +1,73 @@
+import 'dart:async';
+
+import 'package:Parse_example/blocProvider.dart';
+import 'package:Parse_example/parseData.dart';
+import 'package:parse_server_sdk/network/parse_livequery.dart';
+import 'package:rxdart/rxdart.dart';
+
+class LiveQueryBloc implements BlocBase {
+ ///
+ /// A stream only meant to return whether THIS movie is part of the parseLives
+ ///
+ final BehaviorSubject _parseLiveStreamController =
+ BehaviorSubject();
+ Stream get outParseLiveStream =>
+ _parseLiveStreamController.stream.asBroadcastStream();
+
+ ///
+ /// Stream of all the parseLives
+ ///
+ final StreamController _parseLiveController =
+ StreamController.broadcast();
+ Stream get outParseLiveStreams => _parseLiveController.stream;
+ Sink get inParseLives => _parseLiveController.sink;
+
+ ///
+ /// Constructor
+ ///
+ LiveQueryBloc({ParseData parseData, LiveQuery liveQuery}) {
+ // liveQuery.connect();
+ print("LiveQueryBloc: $LiveQueryBloc");
+ // Future> s =
+ // (liveQuery.channel as IOWebSocketChannel).stream.toList();
+ // var v = liveQuery.channel.closeCode;
+ // print("channel.closeCode: $v");
+ // if (liveQuery.channel.closeCode == null) liveQuery.connect();
+ var s = liveQuery.channel.stream;
+ _parseLiveStreamController.addStream(s);
+ // var s = Stream.fromFuture(liveQuery.channel);
+ // _isParseLiveController.addStream(s);
+ // _isParseLiveController.stream.asBroadcastStream();
+
+ _parseLiveStreamController.stream.listen((_) {
+ var v = liveQuery.channel.closeCode;
+ print("channel.closeCode: $v");
+ // print("readyState: $liveQuery.c")
+ // if (liveQuery.channel.closeCode != null) liveQuery.connect();
+
+ print("liveQuery.channel: ${liveQuery.channel} \n---> $_");
+ // _isParseLiveController.sink.add(_);
+ // _isParseLiveController.sink.addStream(liveQuery.channel);
+ // print(JsonEncoder().convert(_));
+
+ // Map actionData = JsonDecoder().convert(_);
+ // print(JsonEncoder().convert(actionData));
+ // print(eventCallbacks);
+ // liveQuery.close();
+ });
+
+ //
+ // We are listening to all parseLives
+ //
+ // _parseLiveController.stream
+ // // but, we only consider the one that matches THIS one
+ // .map((list) => list.any((item) => item.id == parseData.id))
+ // // if any, we notify that it is part of the parseLives
+ // .listen((isParseLive) => _isParseLiveController.add(isParseLive));
+ }
+
+ void dispose() {
+ _parseLiveController.close();
+ _parseLiveStreamController.close();
+ }
+}
diff --git a/example/lib/main.dart b/example/lib/main.dart
index 73d25c00d..d2111ff5b 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -1,12 +1,18 @@
import 'dart:async';
+import 'dart:convert';
+import 'package:Parse_example/application_constants.dart';
+import 'package:Parse_example/diet_plan.dart';
+import 'package:Parse_example/myHome.dart';
+import 'package:english_words/english_words.dart';
import 'package:flutter/material.dart';
-import 'package:flutter_plugin_example/application_constants.dart';
-import 'package:flutter_plugin_example/diet_plan.dart';
+import 'package:parse_server_sdk/network/parse_http_client.dart';
+import 'package:parse_server_sdk/network/parse_livequery.dart';
import 'package:parse_server_sdk/objects/parse_object.dart';
import 'package:parse_server_sdk/network/parse_query.dart';
import 'package:parse_server_sdk/objects/parse_user.dart';
import 'package:parse_server_sdk/parse.dart';
+import 'package:web_socket_channel/io.dart';
void main() => runApp(new MyApp());
@@ -15,47 +21,112 @@ class MyApp extends StatefulWidget {
_MyAppState createState() => new _MyAppState();
}
-class _MyAppState extends State {
+class _MyAppState extends State with WidgetsBindingObserver {
@override
void initState() {
- super.initState();
+ WidgetsBinding.instance.addObserver(this);
initParse();
- getAllItems();
- getSingleItem();
- //query();
- //queryByContainedIn();
- initUser();
+ super.initState();
+ // getAllItems();
+ // getSingleItem();
+ // query();
+ // queryByContainedIn();
+ // initUser();
+ // updatePost();
}
+ LiveQuery live;
+ AppLifecycleState _lastLifecycleState;
Future initParse() async {
-
// Initialize parse
- Parse().initialize(
- ApplicationConstants.PARSE_APPLICATION_ID,
+ Parse().initialize(ApplicationConstants.PARSE_APPLICATION_ID,
ApplicationConstants.PARSE_SERVER_URL,
- masterKey: ApplicationConstants.PARSE_MASTER_KEY);
+ masterKey: ApplicationConstants.PARSE_MASTER_KEY,
+ liveQueryUrl: ApplicationConstants.PARSE_LIVE_SERVER_URL);
+ // parse.liveQuery().connect();
+ live = Parse().liveQuery();
+ // live.connect();
+ live.subscribe("post");
+
+ // live.subscribe("post");
+ // live.on('subscribed', updatePost);
+
+ // Parse().liveQuery().on("update", () => print("object updated!!"));
+ }
+
+ @override
+ void didChangeAppLifecycleState(AppLifecycleState state) {
+ if (state == AppLifecycleState.resumed) {
+ // live.subscribe("post");
+ // _timerLink = new Timer(const Duration(milliseconds: 1), () {
+ // _retrieveDynamicLink();
+ // });
+ }
+ print("state: $state");
+ setState(() {
+ _lastLifecycleState = state;
+ });
+ }
+
+ t() {
+ print('subscription opened');
+ }
+
+ updatePost() {
+ DietPlan().get('2pNUgv1CKA').then((response) {
+ if (response.success) {
+ print("response.result: ${response.result}");
+ var dd = response.result as DietPlan;
+ dd.objectId = "2pNUgv1CKA";
+ dd.name = new WordPair.random().asPascalCase;
+ // Map bodyData = {};
+ // bodyData["title"] = userData.emailAddress;
+ // print(ApplicationConstants.APP_NAME +
+ // ":: ${DietPlan.DIET_PLAN}---${(response.result as DietPlan).objectId}--- " +
+ // (response.result as DietPlan).toString());
+ print("dd: $dd");
+ var m = new Map();
+ m.putIfAbsent("title", () => dd.name);
+ dd.createObjectData(m);
+ // return dd;
+ dd.save();
+ } else {
+ print(
+ ApplicationConstants.APP_NAME + ": " + response.exception.message);
+ }
+ });
}
void getAllItems() {
DietPlan().getAll().then((response) {
- if (response.success){
-
+ if (response.success) {
for (var plan in response.result) {
- print(ApplicationConstants.APP_NAME + ": " + (plan as DietPlan).name);
+ if ((plan as DietPlan).name != null) {
+ print(ApplicationConstants.APP_NAME +
+ ": ${DietPlan.DIET_PLAN}------ " +
+ (plan as DietPlan).name);
+ } else {
+ print(ApplicationConstants.APP_NAME +
+ ": ${DietPlan.DIET_PLAN}---${(plan as DietPlan).objectId}--- null");
+ }
}
-
} else {
- print(ApplicationConstants.APP_NAME + ": " + response.exception.message);
+ print(
+ ApplicationConstants.APP_NAME + ": " + response.exception.message);
}
});
}
void getSingleItem() {
- DietPlan().get('R5EonpUDWy').then((response) {
- if (response.success){
- print(ApplicationConstants.APP_NAME + ": " + (response.result as DietPlan).toString());
+ DietPlan().get('2pNUgv1CKA').then((response) {
+ if (response.success) {
+ print("response.result: ${response.result}");
+ print(ApplicationConstants.APP_NAME +
+ ":: ${DietPlan.DIET_PLAN}---${(response.result as DietPlan).objectId}--- " +
+ (response.result as DietPlan).toString());
} else {
- print(ApplicationConstants.APP_NAME + ": " + response.exception.message);
+ print(
+ ApplicationConstants.APP_NAME + ": " + response.exception.message);
}
});
}
@@ -65,13 +136,18 @@ class _MyAppState extends State {
QueryBuilder()
..object = DietPlan()
..field = DietPlan.NAME
- ..equals = ['Paleo']
- ..query().then((response){
-
- if (response.success){
- print(ApplicationConstants.APP_NAME + ": " + ((response.result as List)[0] as DietPlan).toString());
+ ..equals = ['fff444']
+ ..query().then((response) {
+ if (response.success) {
+ print(ApplicationConstants.APP_NAME +
+ "::: ${DietPlan.DIET_PLAN}---${((response.result as List)[0] as DietPlan).name}--- " +
+ ((response.result as List)[0] as DietPlan)
+ .objectId
+ .toString());
} else {
- print(ApplicationConstants.APP_NAME + ": " + response.exception.message);
+ print(ApplicationConstants.APP_NAME +
+ ": " +
+ response.exception.message);
}
});
}
@@ -81,36 +157,53 @@ class _MyAppState extends State {
QueryBuilder()
..object = DietPlan()
..field = DietPlan.NAME
- ..contains = ['Diet']
- ..query().then((response){
-
- if (response.success){
- print(ApplicationConstants.APP_NAME + ": " + ((response.result as List)[0] as DietPlan).toString());
+ ..containedIn = ['dfg']
+ ..query().then((response) {
+ if (response.success) {
+ print("queryByContainedIn-result: ${response.result}");
+ print(ApplicationConstants.APP_NAME +
+ ": " +
+ ((response.result as List).length > 0
+ ? ((response.result as List)[0] as DietPlan)
+ .toString()
+ : ""));
} else {
- print(ApplicationConstants.APP_NAME + ": " + response.exception.message);
+ print(ApplicationConstants.APP_NAME +
+ ": " +
+ response.exception.message);
}
});
}
Future initUser() async {
- User().createNewUser("TestFlutter", "TestPassword123", "TestEmail@Email.com");
+ User()
+ .createNewUser("TestFlutter", "TestPassword123", "TestEmail@Email.com");
+ User().signUp();
User().login().then((val) {
- print(val);
+ print("val: $val");
});
}
@override
Widget build(BuildContext context) {
+ final title = 'WebSocket Demo';
return new MaterialApp(
- home: new Scaffold(
- appBar: new AppBar(
- title: const Text('Plugin example app'),
- ),
- body: new Center(
- child: new Text('Running Parse init'),
- ),
+ title: title,
+ // home: new StartPage(),
+ home: new MyHomePage(
+ title: title,
+ channel: live.channel,
+ // new IOWebSocketChannel.connect("ws://118.24.162.252:2018/parse"),
+ liveQuery: live,
+ f: updatePost,
),
);
}
+
+ @override
+ void dispose() {
+ live.channel.sink.close();
+ super.dispose();
+ }
}
diff --git a/example/lib/myHome.dart b/example/lib/myHome.dart
new file mode 100644
index 000000000..2773d5c3e
--- /dev/null
+++ b/example/lib/myHome.dart
@@ -0,0 +1,128 @@
+import 'dart:async';
+
+import 'package:Parse_example/blocProvider.dart';
+import 'package:Parse_example/liveQueryBloc.dart';
+import 'package:flutter/material.dart';
+import 'package:parse_server_sdk/network/parse_livequery.dart';
+import 'package:parse_server_sdk/objects/parse_object.dart';
+import 'package:parse_server_sdk/parse.dart';
+import 'package:web_socket_channel/web_socket_channel.dart';
+
+class MyHomePage extends StatefulWidget {
+ final String title;
+ WebSocketChannel channel;
+ LiveQuery liveQuery;
+ final Function f;
+ MyHomePage(
+ {Key key,
+ @required this.title,
+ @required this.channel,
+ this.liveQuery,
+ this.f})
+ : super(key: key) {
+ // this.liveQuery.subscribe("post");
+ // this.channel = liveQuery.channel as WebSocketChannel;
+ }
+
+ @override
+ _MyHomePageState createState() => new _MyHomePageState();
+}
+
+class _MyHomePageState extends State {
+ TextEditingController _controller = new TextEditingController();
+ LiveQueryBloc bloc;
+
+ var info;
+
+ @override
+ void initState() {
+ bloc = new LiveQueryBloc(liveQuery: widget.liveQuery);
+ info = "";
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ // final IncrementBloc bloc = BlocProvider.of(context);
+ // final LiveQueryBloc bloc = BlocProvider.of(context);
+ return new Scaffold(
+ appBar: new AppBar(
+ title: new Text(widget.title),
+ ),
+ body: new Padding(
+ padding: const EdgeInsets.all(20.0),
+ child: new Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ new Form(
+ child: new TextFormField(
+ controller: _controller,
+ decoration: new InputDecoration(labelText: 'Send a message'),
+ ),
+ ),
+ new StreamBuilder(
+ stream: bloc.outParseLiveStream,
+ builder: (context, snapshot) {
+ return new Padding(
+ padding: const EdgeInsets.symmetric(vertical: 24.0),
+ child: new Text(snapshot.hasData ? '${snapshot.data}' : ''),
+ );
+ },
+ ),
+ new Text(widget.liveQuery.channel.closeCode.toString()),
+ new Text(info.toString())
+ ],
+ ),
+ ),
+ floatingActionButton: new FloatingActionButton(
+ onPressed: _sendMessage,
+ tooltip: 'Send message',
+ child: new Icon(Icons.send),
+ ), // This trailing comma makes auto-formatting nicer for build methods.
+ );
+ }
+
+ void _sendMessage() {
+ // if (_controller.text.isNotEmpty) {
+ widget.f();
+
+ // widget.liveQuery.channel.closeCode != null &&
+ // if (widget.liveQuery.channel.closeCode == 1002) {
+ // widget.liveQuery = Parse().liveQuery();
+ // widget.liveQuery.subscribe("post");
+ // bloc = new LiveQueryBloc(liveQuery: widget.liveQuery);
+ // }
+
+ // widget.liveQuery.close();
+
+ // widget.liveQuery.subscribe("post", widget.channel, widget.f);
+
+ // widget.channel.sink.add(_controller.text);
+ // widget.channel.sink.add(widget.function());
+ // }
+ }
+
+ @override
+ void didUpdateWidget(MyHomePage oldWidget) {
+ // TODO: implement didUpdateWidget
+
+ setState(() {
+ info =
+ "closeCode: ${widget.liveQuery.channel.closeCode}--${DateTime.now().toString()}";
+ });
+ if (widget.liveQuery.channel.closeCode == 1002) {
+ widget.liveQuery = Parse().liveQuery();
+ widget.liveQuery.subscribe("post");
+ bloc = new LiveQueryBloc(liveQuery: widget.liveQuery);
+ }
+ super.didUpdateWidget(oldWidget);
+ }
+
+ @override
+ void dispose() {
+ // widget.channel.sink.close();
+ // liveQuery.dispose();
+ bloc.dispose();
+ super.dispose();
+ }
+}
diff --git a/example/lib/parseData.dart b/example/lib/parseData.dart
new file mode 100644
index 000000000..2a48822ff
--- /dev/null
+++ b/example/lib/parseData.dart
@@ -0,0 +1,23 @@
+class ParseData extends Object {
+ final int id;
+ final voteAverage;
+ final String title;
+ // final String posterPath;
+ // final String overview;
+
+ ParseData(this.id, this.voteAverage, this.title);
+
+ ParseData.fromJSON(Map json)
+ : id = json['objectId'],
+ voteAverage = json['data'],
+ title = json['title'];
+ // posterPath = json['poster_path'],
+ // overview = json['overview'];
+
+ @override
+ bool operator ==(dynamic other) =>
+ identical(this, other) || this.id == other.id;
+
+ @override
+ int get hashCode => id;
+}
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index 152aa9e24..95d1e5eb6 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -1,4 +1,4 @@
-name: flutter_plugin_example
+name: Parse_example
description: Demonstrates how to use the flutter_plugin plugin.
dependencies:
@@ -7,10 +7,11 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
- cupertino_icons: ^0.1.2
+ english_words: ^3.1.4
+ rxdart: ^0.19.0
+ # cupertino_icons: ^0.1.2
dev_dependencies:
-
parse_server_sdk:
path: ../
@@ -19,23 +20,18 @@ dev_dependencies:
# The following section is specific to Flutter.
flutter:
-
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
-
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
-
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.io/assets-and-images/#resolution-aware.
-
# For details regarding adding assets from package dependencies, see
# https://flutter.io/assets-and-images/#from-packages
-
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
diff --git a/lib/network/parse_http_client.dart b/lib/network/parse_http_client.dart
index d5427ea75..96c0f2e48 100644
--- a/lib/network/parse_http_client.dart
+++ b/lib/network/parse_http_client.dart
@@ -18,6 +18,7 @@ class ParseHTTPClient extends http.BaseClient {
request.headers['Content-Type'] = 'application/json';
if (data.masterKey != null)
request.headers['X-Parse-Master-Key'] = data.masterKey;
+ print("request: $request");
return _client.send(request);
}
}
diff --git a/lib/network/parse_livequery.dart b/lib/network/parse_livequery.dart
index 99219ae33..03bce017c 100644
--- a/lib/network/parse_livequery.dart
+++ b/lib/network/parse_livequery.dart
@@ -1,11 +1,14 @@
+import 'dart:async';
import "dart:convert";
import 'package:web_socket_channel/io.dart';
import 'package:parse_server_sdk/network/parse_http_client.dart';
import 'dart:io';
+import 'package:web_socket_channel/web_socket_channel.dart';
+import 'package:web_socket_channel/status.dart' as status;
class LiveQuery {
final ParseHTTPClient client;
- var channel;
+ IOWebSocketChannel channel;
Map connectMessage;
Map subscribeMessage;
Map eventCallbacks = {};
@@ -14,30 +17,112 @@ class LiveQuery {
connectMessage = {
"op": "connect",
"applicationId": client.data.applicationId,
+ "masterKey": client.data.masterKey
};
subscribeMessage = {
"op": "subscribe",
"requestId": 1,
"query": {
- "className": null,
+ "className": "post",
"where": {},
}
};
+ // channel = new IOWebSocketChannel.connect(client.data.liveQueryURL);
+ // _parseController = StreamController.broadcast();
+ // _parseController.stream = channel.stream;
+ // connect();
+ }
+
+ connect() {
+ channel = new IOWebSocketChannel.connect(client.data.liveQueryURL);
}
subscribe(String className) async {
+ connect();
+ // var v = channel.closeCode;
+ // print("channel.closeCode: $v");
+ // if (channel.closeCode == null) connect();
+
+ // if (channel == null)
+ // channel = new IOWebSocketChannel.connect(client.data.liveQueryURL);
+ // channel = new IOWebSocketChannel.connect(client.data.liveQueryURL);
+ // channel.sink.add(JsonEncoder().convert(connectMessage));
+ // subscribeMessage['query']['className'] = className.toString();
+ // channel.sink.add(JsonEncoder().convert(subscribeMessage));
+ // channel.close(status.goingAway);
+ // channel.stream.listen((message) {
+ // // handling of the incoming messages
+ // }, onError: (error, StackTrace stackTrace) {
+ // // error handling
+ // }, onDone: () {
+ // // communication has been closed
+ // });
+
+ // ignore: close_sinks
+ // var webSocket = await WebSocket.connect(client.data.liveQueryURL);
+ channel.sink.add(JsonEncoder().convert(connectMessage));
+ print(JsonEncoder().convert(connectMessage));
+ // subscribeMessage['query']['className'] = className;
+ channel.sink.add(JsonEncoder().convert(subscribeMessage));
+ print(JsonEncoder().convert(subscribeMessage));
+ // channel.sink.add(JsonEncoder().convert(parseObject));
+
+ // channel.stream.listen((message) {
+ // print(JsonEncoder().convert(message));
+
+ // Map actionData = JsonDecoder().convert(message);
+ // print(JsonEncoder().convert(actionData));
+ // print(eventCallbacks);
+ // // if (eventCallbacks.containsKey(actionData['op']))
+ // // eventCallbacks[actionData['op']](actionData);
+ // });
+ // close();
+ }
+
+ subscribe2(String className, channel) async {
+ channel.sink.add("received!");
// ignore: close_sinks
- var webSocket = await WebSocket.connect(client.data.liveQueryURL);
- channel = new IOWebSocketChannel(webSocket);
channel.sink.add(JsonEncoder().convert(connectMessage));
- subscribeMessage['query']['className'] = className;
+ print(JsonEncoder().convert(connectMessage));
+ // subscribeMessage['query']['className'] = className;
channel.sink.add(JsonEncoder().convert(subscribeMessage));
- channel.stream.listen((message) {
- Map actionData = JsonDecoder().convert(message);
- if (eventCallbacks.containsKey(actionData['op']))
- eventCallbacks[actionData['op']](actionData);
- });
+ print(JsonEncoder().convert(subscribeMessage));
+ // channel.sink.add(JsonEncoder().convert(parseObject));
+
+ // var po = {
+ // "op": "subscribe",
+ // "requestId": 1,
+ // "query": {
+ // "className": "post",
+ // "where": {"objectId": "2pNUgv1CKA"},
+ // "fields": ["title"] // Optional
+ // },
+ // };
+
+ // channel.sink.add(JsonEncoder().convert(po));
+ // channel.sink.add(JsonEncoder().convert(function()));
+ // function();
+ // var po1 = {
+ // "op": "update",
+ // "requestId": 1,
+ // "object": {
+ // "className": "post",
+ // "objectId": "2pNUgv1CKA",
+ // "title": "",
+ // }
+ // };
+ // channel.sink.add(JsonEncoder().convert(po1));
+
+ // channel.stream.listen((message) {
+ // print(JsonEncoder().convert(message));
+
+ // Map actionData = JsonDecoder().convert(message);
+ // print(JsonEncoder().convert(actionData));
+ // print(eventCallbacks);
+ // if (eventCallbacks.containsKey(actionData['op']))
+ // eventCallbacks[actionData['op']](actionData);
+ // });
}
void on(String op, Function callback) {
@@ -45,6 +130,57 @@ class LiveQuery {
}
void close() {
- channel.close();
+ channel.sink.close(status.goingAway);
}
+
+// other websoket
+ // static WebSocket _webSocket1;
+ // static num _id = 0;
+
+ // void connect() {
+ // closeSocket();
+ // Future futureWebSocket = WebSocket.connect(
+ // client.data.liveQueryURL); // Api.WS_URL 为服务器端的 websocket 服务
+ // futureWebSocket.then((WebSocket ws) {
+ // _webSocket1 = ws;
+ // _webSocket1.readyState;
+ // // 监听事件
+ // void onData(dynamic content) {
+ // _id++;
+ // _sendMessage("收到");
+ // _createNotification("新消息", content + _id.toString());
+ // }
+
+ // _webSocket1.listen(onData,
+ // onError: (a) => print("error"), onDone: () => print("done"));
+ // });
+ // }
+
+ // static void closeSocket() {
+ // if (_webSocket1 != null) _webSocket1.close();
+ // }
+
+ // // 向服务器发送消息
+ // static void _sendMessage(String message) {
+ // _webSocket1.add(message);
+ // }
+
+ // // 手机状态栏弹出推送的消息
+ // static void _createNotification(String title, String content) async {
+ // print("content: $content");
+ // // await LocalNotifications.createNotification(
+ // // id: _id,
+ // // title: title,
+ // // content: content,
+ // // onNotificationClick: NotificationAction(
+ // // actionText: "some action",
+ // // callback: _onNotificationClick,
+ // // payload: "接收成功!"),
+ // // );
+ // }
+
+ // static _onNotificationClick(String payload) {
+ // // LocalNotifications.removeNotification(_id);
+ // _sendMessage("消息已被阅读");
+ // }
}
diff --git a/lib/network/parse_query.dart b/lib/network/parse_query.dart
index daacc7126..f968897c5 100644
--- a/lib/network/parse_query.dart
+++ b/lib/network/parse_query.dart
@@ -8,7 +8,7 @@ import 'package:parse_server_sdk/objects/parse_response.dart';
class QueryBuilder implements ParseBaseObject {
// BaseParams
- String _className;
+ // String _className;
ParseObject object;
final ParseHTTPClient client = ParseHTTPClient();
String path;
@@ -72,12 +72,24 @@ class QueryBuilder implements ParseBaseObject {
existsMap =
_runThroughQueryParamsWithSearchTerms(contains, "term", field);
- //String query = r"""where={"Name":{"$text":{"$search":{"$term":"Diet"}}}}""";
- String query = "where=${JsonEncoder().convert(existsMap)}";
+ print("existsMap: $existsMap");
+
+ // String query = r"""where={"Name":{"$text":{"$search":{"$term":"gg"}}}}""";
+ // String s1 = existsMap.toString();
+ // print("s1: $s1");
+ String query = "where=${JsonEncoder().convert(existsMap.toString())}";
+ // String s = r'{"title":{"$text":{"$search":{"$term":"gg"}}}}';
+ // String query = "where=${JsonEncoder().convert(s)}";
+
+ // query: where="{\"title\":{\"$text\":{\"$search\":{\"$term\":\"gg\"}}}}"
+ // where : {\"title\":{\"$text\":{\"$search\":{\"$term\":\"gg\"}}}}
+ // existsMap: {title: {"text":"{\"search\":\"{\\\"$term\\\":\\\"gg\\\"}\"}"}}
+ // existsMap: {title: {"$text":"{\"$search\":\"{\\\"$term\\\":\\\"gg\\\"}\"}"}}
+ // request: GET http://118.24.162.252:2018/parse/classes/post?where=%22%7B%5C%22title%5C%22:%7B%5C%22$text%5C%22:%7B%5C%22$search%5C%22:%7B%5C%22$term%5C%22:%5C%22gg%5C%22%7D%7D%7D%7D%22
if (limit != 0) query += '?limit=$limit';
if (skip != 0) query += '?skip=$skip';
-
+ print("query: $query");
return query;
}
@@ -88,18 +100,18 @@ class QueryBuilder implements ParseBaseObject {
if (list.isNotEmpty) {
if (list.length == 1) {
- params = list[0];
+ params = '"${list[0]}"';
} else {
for (var listItem in list) {
- params += "$listItem, ";
+ params += '"$listItem", ';
}
params.substring(0, params.length - 2);
}
}
- mapToReturn[queryParam] = params;
-
+ mapToReturn['"$queryParam"'] = params;
+ print(mapToReturn);
return mapToReturn;
}
@@ -107,14 +119,16 @@ class QueryBuilder implements ParseBaseObject {
List list, String queryParam, String fieldName) {
Map mapToReturn = Map();
Map mapWithParamData = Map();
-
+ List s = new List();
for (var item in list) {
- mapWithParamData["\$$queryParam"] = item;
+ // mapWithParamData['"\$$queryParam"'] = '"$item"';
+ s.add('"$item"');
}
+ mapWithParamData['"\$$queryParam"'] = s;
- var params = JsonEncoder().convert(mapWithParamData).toString();
-
- mapToReturn[fieldName] = params;
+ // var params = JsonEncoder().convert(mapWithParamData).toString();
+ // mapToReturn[fieldName] = params;
+ mapToReturn['"$fieldName"'] = mapWithParamData.toString();
return mapToReturn;
}
@@ -127,17 +141,26 @@ class QueryBuilder implements ParseBaseObject {
Map searchEntry = Map();
for (var item in list) {
- mapWithParamData["\$$queryParam"] = item;
+ mapWithParamData['"\$$queryParam"'] = '"$item"';
}
- var jsonMapWithParamData = JsonEncoder().convert(mapWithParamData);
- searchEntry['search'] = jsonMapWithParamData;
+ // var jsonMapWithParamData = JsonEncoder().convert(mapWithParamData);
+ // searchEntry['\$search'] = jsonMapWithParamData;
+ searchEntry['"\$search"'] = mapWithParamData.toString();
+
+ // var jsonSearchEntry = JsonEncoder().convert(searchEntry);
+ // textEntry['\$text'] = jsonSearchEntry;
+ textEntry['"\$text"'] = searchEntry.toString();
+
+ // var params = JsonEncoder().convert(textEntry);
+ // mapToReturn['$fieldName'] = params;
- var jsonSearchEntry = JsonEncoder().convert(searchEntry);
- textEntry['text'] = jsonSearchEntry;
+ // textEntry[fieldName] = textEntry.toString();
+ // print(textEntry.toString());
+ mapToReturn['"$fieldName"'] = textEntry.toString();
+ // print(mapToReturn.toString());
- var params = JsonEncoder().convert(textEntry).toString();
- mapToReturn[fieldName] = params;
+ // mapToReturn = textEntry;
return mapToReturn;
}
diff --git a/lib/objects/parse_object.dart b/lib/objects/parse_object.dart
index 5c39cd38f..e07b3a47e 100644
--- a/lib/objects/parse_object.dart
+++ b/lib/objects/parse_object.dart
@@ -19,8 +19,16 @@ class ParseObject implements ParseBaseObject {
path = "/classes/$className";
}
+ createObjectData(Map objectData) {
+ // ParseDataUser.init(username, password, emailAddress);
+ this.objectData = objectData;
+ // return this;
+ }
+
Future create([Map objectInitialData]) async {
- objectData = {}..addAll(objectData)..addAll(objectInitialData);
+ if (objectInitialData != null) {
+ objectData = {}..addAll(objectData)..addAll(objectInitialData);
+ }
final response = this._client.post("${_client.data.serverUrl}$path",
body: JsonEncoder().convert(objectData));
@@ -30,14 +38,30 @@ class ParseObject implements ParseBaseObject {
}
Future save([Map objectInitialData]) {
- objectData = {}..addAll(objectData)..addAll(objectInitialData);
+ if (objectInitialData != null) {
+ objectData = {}..addAll(objectData)..addAll(objectInitialData);
+ }
if (objectId == null) {
return create(objectData);
} else {
- final response = this._client.put(
- _client.data.serverUrl + "$path/$objectId",
- body: JsonEncoder().convert(objectData));
+ // Map bodyData = {};
+ // bodyData["email"] = objectData.name;
+ // bodyData["password"] = objectData.password;
+
+ Uri tempUri = Uri.parse(_client.data.serverUrl);
+
+ Uri url = Uri(
+ scheme: tempUri.scheme,
+ host: tempUri.host,
+ port: tempUri.port,
+ path: "${tempUri.path}$path/$objectId");
+
+ print("put url: $url");
+ print("objectData: $objectData");
+ final response =
+ this._client.put(url, body: JsonEncoder().convert(objectData));
return response.then((value) {
+ print("value: ${value.body}");
return ParseResponse.handleResponse(this, value);
});
}
@@ -55,8 +79,9 @@ class ParseObject implements ParseBaseObject {
String uri = _client.data.serverUrl + "$path";
if (objectId != null) uri += "/$objectId";
-
+ print("uri: $uri");
return this._client.get(uri).then((value) {
+ print("value: $value");
return ParseResponse.handleResponse(this, value);
});
}
diff --git a/lib/objects/parse_response.dart b/lib/objects/parse_response.dart
index 5b933346e..b65392724 100644
--- a/lib/objects/parse_response.dart
+++ b/lib/objects/parse_response.dart
@@ -12,7 +12,8 @@ class ParseResponse {
dynamic result;
ParseException exception;
- static Future _handleSuccess(ParseResponse response, ParseObject object, String responseBody) async {
+ static Future _handleSuccess(
+ ParseResponse response, ParseObject object, String responseBody) async {
response.success = true;
var map = JsonDecoder().convert(responseBody) as Map;
@@ -29,16 +30,20 @@ class ParseResponse {
}
static ParseResponse _checkForEmptyResult(ParseResponse response) {
- if (response.result == null || (response.result as List).length == 0) {
- response.exception = ParseException();
- response.exception.message = "No result found for query";
- response.success = false;
+ // ||
+ // (response.result as List).length == 0 ||
+ // (response.result as ParseObject) == null
+ if (response.result == null) {
+ response.exception = ParseException();
+ response.exception.message = "No result found for query";
+ response.success = false;
}
return response;
}
- static List _handleMultipleResults(ParseObject object, dynamic map) {
+ static List _handleMultipleResults(
+ ParseObject object, dynamic map) {
var resultsList = List();
for (var value in map) {
@@ -60,7 +65,8 @@ class ParseResponse {
return response;
}
- static Future handleResponse(ParseObject object, Response value) async {
+ static Future handleResponse(
+ ParseObject object, Response value) async {
var response = ParseResponse();
if (value != null) {
diff --git a/lib/objects/parse_user.dart b/lib/objects/parse_user.dart
index 61074f158..525ce89f6 100644
--- a/lib/objects/parse_user.dart
+++ b/lib/objects/parse_user.dart
@@ -9,6 +9,7 @@ import 'package:parse_server_sdk/network/parse_http_client.dart';
class User implements ParseBaseObject {
final String className = '_User';
final ParseHTTPClient client = ParseHTTPClient();
+
String path = "/classes/_User";
Map objectData = {};
@@ -52,6 +53,7 @@ class User implements ParseBaseObject {
}
Map _handleResponse(String response) {
+ print("response: $response");
Map responseData = JsonDecoder().convert(response);
if (responseData.containsKey('objectId')) {
objectData = responseData;
@@ -65,14 +67,15 @@ class User implements ParseBaseObject {
if (sessionId != null) objectData.remove('sessionToken');
}
- Future