diff --git a/ReactNativeClient/package.json b/ReactNativeClient/package.json
index 30df70057f..d0ac058354 100644
--- a/ReactNativeClient/package.json
+++ b/ReactNativeClient/package.json
@@ -12,6 +12,7 @@
"react-native-action-button": "^2.6.9",
"react-native-checkbox": "^1.1.0",
"react-native-popup-menu": "^0.7.4",
+ "react-native-side-menu": "^0.20.1",
"react-native-vector-icons": "^2.0.3",
"react-navigation": "^1.0.0-beta.9",
"uuid": "^3.0.1"
diff --git a/ReactNativeClient/src/components/screens/folder.js b/ReactNativeClient/src/components/screens/folder.js
index ac12ab91f6..b25e1d5e58 100644
--- a/ReactNativeClient/src/components/screens/folder.js
+++ b/ReactNativeClient/src/components/screens/folder.js
@@ -64,7 +64,6 @@ const FolderScreen = connect(
(state) => {
return {
folderId: state.selectedFolderId,
- //folder: state.selectedFolderId ? Folder.byId(state.folders, state.selectedFolderId) : Folder.newFolder(),
};
}
)(FolderScreenComponent)
diff --git a/ReactNativeClient/src/menu.js b/ReactNativeClient/src/menu.js
new file mode 100644
index 0000000000..82f8c742b0
--- /dev/null
+++ b/ReactNativeClient/src/menu.js
@@ -0,0 +1,101 @@
+const React = require('react');
+const {
+ Dimensions,
+ StyleSheet,
+ ScrollView,
+ View,
+ Image,
+ Text,
+} = require('react-native');
+const { Component } = React;
+
+const window = Dimensions.get('window');
+const uri = 'https://pickaface.net/gallery/avatar/Opi51c74d0125fd4.png';
+
+const styles = StyleSheet.create({
+ menu: {
+ flex: 1,
+ width: window.width,
+ height: window.height,
+ backgroundColor: 'gray',
+ padding: 20,
+ },
+ avatarContainer: {
+ marginBottom: 20,
+ marginTop: 20,
+ },
+ avatar: {
+ width: 48,
+ height: 48,
+ borderRadius: 24,
+ flex: 1,
+ },
+ name: {
+ position: 'absolute',
+ left: 70,
+ top: 20,
+ },
+ item: {
+ fontSize: 14,
+ fontWeight: '300',
+ paddingTop: 5,
+ },
+});
+
+module.exports = class Menu extends Component {
+ static propTypes = {
+ onItemSelected: React.PropTypes.func.isRequired,
+ };
+
+ render() {
+ return (
+
+
+
+ Your name
+
+
+ this.props.onItemSelected('About')}
+ style={styles.item}>
+ About
+
+
+ this.props.onItemSelected('Contacts')}
+ style={styles.item}>
+ Contacts
+
+
+ Contacts
+ Contacts
+ Contacts
+ Contacts
+ Contacts
+ Contacts
+ Contacts
+ Contacts
+ Contacts
+ Contacts
+ Contacts
+ Contacts
+ Contacts
+ Contacts
+ Contacts
+ Contacts
+ Contacts
+ Contacts
+ Contacts
+ Contacts
+ Contacts
+ Contacts
+ Contacts
+ Contacts
+ Contacts
+ ContactsLL
+
+ );
+ }
+};
\ No newline at end of file
diff --git a/ReactNativeClient/src/root.js b/ReactNativeClient/src/root.js
index f1275b1eff..01b33882f6 100644
--- a/ReactNativeClient/src/root.js
+++ b/ReactNativeClient/src/root.js
@@ -159,6 +159,11 @@ const AppNavigator = StackNavigator({
Login: {screen: LoginScreen},
});
+const SideMenu = require('react-native-side-menu');
+
+import Menu from 'src/menu.js';
+
+
class AppComponent extends React.Component {
componentDidMount() {
@@ -207,13 +212,17 @@ class AppComponent extends React.Component {
}
render() {
+ const menu =
;
+
return (
-
-
-
+
+
+
+
+
);
}
}
diff --git a/ReactNativeClient/src/synchronizer.js b/ReactNativeClient/src/synchronizer.js
index 4a3c295dcd..c5ccffdf72 100644
--- a/ReactNativeClient/src/synchronizer.js
+++ b/ReactNativeClient/src/synchronizer.js
@@ -131,12 +131,20 @@ class Synchronizer {
return p.then(() => {
processedChangeIds = processedChangeIds.concat(c.ids);
}).catch((error) => {
- Log.warn('Failed applying changes', c.ids);
+ Log.warn('Failed applying changes', c.ids, error.message, error.type);
+ // This is fine - trying to apply changes to an object that has been deleted
+ if (error.type == 'NotFoundException') {
+ processedChangeIds = processedChangeIds.concat(c.ids);
+ } else {
+ throw error;
+ }
});
});
}
- return promiseChain(chain).then(() => {
+ return promiseChain(chain).catch((error) => {
+ Log.warn('Synchronization was interrupted due to an error:', error);
+ }).then(() => {
Log.info('IDs to delete: ', processedChangeIds);
Change.deleteMultiple(processedChangeIds);
});
diff --git a/ReactNativeClient/src/web-api.js b/ReactNativeClient/src/web-api.js
index 0de2461299..c49ed396c7 100644
--- a/ReactNativeClient/src/web-api.js
+++ b/ReactNativeClient/src/web-api.js
@@ -1,6 +1,21 @@
import { Log } from 'src/log.js';
import { stringify } from 'query-string';
+class WebApiError extends Error {
+
+ constructor(msg) {
+ let type = 'WebApiError';
+ // Create a regular JS Error object from a web api error response { error: "something", type: "NotFoundException" }
+ if (typeof msg === 'object' && msg !== null) {
+ if (msg.type) type = msg.type;
+ msg = msg.error ? msg.error : 'error';
+ }
+ super(msg);
+ this.type = type;
+ }
+
+}
+
class WebApi {
constructor(baseUrl) {
@@ -73,7 +88,7 @@ class WebApi {
let responseClone = response.clone();
return response.json().then(function(data) {
if (data && data.error) {
- reject(new Error(data.error));
+ reject(new WebApiError(data));
} else {
resolve(data);
}
diff --git a/src/AppBundle/Controller/FoldersController.php b/src/AppBundle/Controller/FoldersController.php
index 94fe347ba4..6ae4ec5895 100755
--- a/src/AppBundle/Controller/FoldersController.php
+++ b/src/AppBundle/Controller/FoldersController.php
@@ -7,10 +7,8 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use AppBundle\Controller\ApiController;
use AppBundle\Model\Folder;
-
-
-
-use AppBundle\Model\BaseItem;
+use AppBundle\Exception\NotFoundException;
+use AppBundle\Exception\MethodNotAllowedException;
class FoldersController extends ApiController {
@@ -30,7 +28,7 @@ class FoldersController extends ApiController {
return static::successResponse($folder);
}
- return static::errorResponse('Invalid method');
+ throw new MethodNotAllowedException();
}
/**
@@ -38,7 +36,7 @@ class FoldersController extends ApiController {
*/
public function oneAction($id, Request $request) {
$folder = Folder::byId(Folder::unhex($id));
- if (!$folder && !$request->isMethod('PUT')) return static::errorResponse('Not found', 0, 404);
+ if (!$folder && !$request->isMethod('PUT')) throw new NotFoundException();
if ($request->isMethod('GET')) {
return static::successResponse($folder);
@@ -68,7 +66,7 @@ class FoldersController extends ApiController {
return static::successResponse(array('id' => $id));
}
- return static::errorResponse('Invalid method');
+ throw new MethodNotAllowedException();
}
/**
@@ -76,7 +74,7 @@ class FoldersController extends ApiController {
*/
public function linkAction($id, Request $request) {
$folder = Folder::byId(Folder::unhex($id));
- if (!$folder) return static::errorResponse('Not found', 0, 404);
+ if (!$folder) throw new NotFoundException();
if ($request->isMethod('GET')) {
return static::successResponse($folder->notes());
@@ -91,7 +89,7 @@ class FoldersController extends ApiController {
return static::successResponse();
}
- return static::errorResponse('Invalid method');
+ throw new MethodNotAllowedException();
}
}
diff --git a/src/AppBundle/Controller/NotesController.php b/src/AppBundle/Controller/NotesController.php
index 834418ecfc..da7ef97792 100755
--- a/src/AppBundle/Controller/NotesController.php
+++ b/src/AppBundle/Controller/NotesController.php
@@ -7,6 +7,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use AppBundle\Controller\ApiController;
use AppBundle\Model\Note;
+use AppBundle\Exception\NotFoundException;
class NotesController extends ApiController {
@@ -30,7 +31,7 @@ class NotesController extends ApiController {
*/
public function oneAction($id, Request $request) {
$note = Note::find(Note::unhex($id));
- if (!$note && !$request->isMethod('PUT')) return static::errorResponse('Not found', 0, 404);
+ if (!$note && !$request->isMethod('PUT')) throw new NotFoundException();
if ($request->isMethod('GET')) {
return static::successResponse($note);
diff --git a/src/AppBundle/Model/Folder.php b/src/AppBundle/Model/Folder.php
index 6db57ae6d3..d560c7ccd4 100755
--- a/src/AppBundle/Model/Folder.php
+++ b/src/AppBundle/Model/Folder.php
@@ -30,4 +30,18 @@ class Folder extends BaseItem {
return Note::where('parent_id', '=', $this->id)->get();
}
+ static public function countByOwnerId($ownerId) {
+ return Folder::where('owner_id', '=', $ownerId)->count();
+ }
+
+ public function delete() {
+ if (self::countByOwnerId($this->owner_id) <= 1) throw new \Exception('Cannot delete the last folder');
+
+ $notes = $this->notes();
+ foreach ($notes as $note) {
+ $note->delete();
+ }
+ return parent::delete();
+ }
+
}
diff --git a/start_emulator.bat b/start_emulator.bat
new file mode 100644
index 0000000000..775fb7e433
--- /dev/null
+++ b/start_emulator.bat
@@ -0,0 +1,3 @@
+c:
+C:\Users\Laurent\AppData\Local\Android\sdk\tools
+emulator.exe -avd Nexus_5X_API_23_Google_API_
\ No newline at end of file