mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-26 18:58:21 +02:00
Support for tags
This commit is contained in:
parent
e128077326
commit
d5e39d153f
@ -31,7 +31,7 @@ class Command extends BaseCommand {
|
|||||||
|
|
||||||
let tags = await Tag.all();
|
let tags = await Tag.all();
|
||||||
for (let i = 0; i < tags.length; i++) {
|
for (let i = 0; i < tags.length; i++) {
|
||||||
tags[i].notes_ = await Tag.tagNoteIds(tags[i].id);
|
tags[i].notes_ = await Tag.noteIds(tags[i].id);
|
||||||
}
|
}
|
||||||
|
|
||||||
items = items.concat(tags);
|
items = items.concat(tags);
|
||||||
|
@ -511,24 +511,24 @@ describe('Synchronizer', function() {
|
|||||||
expect(remoteTag.id).toBe(tag.id);
|
expect(remoteTag.id).toBe(tag.id);
|
||||||
await Tag.addNote(remoteTag.id, n1.id);
|
await Tag.addNote(remoteTag.id, n1.id);
|
||||||
await Tag.addNote(remoteTag.id, n2.id);
|
await Tag.addNote(remoteTag.id, n2.id);
|
||||||
let noteIds = await Tag.tagNoteIds(tag.id);
|
let noteIds = await Tag.noteIds(tag.id);
|
||||||
expect(noteIds.length).toBe(2);
|
expect(noteIds.length).toBe(2);
|
||||||
await synchronizer().start();
|
await synchronizer().start();
|
||||||
|
|
||||||
await switchClient(1);
|
await switchClient(1);
|
||||||
|
|
||||||
await synchronizer().start();
|
await synchronizer().start();
|
||||||
let remoteNoteIds = await Tag.tagNoteIds(tag.id);
|
let remoteNoteIds = await Tag.noteIds(tag.id);
|
||||||
expect(remoteNoteIds.length).toBe(2);
|
expect(remoteNoteIds.length).toBe(2);
|
||||||
await Tag.removeNote(tag.id, n1.id);
|
await Tag.removeNote(tag.id, n1.id);
|
||||||
remoteNoteIds = await Tag.tagNoteIds(tag.id);
|
remoteNoteIds = await Tag.noteIds(tag.id);
|
||||||
expect(remoteNoteIds.length).toBe(1);
|
expect(remoteNoteIds.length).toBe(1);
|
||||||
await synchronizer().start();
|
await synchronizer().start();
|
||||||
|
|
||||||
await switchClient(2);
|
await switchClient(2);
|
||||||
|
|
||||||
await synchronizer().start();
|
await synchronizer().start();
|
||||||
noteIds = await Tag.tagNoteIds(tag.id);
|
noteIds = await Tag.noteIds(tag.id);
|
||||||
expect(noteIds.length).toBe(1);
|
expect(noteIds.length).toBe(1);
|
||||||
expect(remoteNoteIds[0]).toBe(noteIds[0]);
|
expect(remoteNoteIds[0]).toBe(noteIds[0]);
|
||||||
|
|
||||||
|
@ -307,6 +307,10 @@ class BaseModel {
|
|||||||
return this.db_;
|
return this.db_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static isReady() {
|
||||||
|
return !!this.db_;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseModel.TYPE_NOTE = 1;
|
BaseModel.TYPE_NOTE = 1;
|
||||||
|
@ -5,7 +5,7 @@ const globalStyle = {
|
|||||||
colorFaded: "#777777", // For less important text
|
colorFaded: "#777777", // For less important text
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
dividerColor: "#dddddd",
|
dividerColor: "#dddddd",
|
||||||
selectedColor: '#eeeeee',
|
selectedColor: '#e5e5e5',
|
||||||
disabledOpacity: 0.3,
|
disabledOpacity: 0.3,
|
||||||
|
|
||||||
// For WebView - must correspond to the properties above
|
// For WebView - must correspond to the properties above
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import { ListView, Text, TouchableHighlight, Switch, View, StyleSheet } from 'react-native';
|
import { ListView, Text, TouchableHighlight, View, StyleSheet } from 'react-native';
|
||||||
import { Log } from 'lib/log.js';
|
import { Log } from 'lib/log.js';
|
||||||
import { _ } from 'lib/locale.js';
|
import { _ } from 'lib/locale.js';
|
||||||
import { Checkbox } from 'lib/components/checkbox.js';
|
import { Checkbox } from 'lib/components/checkbox.js';
|
||||||
@ -39,10 +39,29 @@ class NoteItemComponent extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async todoCheckbox_change(checked) {
|
||||||
|
if (!this.props.note) return;
|
||||||
|
|
||||||
|
const newNote = {
|
||||||
|
id: this.props.note.id,
|
||||||
|
todo_completed: checked ? time.unixMs() : 0,
|
||||||
|
}
|
||||||
|
await Note.save(newNote);
|
||||||
|
}
|
||||||
|
|
||||||
|
onPress() {
|
||||||
|
if (!this.props.note) return;
|
||||||
|
|
||||||
|
this.props.dispatch({
|
||||||
|
type: 'NAV_GO',
|
||||||
|
routeName: 'Note',
|
||||||
|
noteId: this.props.note.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const note = this.props.note ? this.props.note : {};
|
const note = this.props.note ? this.props.note : {};
|
||||||
const onPress = this.props.onPress;
|
const onPress = this.props.onPress;
|
||||||
const onLongPress = this.props.onLongPress;
|
|
||||||
const onCheckboxChange = this.props.onCheckboxChange;
|
const onCheckboxChange = this.props.onCheckboxChange;
|
||||||
|
|
||||||
const checkboxStyle = !Number(note.is_todo) ? { display: 'none' } : { color: globalStyle.color };
|
const checkboxStyle = !Number(note.is_todo) ? { display: 'none' } : { color: globalStyle.color };
|
||||||
@ -51,9 +70,14 @@ class NoteItemComponent extends Component {
|
|||||||
const listItemStyle = !!Number(note.is_todo) && checkboxChecked ? styles.listItemFadded : styles.listItem;
|
const listItemStyle = !!Number(note.is_todo) && checkboxChecked ? styles.listItemFadded : styles.listItem;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchableHighlight onPress={() => onPress ? onPress(note) : this.noteItem_press(note.id)} onLongPress={() => onLongPress(note)} underlayColor="#0066FF">
|
<TouchableHighlight onPress={() => this.onPress()} underlayColor="#0066FF">
|
||||||
<View style={ listItemStyle }>
|
<View style={ listItemStyle }>
|
||||||
<Checkbox style={checkboxStyle} checked={checkboxChecked} onChange={(checked) => { onCheckboxChange(note, checked) }}/><Text style={styles.listItemText}>{note.title}</Text>
|
<Checkbox
|
||||||
|
style={checkboxStyle}
|
||||||
|
checked={checkboxChecked}
|
||||||
|
onChange={(checked) => this.todoCheckbox_change(checked)}
|
||||||
|
/>
|
||||||
|
<Text style={styles.listItemText}>{note.title}</Text>
|
||||||
</View>
|
</View>
|
||||||
</TouchableHighlight>
|
</TouchableHighlight>
|
||||||
);
|
);
|
||||||
|
@ -66,21 +66,6 @@ class NoteListComponent extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async todoCheckbox_change(itemId, checked) {
|
|
||||||
let note = await Note.load(itemId);
|
|
||||||
await Note.save({ id: note.id, todo_completed: checked ? time.unixMs() : 0 });
|
|
||||||
}
|
|
||||||
|
|
||||||
listView_itemPress(noteId) {
|
|
||||||
this.props.dispatch({
|
|
||||||
type: 'NAV_GO',
|
|
||||||
routeName: 'Note',
|
|
||||||
noteId: noteId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
listView_itemLongPress(itemId) {}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
// `enableEmptySections` is to fix this warning: https://github.com/FaridSafi/react-native-gifted-listview/issues/39
|
// `enableEmptySections` is to fix this warning: https://github.com/FaridSafi/react-native-gifted-listview/issues/39
|
||||||
|
|
||||||
@ -89,11 +74,8 @@ class NoteListComponent extends Component {
|
|||||||
<ListView
|
<ListView
|
||||||
dataSource={this.state.dataSource}
|
dataSource={this.state.dataSource}
|
||||||
renderRow={(note) => {
|
renderRow={(note) => {
|
||||||
return <NoteItem
|
return <NoteItem note={note}/>
|
||||||
note={note}
|
}}
|
||||||
onPress={(note) => this.listView_itemPress(note.id) }
|
|
||||||
onCheckboxChange={(note, checked) => this.todoCheckbox_change(note.id, checked) }
|
|
||||||
/> }}
|
|
||||||
enableEmptySections={true}
|
enableEmptySections={true}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -5,6 +5,7 @@ import { reg } from 'lib/registry.js';
|
|||||||
import { Log } from 'lib/log.js'
|
import { Log } from 'lib/log.js'
|
||||||
import { NoteList } from 'lib/components/note-list.js'
|
import { NoteList } from 'lib/components/note-list.js'
|
||||||
import { Folder } from 'lib/models/folder.js'
|
import { Folder } from 'lib/models/folder.js'
|
||||||
|
import { Tag } from 'lib/models/tag.js'
|
||||||
import { Note } from 'lib/models/note.js'
|
import { Note } from 'lib/models/note.js'
|
||||||
import { ScreenHeader } from 'lib/components/screen-header.js';
|
import { ScreenHeader } from 'lib/components/screen-header.js';
|
||||||
import { MenuOption, Text } from 'react-native-popup-menu';
|
import { MenuOption, Text } from 'react-native-popup-menu';
|
||||||
@ -27,7 +28,9 @@ class NotesScreenComponent extends BaseScreenComponent {
|
|||||||
async componentWillReceiveProps(newProps) {
|
async componentWillReceiveProps(newProps) {
|
||||||
if (newProps.notesOrder.orderBy != this.props.notesOrder.orderBy ||
|
if (newProps.notesOrder.orderBy != this.props.notesOrder.orderBy ||
|
||||||
newProps.notesOrder.orderByDir != this.props.notesOrder.orderByDir ||
|
newProps.notesOrder.orderByDir != this.props.notesOrder.orderByDir ||
|
||||||
newProps.selectedFolderId != this.props.selectedFolderId) {
|
newProps.selectedFolderId != this.props.selectedFolderId ||
|
||||||
|
newProps.selectedTagId != this.props.selectedTagId ||
|
||||||
|
newProps.notesParentType != this.props.notesParentType) {
|
||||||
await this.refreshNotes(newProps);
|
await this.refreshNotes(newProps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -40,16 +43,26 @@ class NotesScreenComponent extends BaseScreenComponent {
|
|||||||
orderByDir: props.notesOrder.orderByDir,
|
orderByDir: props.notesOrder.orderByDir,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const parent = this.parentItem(props);
|
||||||
|
|
||||||
const source = JSON.stringify({
|
const source = JSON.stringify({
|
||||||
options: options,
|
options: options,
|
||||||
selectedFolderId: props.selectedFolderId,
|
parentId: parent.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
let folder = Folder.byId(props.folders, props.selectedFolderId);
|
if (source == props.notesSource) {
|
||||||
|
console.info('NO SOURCE CHAGNE');
|
||||||
|
console.info(source);
|
||||||
|
console.info(props.notesSource);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (source == props.notesSource) return;
|
let notes = [];
|
||||||
|
if (props.notesParentType == 'Folder') {
|
||||||
const notes = await Note.previews(props.selectedFolderId, options);
|
notes = await Note.previews(props.selectedFolderId, options);
|
||||||
|
} else {
|
||||||
|
notes = await Tag.notes(props.selectedTagId); // TODO: should also return previews
|
||||||
|
}
|
||||||
|
|
||||||
this.props.dispatch({
|
this.props.dispatch({
|
||||||
type: 'NOTES_UPDATE_ALL',
|
type: 'NOTES_UPDATE_ALL',
|
||||||
@ -82,18 +95,36 @@ class NotesScreenComponent extends BaseScreenComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
menuOptions() {
|
menuOptions() {
|
||||||
|
if (this.props.notesParentType == 'Folder') {
|
||||||
if (this.props.selectedFolderId == Folder.conflictFolderId()) return [];
|
if (this.props.selectedFolderId == Folder.conflictFolderId()) return [];
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{ title: _('Delete notebook'), onPress: () => { this.deleteFolder_onPress(this.props.selectedFolderId); } },
|
{ title: _('Delete notebook'), onPress: () => { this.deleteFolder_onPress(this.props.selectedFolderId); } },
|
||||||
{ title: _('Edit notebook'), onPress: () => { this.editFolder_onPress(this.props.selectedFolderId); } },
|
{ title: _('Edit notebook'), onPress: () => { this.editFolder_onPress(this.props.selectedFolderId); } },
|
||||||
];
|
];
|
||||||
|
} else {
|
||||||
|
return []; // TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parentItem(props = null) {
|
||||||
|
if (!props) props = this.props;
|
||||||
|
|
||||||
|
let output = null;
|
||||||
|
if (props.notesParentType == 'Folder') {
|
||||||
|
output = Folder.byId(props.folders, props.selectedFolderId);
|
||||||
|
} else if (props.notesParentType == 'Tag') {
|
||||||
|
output = Tag.byId(props.tags, props.selectedTagId);
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid parent type: ' + props.notesParentType);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let folder = Folder.byId(this.props.folders, this.props.selectedFolderId);
|
const parent = this.parentItem();
|
||||||
|
|
||||||
if (!folder) {
|
if (!parent) {
|
||||||
return (
|
return (
|
||||||
<View style={this.styles().screen}>
|
<View style={this.styles().screen}>
|
||||||
<ScreenHeader title={title} menuOptions={this.menuOptions()} />
|
<ScreenHeader title={title} menuOptions={this.menuOptions()} />
|
||||||
@ -101,8 +132,8 @@ class NotesScreenComponent extends BaseScreenComponent {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let title = folder ? folder.title : null;
|
let title = parent ? parent.title : null;
|
||||||
const addFolderNoteButtons = folder.id != Folder.conflictFolderId();
|
const addFolderNoteButtons = this.props.selectedFolderId && this.props.selectedFolderId != Folder.conflictFolderId();
|
||||||
|
|
||||||
const { navigate } = this.props.navigation;
|
const { navigate } = this.props.navigation;
|
||||||
return (
|
return (
|
||||||
@ -120,7 +151,10 @@ const NotesScreen = connect(
|
|||||||
(state) => {
|
(state) => {
|
||||||
return {
|
return {
|
||||||
folders: state.folders,
|
folders: state.folders,
|
||||||
|
tags: state.tags,
|
||||||
selectedFolderId: state.selectedFolderId,
|
selectedFolderId: state.selectedFolderId,
|
||||||
|
selectedTagId: state.selectedTagId,
|
||||||
|
notesParentType: state.notesParentType,
|
||||||
notes: state.notes,
|
notes: state.notes,
|
||||||
notesOrder: state.notesOrder,
|
notesOrder: state.notesOrder,
|
||||||
notesSource: state.notesSource,
|
notesSource: state.notesSource,
|
||||||
|
@ -134,9 +134,7 @@ class SearchScreenComponent extends BaseScreenComponent {
|
|||||||
<FlatList
|
<FlatList
|
||||||
data={this.state.notes}
|
data={this.state.notes}
|
||||||
keyExtractor={(item, index) => item.id}
|
keyExtractor={(item, index) => item.id}
|
||||||
renderItem={(event) => <NoteItem
|
renderItem={(event) => <NoteItem note={event.item}/>}
|
||||||
note={event.item}
|
|
||||||
/>}
|
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
76
ReactNativeClient/lib/components/screens/tag.js
Normal file
76
ReactNativeClient/lib/components/screens/tag.js
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import { ListView, StyleSheet, View, TextInput, FlatList, TouchableHighlight } from 'react-native';
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { ScreenHeader } from 'lib/components/screen-header.js';
|
||||||
|
import Icon from 'react-native-vector-icons/Ionicons';
|
||||||
|
import { _ } from 'lib/locale.js';
|
||||||
|
import { Note } from 'lib/models/note.js';
|
||||||
|
import { NoteItem } from 'lib/components/note-item.js';
|
||||||
|
import { BaseScreenComponent } from 'lib/components/base-screen.js';
|
||||||
|
import { globalStyle } from 'lib/components/global-style.js';
|
||||||
|
|
||||||
|
let styles = {
|
||||||
|
body: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
class TagScreenComponent extends BaseScreenComponent {
|
||||||
|
|
||||||
|
static navigationOptions(options) {
|
||||||
|
return { header: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.refreshNotes();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(newProps) {
|
||||||
|
if (newProps.selectedTagId !== this.props.selectedTagId) {
|
||||||
|
this.refreshNotes(newProps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async refreshNotes(props = null) {
|
||||||
|
if (props === null) props = this.props;
|
||||||
|
|
||||||
|
const source = JSON.stringify({ selectedTagId: props.selectedTagId });
|
||||||
|
if (source == props.tagNotesSource) return;
|
||||||
|
|
||||||
|
const notes = await Tag.notes(props.selectedTagId);
|
||||||
|
|
||||||
|
this.props.dispatch({
|
||||||
|
type: 'NOTES_UPDATE_ALL',
|
||||||
|
notes: notes,
|
||||||
|
notesSource: source,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let title = tag ? tag.title : '';
|
||||||
|
|
||||||
|
// <ActionButton addFolderNoteButtons={true} parentFolderId={this.props.selectedFolderId}></ActionButton>
|
||||||
|
|
||||||
|
const { navigate } = this.props.navigation;
|
||||||
|
return (
|
||||||
|
<View style={this.styles().screen}>
|
||||||
|
<ScreenHeader title={title} menuOptions={this.menuOptions()} />
|
||||||
|
<NoteList style={{flex: 1}}/>
|
||||||
|
<DialogBox ref={dialogbox => { this.dialogbox = dialogbox }}/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const TagScreen = connect(
|
||||||
|
(state) => {
|
||||||
|
return {
|
||||||
|
tag: tag,
|
||||||
|
notes: state.notes,
|
||||||
|
notesSource: state.notesSource,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)(TagScreenComponent)
|
||||||
|
|
||||||
|
export { TagScreen };
|
@ -3,6 +3,7 @@ import { TouchableOpacity , Button, Text, Image, StyleSheet, ScrollView, View }
|
|||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import Icon from 'react-native-vector-icons/Ionicons';
|
import Icon from 'react-native-vector-icons/Ionicons';
|
||||||
import { Log } from 'lib/log.js';
|
import { Log } from 'lib/log.js';
|
||||||
|
import { Tag } from 'lib/models/tag.js';
|
||||||
import { Note } from 'lib/models/note.js';
|
import { Note } from 'lib/models/note.js';
|
||||||
import { Setting } from 'lib/models/setting.js';
|
import { Setting } from 'lib/models/setting.js';
|
||||||
import { FoldersScreenUtils } from 'lib/components/screens/folders-utils.js'
|
import { FoldersScreenUtils } from 'lib/components/screens/folders-utils.js'
|
||||||
@ -11,7 +12,7 @@ import { reg } from 'lib/registry.js';
|
|||||||
import { _ } from 'lib/locale.js';
|
import { _ } from 'lib/locale.js';
|
||||||
import { globalStyle } from 'lib/components/global-style.js';
|
import { globalStyle } from 'lib/components/global-style.js';
|
||||||
|
|
||||||
const styleObject = {
|
let styles = {
|
||||||
menu: {
|
menu: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
backgroundColor: globalStyle.backgroundColor,
|
backgroundColor: globalStyle.backgroundColor,
|
||||||
@ -43,24 +44,39 @@ const styleObject = {
|
|||||||
paddingRight: globalStyle.marginRight,
|
paddingRight: globalStyle.marginRight,
|
||||||
color: globalStyle.colorFaded,
|
color: globalStyle.colorFaded,
|
||||||
},
|
},
|
||||||
|
tagItemList: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
flexWrap: 'wrap'
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
styleObject.folderButton = Object.assign({}, styleObject.button);
|
styles.folderButton = Object.assign({}, styles.button);
|
||||||
styleObject.folderButtonText = Object.assign({}, styleObject.buttonText);
|
styles.folderButtonText = Object.assign({}, styles.buttonText);
|
||||||
styleObject.folderIcon = Object.assign({}, globalStyle.icon);
|
styles.folderButtonSelected = Object.assign({}, styles.folderButton);
|
||||||
styleObject.folderIcon.color = '#0072d5';
|
styles.folderButtonSelected.backgroundColor = globalStyle.selectedColor;
|
||||||
styleObject.syncButton = Object.assign({}, styleObject.button);
|
styles.folderIcon = Object.assign({}, globalStyle.icon);
|
||||||
styleObject.syncButtonText = Object.assign({}, styleObject.buttonText);
|
styles.folderIcon.color = '#0072d5';
|
||||||
styleObject.folderButtonSelected = Object.assign({}, styleObject.folderButton);
|
|
||||||
styleObject.folderButtonSelected.backgroundColor = globalStyle.selectedColor;
|
|
||||||
|
|
||||||
const styles = StyleSheet.create(styleObject);
|
styles.tagButton = Object.assign({}, styles.button);
|
||||||
|
styles.tagButtonSelected = Object.assign({}, styles.tagButton);
|
||||||
|
styles.tagButtonSelected.backgroundColor = globalStyle.selectedColor;
|
||||||
|
styles.tagButtonSelected.borderRadius = 1000;
|
||||||
|
styles.tagButtonText = Object.assign({}, styles.buttonText);
|
||||||
|
styles.tagButtonText.flex = 0;
|
||||||
|
|
||||||
|
styles.syncButton = Object.assign({}, styles.button);
|
||||||
|
styles.syncButtonText = Object.assign({}, styles.buttonText);
|
||||||
|
|
||||||
|
styles = StyleSheet.create(styles);
|
||||||
|
|
||||||
class SideMenuContentComponent extends Component {
|
class SideMenuContentComponent extends Component {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.state = { syncReportText: '' };
|
this.state = { syncReportText: '',
|
||||||
|
//width: 0,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
folder_press(folder) {
|
folder_press(folder) {
|
||||||
@ -73,6 +89,16 @@ class SideMenuContentComponent extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tag_press(tag) {
|
||||||
|
this.props.dispatch({ type: 'SIDE_MENU_CLOSE' });
|
||||||
|
|
||||||
|
this.props.dispatch({
|
||||||
|
type: 'NAV_GO',
|
||||||
|
routeName: 'Notes',
|
||||||
|
tagId: tag.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async synchronize_press() {
|
async synchronize_press() {
|
||||||
if (Setting.value('sync.target') == Setting.SYNC_TARGET_ONEDRIVE && !reg.oneDriveApi().auth()) {
|
if (Setting.value('sync.target') == Setting.SYNC_TARGET_ONEDRIVE && !reg.oneDriveApi().auth()) {
|
||||||
this.props.dispatch({ type: 'SIDE_MENU_CLOSE' });
|
this.props.dispatch({ type: 'SIDE_MENU_CLOSE' });
|
||||||
@ -107,6 +133,20 @@ class SideMenuContentComponent extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tagItem(tag, selected) {
|
||||||
|
const iconComp = <Icon name='md-pricetag' style={styles.folderIcon} />
|
||||||
|
const tagButtonStyle = selected ? styles.tagButtonSelected : styles.tagButton;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TouchableOpacity key={tag.id} onPress={() => { this.tag_press(tag) }}>
|
||||||
|
<View style={tagButtonStyle}>
|
||||||
|
{ iconComp }
|
||||||
|
<Text numberOfLines={1} style={styles.tagButtonText}>{tag.title}</Text>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
synchronizeButton(state) {
|
synchronizeButton(state) {
|
||||||
const title = state == 'sync' ? _('Synchronize') : _('Cancel synchronization');
|
const title = state == 'sync' ? _('Synchronize') : _('Cancel synchronization');
|
||||||
const iconComp = state == 'sync' ? <Icon name='md-sync' style={globalStyle.icon} /> : <Icon name='md-close' style={globalStyle.icon} />;
|
const iconComp = state == 'sync' ? <Icon name='md-sync' style={globalStyle.icon} /> : <Icon name='md-close' style={globalStyle.icon} />;
|
||||||
@ -121,6 +161,16 @@ class SideMenuContentComponent extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
makeDivider(key) {
|
||||||
|
return <View style={{ marginTop: 15, marginBottom: 15, flex: -1, borderBottomWidth: 1, borderBottomColor: globalStyle.dividerColor }} key={key}></View>
|
||||||
|
}
|
||||||
|
|
||||||
|
// onLayout(event) {
|
||||||
|
// const newWidth = event.nativeEvent.layout.width;
|
||||||
|
// if (this.state.width == newWidth) return;
|
||||||
|
// this.setState({ width: newWidth });
|
||||||
|
// }
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let items = [];
|
let items = [];
|
||||||
|
|
||||||
@ -130,10 +180,24 @@ class SideMenuContentComponent extends Component {
|
|||||||
|
|
||||||
for (let i = 0; i < this.props.folders.length; i++) {
|
for (let i = 0; i < this.props.folders.length; i++) {
|
||||||
let folder = this.props.folders[i];
|
let folder = this.props.folders[i];
|
||||||
items.push(this.folderItem(folder, this.props.selectedFolderId == folder.id));
|
items.push(this.folderItem(folder, this.props.selectedFolderId == folder.id && this.props.notesParentType == 'Folder'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (items.length) items.push(<View style={{ height: 30, flex: -1 }} key='divider_1'></View>); // DIVIDER
|
if (items.length) items.push(this.makeDivider('divider_1'));
|
||||||
|
|
||||||
|
let tagItems = [];
|
||||||
|
for (let i = 0; i < this.props.tags.length; i++) {
|
||||||
|
const tag = this.props.tags[i];
|
||||||
|
tagItems.push(this.tagItem(tag, this.props.selectedTagId == tag.id && this.props.notesParentType == 'Tag'));
|
||||||
|
}
|
||||||
|
|
||||||
|
items.push(
|
||||||
|
<View style={styles.tagItemList} key="tag_items">
|
||||||
|
{tagItems}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (items.length) items.push(this.makeDivider('divider_2'));
|
||||||
|
|
||||||
let lines = Synchronizer.reportToLines(this.props.syncReport);
|
let lines = Synchronizer.reportToLines(this.props.syncReport);
|
||||||
const syncReportText = lines.join("\n");
|
const syncReportText = lines.join("\n");
|
||||||
@ -144,6 +208,8 @@ class SideMenuContentComponent extends Component {
|
|||||||
|
|
||||||
items.push(<View style={{ height: globalStyle.marginBottom }} key='bottom_padding_hack'/>);
|
items.push(<View style={{ height: globalStyle.marginBottom }} key='bottom_padding_hack'/>);
|
||||||
|
|
||||||
|
// onLayout={(event) => this.onLayout(event)}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={{flex:1}}>
|
<View style={{flex:1}}>
|
||||||
<View style={{flexDirection:'row'}}>
|
<View style={{flexDirection:'row'}}>
|
||||||
@ -161,9 +227,12 @@ const SideMenuContent = connect(
|
|||||||
(state) => {
|
(state) => {
|
||||||
return {
|
return {
|
||||||
folders: state.folders,
|
folders: state.folders,
|
||||||
|
tags: state.tags,
|
||||||
syncStarted: state.syncStarted,
|
syncStarted: state.syncStarted,
|
||||||
syncReport: state.syncReport,
|
syncReport: state.syncReport,
|
||||||
selectedFolderId: state.selectedFolderId,
|
selectedFolderId: state.selectedFolderId,
|
||||||
|
selectedTagId: state.selectedTagId,
|
||||||
|
notesParentType: state.notesParentType,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
)(SideMenuContentComponent)
|
)(SideMenuContentComponent)
|
||||||
|
@ -21,7 +21,7 @@ class Tag extends BaseItem {
|
|||||||
return super.serialize(item, 'tag', fieldNames);
|
return super.serialize(item, 'tag', fieldNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async tagNoteIds(tagId) {
|
static async noteIds(tagId) {
|
||||||
let rows = await this.db().selectAll('SELECT note_id FROM note_tags WHERE tag_id = ?', [tagId]);
|
let rows = await this.db().selectAll('SELECT note_id FROM note_tags WHERE tag_id = ?', [tagId]);
|
||||||
let output = [];
|
let output = [];
|
||||||
for (let i = 0; i < rows.length; i++) {
|
for (let i = 0; i < rows.length; i++) {
|
||||||
@ -31,7 +31,7 @@ class Tag extends BaseItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static async notes(tagId) {
|
static async notes(tagId) {
|
||||||
let noteIds = await this.tagNoteIds(tagId);
|
let noteIds = await this.noteIds(tagId);
|
||||||
if (!noteIds.length) return [];
|
if (!noteIds.length) return [];
|
||||||
|
|
||||||
return Note.search({
|
return Note.search({
|
||||||
|
@ -38,10 +38,13 @@ import { PoorManIntervals } from 'lib/poor-man-intervals.js';
|
|||||||
let defaultState = {
|
let defaultState = {
|
||||||
notes: [],
|
notes: [],
|
||||||
notesSource: '',
|
notesSource: '',
|
||||||
|
notesParentType: null,
|
||||||
folders: [],
|
folders: [],
|
||||||
|
tags: [],
|
||||||
selectedNoteId: null,
|
selectedNoteId: null,
|
||||||
selectedItemType: 'note',
|
|
||||||
selectedFolderId: null,
|
selectedFolderId: null,
|
||||||
|
selectedTagId: null,
|
||||||
|
selectedItemType: 'note',
|
||||||
showSideMenu: false,
|
showSideMenu: false,
|
||||||
screens: {},
|
screens: {},
|
||||||
loading: true,
|
loading: true,
|
||||||
@ -148,6 +151,12 @@ const reducer = (state = defaultState, action) => {
|
|||||||
|
|
||||||
if ('folderId' in action) {
|
if ('folderId' in action) {
|
||||||
newState.selectedFolderId = action.folderId;
|
newState.selectedFolderId = action.folderId;
|
||||||
|
newState.notesParentType = 'Folder';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('tagId' in action) {
|
||||||
|
newState.selectedTagId = action.tagId;
|
||||||
|
newState.notesParentType = 'Tag';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('itemType' in action) {
|
if ('itemType' in action) {
|
||||||
@ -229,6 +238,12 @@ const reducer = (state = defaultState, action) => {
|
|||||||
newState.folders = action.folders;
|
newState.folders = action.folders;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'TAGS_UPDATE_ALL':
|
||||||
|
|
||||||
|
newState = Object.assign({}, state);
|
||||||
|
newState.tags = action.tags;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'FOLDERS_UPDATE_ONE':
|
case 'FOLDERS_UPDATE_ONE':
|
||||||
|
|
||||||
var newFolders = state.folders.splice(0);
|
var newFolders = state.folders.splice(0);
|
||||||
@ -414,6 +429,13 @@ async function initialize(dispatch, backButtonHandler) {
|
|||||||
|
|
||||||
await FoldersScreenUtils.refreshFolders();
|
await FoldersScreenUtils.refreshFolders();
|
||||||
|
|
||||||
|
const tags = await Tag.all();
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: 'TAGS_UPDATE_ALL',
|
||||||
|
tags: tags,
|
||||||
|
});
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'APPLICATION_LOADING_DONE',
|
type: 'APPLICATION_LOADING_DONE',
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user