mirror of https://github.com/laurent22/joplin.git
Side menu and sync fixes
parent
c01d850b2d
commit
3078797134
|
@ -12,6 +12,7 @@
|
||||||
"react-native-action-button": "^2.6.9",
|
"react-native-action-button": "^2.6.9",
|
||||||
"react-native-checkbox": "^1.1.0",
|
"react-native-checkbox": "^1.1.0",
|
||||||
"react-native-popup-menu": "^0.7.4",
|
"react-native-popup-menu": "^0.7.4",
|
||||||
|
"react-native-side-menu": "^0.20.1",
|
||||||
"react-native-vector-icons": "^2.0.3",
|
"react-native-vector-icons": "^2.0.3",
|
||||||
"react-navigation": "^1.0.0-beta.9",
|
"react-navigation": "^1.0.0-beta.9",
|
||||||
"uuid": "^3.0.1"
|
"uuid": "^3.0.1"
|
||||||
|
|
|
@ -64,7 +64,6 @@ const FolderScreen = connect(
|
||||||
(state) => {
|
(state) => {
|
||||||
return {
|
return {
|
||||||
folderId: state.selectedFolderId,
|
folderId: state.selectedFolderId,
|
||||||
//folder: state.selectedFolderId ? Folder.byId(state.folders, state.selectedFolderId) : Folder.newFolder(),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
)(FolderScreenComponent)
|
)(FolderScreenComponent)
|
||||||
|
|
|
@ -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 (
|
||||||
|
<ScrollView scrollsToTop={false} style={styles.menu}>
|
||||||
|
<View style={styles.avatarContainer}>
|
||||||
|
<Image
|
||||||
|
style={styles.avatar}
|
||||||
|
source={{ uri, }}/>
|
||||||
|
<Text style={styles.name}>Your name</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Text
|
||||||
|
onPress={() => this.props.onItemSelected('About')}
|
||||||
|
style={styles.item}>
|
||||||
|
About
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Text
|
||||||
|
onPress={() => this.props.onItemSelected('Contacts')}
|
||||||
|
style={styles.item}>
|
||||||
|
Contacts
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Text style={styles.item}>Contacts</Text>
|
||||||
|
<Text style={styles.item}>Contacts</Text>
|
||||||
|
<Text style={styles.item}>Contacts</Text>
|
||||||
|
<Text style={styles.item}>Contacts</Text>
|
||||||
|
<Text style={styles.item}>Contacts</Text>
|
||||||
|
<Text style={styles.item}>Contacts</Text>
|
||||||
|
<Text style={styles.item}>Contacts</Text>
|
||||||
|
<Text style={styles.item}>Contacts</Text>
|
||||||
|
<Text style={styles.item}>Contacts</Text>
|
||||||
|
<Text style={styles.item}>Contacts</Text>
|
||||||
|
<Text style={styles.item}>Contacts</Text>
|
||||||
|
<Text style={styles.item}>Contacts</Text>
|
||||||
|
<Text style={styles.item}>Contacts</Text>
|
||||||
|
<Text style={styles.item}>Contacts</Text>
|
||||||
|
<Text style={styles.item}>Contacts</Text>
|
||||||
|
<Text style={styles.item}>Contacts</Text>
|
||||||
|
<Text style={styles.item}>Contacts</Text>
|
||||||
|
<Text style={styles.item}>Contacts</Text>
|
||||||
|
<Text style={styles.item}>Contacts</Text>
|
||||||
|
<Text style={styles.item}>Contacts</Text>
|
||||||
|
<Text style={styles.item}>Contacts</Text>
|
||||||
|
<Text style={styles.item}>Contacts</Text>
|
||||||
|
<Text style={styles.item}>Contacts</Text>
|
||||||
|
<Text style={styles.item}>Contacts</Text>
|
||||||
|
<Text style={styles.item}>Contacts</Text>
|
||||||
|
<Text style={styles.item}>ContactsLL</Text>
|
||||||
|
</ScrollView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
|
@ -159,6 +159,11 @@ const AppNavigator = StackNavigator({
|
||||||
Login: {screen: LoginScreen},
|
Login: {screen: LoginScreen},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const SideMenu = require('react-native-side-menu');
|
||||||
|
|
||||||
|
import Menu from 'src/menu.js';
|
||||||
|
|
||||||
|
|
||||||
class AppComponent extends React.Component {
|
class AppComponent extends React.Component {
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -207,13 +212,17 @@ class AppComponent extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const menu = <Menu/>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MenuContext style={{ flex: 1 }}>
|
<SideMenu menu={menu}>
|
||||||
<AppNavigator navigation={addNavigationHelpers({
|
<MenuContext style={{ flex: 1 }}>
|
||||||
dispatch: this.props.dispatch,
|
<AppNavigator navigation={addNavigationHelpers({
|
||||||
state: this.props.nav,
|
dispatch: this.props.dispatch,
|
||||||
})} />
|
state: this.props.nav,
|
||||||
</MenuContext>
|
})} />
|
||||||
|
</MenuContext>
|
||||||
|
</SideMenu>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,12 +131,20 @@ class Synchronizer {
|
||||||
return p.then(() => {
|
return p.then(() => {
|
||||||
processedChangeIds = processedChangeIds.concat(c.ids);
|
processedChangeIds = processedChangeIds.concat(c.ids);
|
||||||
}).catch((error) => {
|
}).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);
|
Log.info('IDs to delete: ', processedChangeIds);
|
||||||
Change.deleteMultiple(processedChangeIds);
|
Change.deleteMultiple(processedChangeIds);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,21 @@
|
||||||
import { Log } from 'src/log.js';
|
import { Log } from 'src/log.js';
|
||||||
import { stringify } from 'query-string';
|
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 {
|
class WebApi {
|
||||||
|
|
||||||
constructor(baseUrl) {
|
constructor(baseUrl) {
|
||||||
|
@ -73,7 +88,7 @@ class WebApi {
|
||||||
let responseClone = response.clone();
|
let responseClone = response.clone();
|
||||||
return response.json().then(function(data) {
|
return response.json().then(function(data) {
|
||||||
if (data && data.error) {
|
if (data && data.error) {
|
||||||
reject(new Error(data.error));
|
reject(new WebApiError(data));
|
||||||
} else {
|
} else {
|
||||||
resolve(data);
|
resolve(data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,8 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use AppBundle\Controller\ApiController;
|
use AppBundle\Controller\ApiController;
|
||||||
use AppBundle\Model\Folder;
|
use AppBundle\Model\Folder;
|
||||||
|
use AppBundle\Exception\NotFoundException;
|
||||||
|
use AppBundle\Exception\MethodNotAllowedException;
|
||||||
|
|
||||||
use AppBundle\Model\BaseItem;
|
|
||||||
|
|
||||||
class FoldersController extends ApiController {
|
class FoldersController extends ApiController {
|
||||||
|
|
||||||
|
@ -30,7 +28,7 @@ class FoldersController extends ApiController {
|
||||||
return static::successResponse($folder);
|
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) {
|
public function oneAction($id, Request $request) {
|
||||||
$folder = Folder::byId(Folder::unhex($id));
|
$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')) {
|
if ($request->isMethod('GET')) {
|
||||||
return static::successResponse($folder);
|
return static::successResponse($folder);
|
||||||
|
@ -68,7 +66,7 @@ class FoldersController extends ApiController {
|
||||||
return static::successResponse(array('id' => $id));
|
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) {
|
public function linkAction($id, Request $request) {
|
||||||
$folder = Folder::byId(Folder::unhex($id));
|
$folder = Folder::byId(Folder::unhex($id));
|
||||||
if (!$folder) return static::errorResponse('Not found', 0, 404);
|
if (!$folder) throw new NotFoundException();
|
||||||
|
|
||||||
if ($request->isMethod('GET')) {
|
if ($request->isMethod('GET')) {
|
||||||
return static::successResponse($folder->notes());
|
return static::successResponse($folder->notes());
|
||||||
|
@ -91,7 +89,7 @@ class FoldersController extends ApiController {
|
||||||
return static::successResponse();
|
return static::successResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
return static::errorResponse('Invalid method');
|
throw new MethodNotAllowedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use AppBundle\Controller\ApiController;
|
use AppBundle\Controller\ApiController;
|
||||||
use AppBundle\Model\Note;
|
use AppBundle\Model\Note;
|
||||||
|
use AppBundle\Exception\NotFoundException;
|
||||||
|
|
||||||
class NotesController extends ApiController {
|
class NotesController extends ApiController {
|
||||||
|
|
||||||
|
@ -30,7 +31,7 @@ class NotesController extends ApiController {
|
||||||
*/
|
*/
|
||||||
public function oneAction($id, Request $request) {
|
public function oneAction($id, Request $request) {
|
||||||
$note = Note::find(Note::unhex($id));
|
$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')) {
|
if ($request->isMethod('GET')) {
|
||||||
return static::successResponse($note);
|
return static::successResponse($note);
|
||||||
|
|
|
@ -30,4 +30,18 @@ class Folder extends BaseItem {
|
||||||
return Note::where('parent_id', '=', $this->id)->get();
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
c:
|
||||||
|
C:\Users\Laurent\AppData\Local\Android\sdk\tools
|
||||||
|
emulator.exe -avd Nexus_5X_API_23_Google_API_
|
Loading…
Reference in New Issue