1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-08-30 20:39:46 +02:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Laurent Cozic
10b4907cee Updated readme 2017-12-05 09:35:15 +00:00
62 changed files with 1073 additions and 3181 deletions

View File

@@ -1,21 +1,20 @@
# General information
- All the applications share the same library, which, for historical reasons, is in ReactNativeClient/lib. This library is copied to the relevant directories when builing each app.
- The translations are built by running CliClient/build-translation.sh. You normally don't need to run this if you haven't updated the translation since the compiled files are on the repository.
- The translations are built by running CliClient/build-translation.sh. For this reasons, it's generally better to get the CLI app to build first so that everything is setup correctly.
- Note: building translations is no longer required to run the apps, so you can ignore all the below requirements about gettext.
## macOS dependencies
brew install yarn node
echo 'export PATH="/usr/local/opt/gettext/bin:$PATH"' >> ~/.bash_profile
source ~/.bash_profile
If you get a node-gyp related error you might need to manually install it: `npm install -g node-gyp`
brew install yarn node xgettext
npm install -g node-gyp
echo 'export PATH="/usr/local/opt/gettext/bin:$PATH"' >> ~/.bash_profile
source ~/.bash_profile
## Linux and Windows (WSL) dependencies
## Linux and Windows dependencies
- Install yarn - https://yarnpkg.com/lang/en/docs/install/
- Install node v8.x (check with `node --version`) - https://nodejs.org/en/
- If you get a node-gyp related error you might need to manually install it: `npm install -g node-gyp`
# Building the Electron application
@@ -38,8 +37,4 @@ From `/ReactNativeClient`, run `npm install`, then `react-native run-ios` or `re
# Building the Terminal application
From `/CliClient`:
- Run `npm install`
- Then `build.sh`
- Copy the translations to the build directory: `rsync -aP ../ReactNativeClient/locales/ build/locales/`
- Run `run.sh` to start the application for testing.
From `/CliClient`, run `npm install` then run `run.sh`. If you get an error about `xgettext`, comment out the command `node build-translation.js --silent` in build.sh

View File

@@ -177,33 +177,22 @@ class Application extends BaseApplication {
await doExit();
}
commands(uiType = null) {
if (!this.allCommandsLoaded_) {
fs.readdirSync(__dirname).forEach((path) => {
if (path.indexOf('command-') !== 0) return;
const ext = fileExtension(path)
if (ext != 'js') return;
commands() {
if (this.allCommandsLoaded_) return this.commands_;
let CommandClass = require('./' + path);
let cmd = new CommandClass();
if (!cmd.enabled()) return;
cmd = this.setupCommand(cmd);
this.commands_[cmd.name()] = cmd;
});
fs.readdirSync(__dirname).forEach((path) => {
if (path.indexOf('command-') !== 0) return;
const ext = fileExtension(path)
if (ext != 'js') return;
this.allCommandsLoaded_ = true;
}
let CommandClass = require('./' + path);
let cmd = new CommandClass();
if (!cmd.enabled()) return;
cmd = this.setupCommand(cmd);
this.commands_[cmd.name()] = cmd;
});
if (uiType !== null) {
let temp = [];
for (let n in this.commands_) {
if (!this.commands_.hasOwnProperty(n)) continue;
const c = this.commands_[n];
if (!c.supportsUi(uiType)) continue;
temp[n] = c;
}
return temp;
}
this.allCommandsLoaded_ = true;
return this.commands_;
}
@@ -257,13 +246,9 @@ class Application extends BaseApplication {
try {
CommandClass = require(__dirname + '/command-' + name + '.js');
} catch (error) {
if (error.message && error.message.indexOf('Cannot find module') >= 0) {
let e = new Error(_('No such command: %s', name));
e.type = 'notFound';
throw e;
} else {
throw error;
}
let e = new Error('No such command: ' + name);
e.type = 'notFound';
throw e;
}
let cmd = new CommandClass();
@@ -321,8 +306,6 @@ class Application extends BaseApplication {
if (argv.length) {
this.gui_ = this.dummyGui();
this.currentFolder_ = await Folder.load(Setting.value('activeFolderId'));
try {
await this.execCommand(argv);
} catch (error) {

View File

@@ -12,10 +12,6 @@ class Command extends BaseCommand {
return _('Exits the application.');
}
compatibleUis() {
return ['gui'];
}
async action(args) {
await app().exit();
}

View File

@@ -18,7 +18,7 @@ class Command extends BaseCommand {
}
allCommands() {
const commands = app().commands(app().uiType());
const commands = app().commands();
let output = [];
for (let n in commands) {
if (!commands.hasOwnProperty(n)) continue;
@@ -69,8 +69,6 @@ class Command extends BaseCommand {
this.stdout('');
this.stdout(_('The possible commands are:'));
this.stdout('');
this.stdout(_('Type `help all` for the complete help of all the commands.'));
this.stdout('');
this.stdout(commandNames.join(', '));
this.stdout('');
this.stdout(_('In any command, a note or notebook can be refered to by title or ID, or using the shortcuts `$n` or `$b` for, respectively, the currently selected note or notebook. `$c` can be used to refer to the currently selected item.'));

View File

@@ -2,7 +2,6 @@ const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const { BaseModel } = require('lib/base-model.js');
const { Database } = require('lib/database.js');
const { Folder } = require('lib/models/folder.js');
const { Note } = require('lib/models/note.js');
const { BaseItem } = require('lib/models/base-item.js');
@@ -13,16 +12,16 @@ class Command extends BaseCommand {
return 'set <note> <name> [value]';
}
description() {
const fields = Note.fields();
const s = [];
for (let i = 0; i < fields.length; i++) {
const f = fields[i];
if (f.name === 'id') continue;
s.push(f.name + ' (' + Database.enumName('fieldType', f.type) + ')');
}
enabled() {
return false;
}
return _('Sets the property <name> of the given <note> to the given [value]. Possible properties are:\n\n%s', s.join(', '));
description() {
return _('Sets the property <name> of the given <note> to the given [value].');
}
hidden() {
return true;
}
async action(args) {

View File

@@ -32,6 +32,7 @@ class Command extends BaseCommand {
options() {
return [
['--target <target>', _('Sync to provided target (defaults to sync.target config value)')],
['--random-failures', 'For debugging purposes. Do not use.'],
];
}
@@ -139,6 +140,7 @@ class Command extends BaseCommand {
cliUtils.redrawDone();
this.stdout(msg);
},
randomFailures: args.options['random-failures'] === true,
};
this.stdout(_('Synchronisation target: %s (%s)', Setting.enumOptionLabel('sync.target', this.syncTargetId_), this.syncTargetId_));

View File

@@ -18,8 +18,8 @@ class Command extends BaseCommand {
return { data: autocompleteFolders };
}
compatibleUis() {
return ['cli'];
enabled() {
return false;
}
async action(args) {

File diff suppressed because it is too large Load Diff

View File

@@ -100,10 +100,6 @@ msgstr ""
msgid "Cancelling background synchronisation... Please wait."
msgstr ""
#, javascript-format
msgid "No such command: %s"
msgstr ""
#, javascript-format
msgid "The command \"%s\" is only available in GUI mode"
msgstr ""
@@ -512,9 +508,6 @@ msgstr ""
msgid "Tools"
msgstr ""
msgid "Synchronisation status"
msgstr ""
msgid "Options"
msgstr ""
@@ -575,12 +568,6 @@ msgstr ""
msgid "Layout"
msgstr ""
msgid "Some items cannot be synchronised."
msgstr ""
msgid "View them now"
msgstr ""
msgid "Add or remove tags"
msgstr ""
@@ -618,9 +605,6 @@ msgstr ""
msgid "Import"
msgstr ""
msgid "Synchronisation Status"
msgstr ""
msgid "Delete notebook?"
msgstr ""
@@ -828,13 +812,6 @@ msgstr ""
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr ""
msgid "Items that cannot be synchronised"
msgstr ""
#, javascript-format
msgid "\"%s\": \"%s\""
msgstr ""
msgid "Sync status (synced items / total items)"
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@@ -106,10 +106,6 @@ msgstr ""
msgid "Cancelling background synchronisation... Please wait."
msgstr "Cancelando sincronización en segundo plano... Por favor espere."
#, fuzzy, javascript-format
msgid "No such command: %s"
msgstr "Comando inválido: \"%s\""
#, javascript-format
msgid "The command \"%s\" is only available in GUI mode"
msgstr "El comando \"%s\" solo está disponible en el modo gráfico (GUI)"
@@ -557,10 +553,6 @@ msgstr "Buscar en todas la notas"
msgid "Tools"
msgstr "Herramientas"
#, fuzzy
msgid "Synchronisation status"
msgstr "Objetivo de sincronización"
msgid "Options"
msgstr "Opciones"
@@ -624,13 +616,6 @@ msgstr "Establecer alarma"
msgid "Layout"
msgstr "Plantilla"
#, fuzzy
msgid "Some items cannot be synchronised."
msgstr "No se puede iniciar la sincronización."
msgid "View them now"
msgstr ""
msgid "Add or remove tags"
msgstr "Añadir o eliminar etiquetas"
@@ -669,10 +654,6 @@ msgstr "Inicio de sesión de OneDrive"
msgid "Import"
msgstr "Importar"
#, fuzzy
msgid "Synchronisation Status"
msgstr "Objetivo de sincronización"
msgid "Delete notebook?"
msgstr "¿Eliminar cuaderno?"
@@ -888,13 +869,6 @@ msgstr ""
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Valor inválido: \"%s\". Posibles valores: %s."
msgid "Items that cannot be synchronised"
msgstr ""
#, javascript-format
msgid "\"%s\": \"%s\""
msgstr ""
msgid "Sync status (synced items / total items)"
msgstr ""
"Estado de sincronización (elementos sincronizados / total de elementos)"

View File

@@ -100,10 +100,6 @@ msgstr "o"
msgid "Cancelling background synchronisation... Please wait."
msgstr "Annulation de la synchronisation... Veuillez patienter."
#, fuzzy, javascript-format
msgid "No such command: %s"
msgstr "Commande invalide : \"%s\""
#, javascript-format
msgid "The command \"%s\" is only available in GUI mode"
msgstr ""
@@ -562,10 +558,6 @@ msgstr "Chercher dans toutes les notes"
msgid "Tools"
msgstr "Outils"
#, fuzzy
msgid "Synchronisation status"
msgstr "Cible de la synchronisation"
msgid "Options"
msgstr "Options"
@@ -629,13 +621,6 @@ msgstr "Définir ou modifier alarme"
msgid "Layout"
msgstr "Disposition"
#, fuzzy
msgid "Some items cannot be synchronised."
msgstr "Impossible d'initialiser la synchronisation."
msgid "View them now"
msgstr ""
msgid "Add or remove tags"
msgstr "Gérer les étiquettes"
@@ -675,10 +660,6 @@ msgstr "Connexion OneDrive"
msgid "Import"
msgstr "Importer"
#, fuzzy
msgid "Synchronisation Status"
msgstr "Cible de la synchronisation"
msgid "Delete notebook?"
msgstr "Supprimer le carnet ?"
@@ -894,13 +875,6 @@ msgstr ""
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Option invalide: \"%s\". Les valeurs possibles sont : %s."
msgid "Items that cannot be synchronised"
msgstr ""
#, javascript-format
msgid "\"%s\": \"%s\""
msgstr ""
msgid "Sync status (synced items / total items)"
msgstr "Status de la synchronisation (objets synchro. / total)"
@@ -1104,6 +1078,10 @@ msgstr "Bienvenue"
#~ msgid "Show/Hide the console"
#~ msgstr "Quitter le logiciel."
#, fuzzy
#~ msgid "Last command: %s"
#~ msgstr "Commande invalide : \"%s\""
#~ msgid "Done editing."
#~ msgstr "Edition terminée."

View File

@@ -100,10 +100,6 @@ msgstr ""
msgid "Cancelling background synchronisation... Please wait."
msgstr ""
#, javascript-format
msgid "No such command: %s"
msgstr ""
#, javascript-format
msgid "The command \"%s\" is only available in GUI mode"
msgstr ""
@@ -512,9 +508,6 @@ msgstr ""
msgid "Tools"
msgstr ""
msgid "Synchronisation status"
msgstr ""
msgid "Options"
msgstr ""
@@ -575,12 +568,6 @@ msgstr ""
msgid "Layout"
msgstr ""
msgid "Some items cannot be synchronised."
msgstr ""
msgid "View them now"
msgstr ""
msgid "Add or remove tags"
msgstr ""
@@ -618,9 +605,6 @@ msgstr ""
msgid "Import"
msgstr ""
msgid "Synchronisation Status"
msgstr ""
msgid "Delete notebook?"
msgstr ""
@@ -828,13 +812,6 @@ msgstr ""
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr ""
msgid "Items that cannot be synchronised"
msgstr ""
#, javascript-format
msgid "\"%s\": \"%s\""
msgstr ""
msgid "Sync status (synced items / total items)"
msgstr ""

View File

@@ -1,6 +1,6 @@
{
"name": "joplin",
"version": "0.10.79",
"version": "0.10.77",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -18,7 +18,7 @@
],
"owner": "Laurent Cozic"
},
"version": "0.10.79",
"version": "0.10.77",
"bin": {
"joplin": "./main.js"
},

View File

@@ -629,32 +629,4 @@ describe('Synchronizer', function() {
done();
});
it('items should skip items that cannot be synced', async (done) => {
let folder1 = await Folder.save({ title: "folder1" });
let note1 = await Note.save({ title: "un", is_todo: 1, parent_id: folder1.id });
const noteId = note1.id;
await synchronizer().start();
let disabledItems = await BaseItem.syncDisabledItems();
expect(disabledItems.length).toBe(0);
await Note.save({ id: noteId, title: "un mod", });
synchronizer().debugFlags_ = ['cannotSync'];
await synchronizer().start();
synchronizer().debugFlags_ = [];
await synchronizer().start(); // Another sync to check that this item is now excluded from sync
await switchClient(2);
await synchronizer().start();
let notes = await Note.all();
expect(notes.length).toBe(1);
expect(notes[0].title).toBe('un');
await switchClient(1);
disabledItems = await BaseItem.syncDisabledItems();
expect(disabledItems.length).toBe(1);
done();
});
});

View File

@@ -43,9 +43,8 @@ const syncDir = __dirname + '/../tests/sync';
const sleepTime = syncTargetId_ == SyncTargetRegistry.nameToId('filesystem') ? 1001 : 400;
const logger = new Logger();
logger.addTarget('console');
logger.addTarget('file', { path: logDir + '/log.txt' });
logger.setLevel(Logger.LEVEL_WARN);
logger.setLevel(Logger.LEVEL_DEBUG);
BaseItem.loadClass('Note', Note);
BaseItem.loadClass('Folder', Folder);

View File

@@ -259,14 +259,6 @@ class Application extends BaseApplication {
}, {
label: _('Tools'),
submenu: [{
label: _('Synchronisation status'),
click: () => {
this.dispatch({
type: 'NAV_GO',
routeName: 'Status',
});
}
},{
label: _('Options'),
click: () => {
this.dispatch({

View File

@@ -39,22 +39,10 @@ class Bridge {
return this.window().setSize(width, height);
}
showSaveDialog(options) {
const {dialog} = require('electron');
if (!options) options = {};
if (!('defaultPath' in options) && this.lastSelectedPath_) options.defaultPath = this.lastSelectedPath_;
const filePath = dialog.showSaveDialog(options);
if (filePath) {
this.lastSelectedPath_ = filePath;
}
return filePath;
}
showOpenDialog(options) {
const {dialog} = require('electron');
if (!options) options = {};
if (!('defaultPath' in options) && this.lastSelectedPath_) options.defaultPath = this.lastSelectedPath_;
if (!('createDirectory' in options)) options.createDirectory = true;
const filePaths = dialog.showOpenDialog(options);
if (filePaths && filePaths.length) {
this.lastSelectedPath_ = dirname(filePaths[0]);
@@ -83,15 +71,6 @@ class Bridge {
return result === 0;
}
showInfoMessageBox(message) {
const result = this.showMessageBox({
type: 'info',
message: message,
buttons: [_('OK')],
});
return result === 0;
}
get Menu() {
return require('electron').Menu;
}

View File

@@ -9,18 +9,6 @@ const { _ } = require('lib/locale.js');
class ConfigScreenComponent extends React.Component {
constructor() {
super();
this.state = {
settings: {},
};
}
componentWillMount() {
this.setState({ settings: this.props.settings });
}
settingToComponent(key, value) {
const theme = themeStyle(this.props.theme);
@@ -40,9 +28,7 @@ class ConfigScreenComponent extends React.Component {
};
const updateSettingValue = (key, value) => {
const settings = Object.assign({}, this.state.settings);
settings[key] = value;
this.setState({ settings: settings });
Setting.setValue(key, value);
}
// Component key needs to be key+value otherwise it doesn't update when the settings change.
@@ -66,53 +52,22 @@ class ConfigScreenComponent extends React.Component {
</div>
);
} else if (md.type === Setting.TYPE_BOOL) {
const onCheckboxClick = (event) => {
updateSettingValue(key, !value)
}
return (
<div key={key+value} style={rowStyle}>
<div style={controlStyle}>
<input id={'setting_checkbox_' + key} type="checkbox" defaultChecked={!!value} onChange={(event) => { onCheckboxClick(event) }}/><label onClick={(event) => { onCheckboxClick(event) }} style={labelStyle} htmlFor={'setting_checkbox_' + key}>{md.label()}</label>
<label><input type="checkbox" defaultChecked={!!value} onChange={(event) => { updateSettingValue(key, !!event.target.checked) }}/><span style={labelStyle}> {md.label()}</span></label>
</div>
</div>
);
} else if (md.type === Setting.TYPE_STRING) {
const onTextChange = (event) => {
const settings = Object.assign({}, this.state.settings);
settings[key] = event.target.value;
this.setState({ settings: settings });
}
return (
<div key={key+value} style={rowStyle}>
<div style={labelStyle}><label>{md.label()}</label></div>
<input type="text" style={controlStyle} value={this.state.settings[key]} onChange={(event) => {onTextChange(event)}} />
</div>
);
} else {
console.warn('Type not implemented: ' + key);
}
return output;
}
onSaveClick() {
for (let n in this.state.settings) {
if (!this.state.settings.hasOwnProperty(n)) continue;
Setting.setValue(n, this.state.settings[n]);
}
this.props.dispatch({ type: 'NAV_BACK' });
}
onCancelClick() {
this.props.dispatch({ type: 'NAV_BACK' });
}
render() {
const theme = themeStyle(this.props.theme);
const style = this.props.style;
const settings = this.state.settings;
const settings = this.props.settings;
const headerStyle = {
width: style.width,
@@ -122,21 +77,15 @@ class ConfigScreenComponent extends React.Component {
padding: 10,
};
const buttonStyle = {
display: this.state.settings === this.props.settings ? 'none' : 'inline-block',
marginRight: 10,
}
let settingComps = [];
let keys = Setting.keys(true, 'desktop');
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (key === 'sync.target') continue;
if (!(key in settings)) {
console.warn('Missing setting: ' + key);
continue;
}
const md = Setting.settingMetadata(key);
if (md.show && !md.show(settings)) continue;
const comp = this.settingToComponent(key, settings[key]);
if (!comp) continue;
settingComps.push(comp);
@@ -147,8 +96,6 @@ class ConfigScreenComponent extends React.Component {
<Header style={headerStyle} />
<div style={containerStyle}>
{ settingComps }
<button onClick={() => {this.onSaveClick()}} style={buttonStyle}>{_('Save')}</button>
<button onClick={() => {this.onCancelClick()}} style={buttonStyle}>{_('Cancel')}</button>
</div>
</div>
);

View File

@@ -41,24 +41,19 @@ class ImportScreenComponent extends React.Component {
const messages = this.state.messages.slice();
let found = false;
messages.push({ key: key, text: text });
for (let i = 0; i < messages.length; i++) {
if (messages[i].key === key) {
messages[i].text = text;
found = true;
break;
}
}
if (!found) messages.push({ key: key, text: text });
this.setState({ messages: messages });
}
uniqueMessages() {
let output = [];
const messages = this.state.messages.slice();
let foundKeys = [];
for (let i = messages.length - 1; i >= 0; i--) {
const msg = messages[i];
if (foundKeys.indexOf(msg.key) >= 0) continue;
foundKeys.push(msg.key);
output.unshift(msg);
}
return output;
}
async doImport() {
const filePath = this.props.filePath;
const folderTitle = await Folder.findUniqueFolderTitle(filename(filePath));
@@ -82,9 +77,10 @@ class ImportScreenComponent extends React.Component {
this.addMessage('progress', lastProgress);
},
onError: (error) => {
// Don't display the error directly because most of the time it doesn't matter
// (eg. for weird broken HTML, but the note is still imported)
console.warn('When importing ENEX file', error);
const messages = this.state.messages.slice();
let s = error.trace ? error.trace : error.toString();
messages.push({ key: 'error_' + (progressCount++), text: s });
this.addMessage('error_' + (progressCount++), lastProgress);
},
}
@@ -99,7 +95,7 @@ class ImportScreenComponent extends React.Component {
render() {
const theme = themeStyle(this.props.theme);
const style = this.props.style;
const messages = this.uniqueMessages();
const messages = this.state.messages;
const messagesStyle = {
padding: 10,

View File

@@ -229,8 +229,8 @@ class MainScreenComponent extends React.Component {
}
}
styles(themeId, width, height, messageBoxVisible) {
const styleKey = themeId + '_' + width + '_' + height + '_' + messageBoxVisible;
styles(themeId, width, height) {
const styleKey = themeId + '_' + width + '_' + height;
if (styleKey === this.styleKey_) return this.styles_;
const theme = themeStyle(themeId);
@@ -239,21 +239,12 @@ class MainScreenComponent extends React.Component {
this.styles_ = {};
const rowHeight = height - theme.headerHeight;
this.styles_.header = {
width: width,
};
this.styles_.messageBox = {
width: width,
height: 30,
display: 'flex',
alignItems: 'center',
paddingLeft: 10,
backgroundColor: theme.warningBackgroundColor,
}
const rowHeight = height - theme.headerHeight - (messageBoxVisible ? this.styles_.messageBox.height : 0);
this.styles_.sideBar = {
width: Math.floor(layoutUtils.size(width * .2, 150, 300)),
height: rowHeight,
@@ -288,10 +279,8 @@ class MainScreenComponent extends React.Component {
const promptOptions = this.state.promptOptions;
const folders = this.props.folders;
const notes = this.props.notes;
const messageBoxVisible = this.props.hasDisabledSyncItems;
const styles = this.styles(this.props.theme, style.width, style.height, messageBoxVisible);
const theme = themeStyle(this.props.theme);
const styles = this.styles(this.props.theme, style.width, style.height);
const headerButtons = [];
@@ -336,21 +325,6 @@ class MainScreenComponent extends React.Component {
}
}
const onViewDisabledItemsClick = () => {
this.props.dispatch({
type: 'NAV_GO',
routeName: 'Status',
});
}
const messageComp = messageBoxVisible ? (
<div style={styles.messageBox}>
<span style={theme.textStyle}>
{_('Some items cannot be synchronised.')} <a href="#" onClick={() => { onViewDisabledItemsClick() }}>{_('View them now')}</a>
</span>
</div>
) : null;
return (
<div style={style}>
<PromptDialog
@@ -365,7 +339,6 @@ class MainScreenComponent extends React.Component {
buttons={promptOptions && ('buttons' in promptOptions) ? promptOptions.buttons : null}
inputType={promptOptions && ('inputType' in promptOptions) ? promptOptions.inputType : null} />
<Header style={styles.header} showBackButton={false} buttons={headerButtons} />
{messageComp}
<SideBar style={styles.sideBar} />
<NoteList style={styles.noteList} />
<NoteText style={styles.noteText} visiblePanes={this.props.noteVisiblePanes} />
@@ -382,7 +355,6 @@ const mapStateToProps = (state) => {
noteVisiblePanes: state.noteVisiblePanes,
folders: state.folders,
notes: state.notes,
hasDisabledSyncItems: state.hasDisabledSyncItems,
};
};

View File

@@ -174,7 +174,7 @@ class NoteListComponent extends React.Component {
}, style);
emptyDivStyle.width = emptyDivStyle.width - padding * 2;
emptyDivStyle.height = emptyDivStyle.height - padding * 2;
return <div style={emptyDivStyle}>{ this.props.folders.length ? _('No notes in here. Create one by clicking on "New note".') : _('There is currently no notebook. Create one by clicking on "New notebook".')}</div>
return <div style={emptyDivStyle}>{_('No notes in here. Create one by clicking on "New note".')}</div>
}
return (
@@ -193,7 +193,6 @@ class NoteListComponent extends React.Component {
const mapStateToProps = (state) => {
return {
notes: state.notes,
folders: state.folders,
selectedNoteIds: state.selectedNoteIds,
theme: state.settings.theme,
// uncompletedTodosOnTop: state.settings.uncompletedTodosOnTop,

View File

@@ -8,7 +8,6 @@ const { Setting } = require('lib/models/setting.js');
const { MainScreen } = require('./MainScreen.min.js');
const { OneDriveLoginScreen } = require('./OneDriveLoginScreen.min.js');
const { StatusScreen } = require('./StatusScreen.min.js');
const { ImportScreen } = require('./ImportScreen.min.js');
const { ConfigScreen } = require('./ConfigScreen.min.js');
const { Navigator } = require('./Navigator.min.js');
@@ -76,7 +75,6 @@ class RootComponent extends React.Component {
OneDriveLogin: { screen: OneDriveLoginScreen, title: () => _('OneDrive Login') },
Import: { screen: ImportScreen, title: () => _('Import') },
Config: { screen: ConfigScreen, title: () => _('Options') },
Status: { screen: StatusScreen, title: () => _('Synchronisation Status') },
};
return (

View File

@@ -1,135 +0,0 @@
const React = require('react');
const { connect } = require('react-redux');
const { reg } = require('lib/registry.js');
const { Setting } = require('lib/models/setting.js');
const { bridge } = require('electron').remote.require('./bridge');
const { Header } = require('./Header.min.js');
const { themeStyle } = require('../theme.js');
const { _ } = require('lib/locale.js');
const { ReportService } = require('lib/services/report.js');
const fs = require('fs-extra');
class StatusScreenComponent extends React.Component {
constructor() {
super();
this.state = {
report: [],
};
}
componentWillMount() {
this.resfreshScreen();
}
async resfreshScreen() {
const service = new ReportService();
const report = await service.status(Setting.value('sync.target'));
this.setState({ report: report });
}
async exportDebugReportClick() {
const filename = 'syncReport-' + (new Date()).getTime() + '.csv';
const filePath = bridge().showSaveDialog({
title: _('Please select where the sync status should be exported to'),
defaultPath: filename,
});
if (!filePath) return;
const service = new ReportService();
const csv = await service.basicItemList({ format: 'csv' });
await fs.writeFileSync(filePath, csv);
}
render() {
const theme = themeStyle(this.props.theme);
const style = this.props.style;
const headerStyle = {
width: style.width,
};
const containerPadding = 10;
const containerStyle = {
padding: containerPadding,
overflowY: 'auto',
height: style.height - theme.headerHeight - containerPadding * 2,
};
function renderSectionTitleHtml(key, title) {
return <h2 key={'section_' + key} style={theme.h2Style}>{title}</h2>
}
function renderSectionHtml(key, section) {
let itemsHtml = [];
itemsHtml.push(renderSectionTitleHtml(section.title, section.title));
for (let n in section.body) {
if (!section.body.hasOwnProperty(n)) continue;
itemsHtml.push(<div style={theme.textStyle} key={'item_' + n}>{section.body[n]}</div>);
}
return (
<div key={key}>
{itemsHtml}
</div>
);
}
function renderBodyHtml(report) {
let output = [];
let baseStyle = {
paddingLeft: 6,
paddingRight: 6,
paddingTop: 2,
paddingBottom: 2,
flex: 0,
color: theme.color,
fontSize: theme.fontSize,
};
let sectionsHtml = [];
for (let i = 0; i < report.length; i++) {
let section = report[i];
if (!section.body.length) continue;
sectionsHtml.push(renderSectionHtml(i, section));
}
return (
<div>
{sectionsHtml}
</div>
);
}
let body = renderBodyHtml(this.state.report);
return (
<div style={style}>
<Header style={headerStyle} />
<div style={containerStyle}>
<a style={theme.textStyle} onClick={() => this.exportDebugReportClick()}href="#">Export debug report</a>
{body}
</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
theme: state.settings.theme,
settings: state.settings,
locale: state.settings.locale,
};
};
const StatusScreen = connect(mapStateToProps)(StatusScreenComponent);
module.exports = { StatusScreen };

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,5 @@
var locales = {};
locales['en_GB'] = require('./en_GB.json');
locales['de_DE'] = require('./de_DE.json');
locales['es_CR'] = require('./es_CR.json');
locales['fr_FR'] = require('./fr_FR.json');
module.exports = { locales: locales };

View File

@@ -1,6 +1,6 @@
{
"name": "Joplin",
"version": "0.10.37",
"version": "0.10.36",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "Joplin",
"version": "0.10.37",
"version": "0.10.36",
"description": "Joplin for Desktop",
"main": "main.js",
"scripts": {
@@ -25,10 +25,6 @@
"win": {
"icon": "../../Assets/Joplin.ico"
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true
},
"mac": {
"icon": "../../Assets/macOs.icns",
"asar": false

View File

@@ -9,19 +9,6 @@ body, textarea {
overflow: hidden;
}
table {
border-collapse: collapse;
}
table th {
text-align: left;
}
table td, table th {
padding: .5em;
border: 1px solid #ccc;
}
/* By default, the Ice Editor displays invalid characters, such as non-breaking spaces
as red boxes, but since those are actually valid characters and common in imported
Evernote data, we hide them here. */

View File

@@ -25,8 +25,6 @@ const globalStyle = {
selectedColor2: "#5A4D70",
colorError2: "#ff6c6c",
warningBackgroundColor: "#FFD08D",
headerHeight: 35,
headerButtonHPadding: 6,
@@ -71,9 +69,6 @@ globalStyle.textStyle2 = Object.assign({}, globalStyle.textStyle, {
color: globalStyle.color2,
});
globalStyle.h2Style = Object.assign({}, globalStyle.textStyle);
globalStyle.h2Style.fontSize *= 1.3;
let themeCache_ = {};
function themeStyle(theme) {

View File

@@ -18,9 +18,9 @@ Three types of applications are available: for the **desktop** (Windows, macOS a
Operating System | Download
-----------------|--------
Windows | <a href='https://github.com/laurent22/joplin/releases/download/v0.10.37/Joplin-Setup-0.10.37.exe'><img alt='Get it on Windows' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeWindows.png'/></a>
macOS | <a href='https://github.com/laurent22/joplin/releases/download/v0.10.37/Joplin-0.10.37.dmg'><img alt='Get it on macOS' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeMacOS.png'/></a>
Linux | <a href='https://github.com/laurent22/joplin/releases/download/v0.10.37/Joplin-0.10.37-x86_64.AppImage'><img alt='Get it on macOS' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeLinux.png'/></a>
Windows | <a href='https://github.com/laurent22/joplin/releases/download/v0.10.36/Joplin-Setup-0.10.36.exe'><img alt='Get it on Windows' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeWindows.png'/></a>
macOS | <a href='https://github.com/laurent22/joplin/releases/download/v0.10.36/Joplin-0.10.36.dmg'><img alt='Get it on macOS' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeMacOS.png'/></a>
Linux | <a href='https://github.com/laurent22/joplin/releases/download/v0.10.36/Joplin-0.10.36-x86_64.AppImage'><img alt='Get it on macOS' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeLinux.png'/></a>
## Mobile applications

View File

@@ -137,25 +137,6 @@ Since this is still an actual URL, the terminal will still make it clickable. An
In Markdown, links to resources are represented as a simple ID to the resource. In order to give access to these resources, they will be, like links, converted to local URLs. Clicking this link will then open a browser, which will handle the file - i.e. display the image, open the PDF file, etc.
# Shell mode
Commands can also be used directly from a shell. To view the list of available commands, type `joplin help all`. To reference a note, notebook or tag you can either use the ID (type `joplin ls -l` to view the ID) or by title.
For example, this will create a new note "My note" in the notebook "My notebook":
$ joplin mkbook "My notebook"
$ joplin use "My notebook"
$ joplin mknote "My note"
To view the newly created note:
$ joplin ls -l
fe889 07/12/2017 17:57 My note
Give a new title to the note:
$ joplin set fe889 title "New title"
# Available shortcuts
There are two types of shortcuts: those that manipulate the user interface directly, such as `TAB` to move from one pane to another, and those that are simply shortcuts to actual commands. In a way similar to Vim, these shortcuts are generally a verb followed by an object. For example, typing `mn` ([m]ake [n]ote), is used to create a new note: it will switch the interface to command line mode and pre-fill it with `mknote ""` from where the title of the note can be entered. See below for the full list of shortcuts:

View File

@@ -17,11 +17,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.10.5</string>
<string>0.10.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>5</string>
<string>3</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>

View File

@@ -66,15 +66,10 @@ class BaseApplication {
}
switchCurrentFolder(folder) {
if (!this.hasGui()) {
this.currentFolder_ = Object.assign({}, folder);
Setting.setValue('activeFolderId', folder ? folder.id : '');
} else {
this.dispatch({
type: 'FOLDER_SELECT',
id: folder ? folder.id : '',
});
}
this.dispatch({
type: 'FOLDER_SELECT',
id: folder ? folder.id : '',
});
}
// Handles the initial flags passed to main script and
@@ -232,10 +227,6 @@ class BaseApplication {
return false;
}
uiType() {
return this.hasGui() ? 'gui' : 'cli';
}
generalMiddlewareFn() {
const middleware = store => next => (action) => {
return this.generalMiddleware(store, next, action);

View File

@@ -100,7 +100,7 @@ class MdToHtml {
const href = this.getAttr_(attrs, 'src');
if (!Resource.isResourceUrl(href)) {
return '<img title="' + htmlentities(title) + '" src="' + href + '"/>';
return '<span>' + href + '</span><img title="' + htmlentities(title) + '" src="' + href + '"/>';
}
const resourceId = Resource.urlToId(href);

View File

@@ -1,6 +1,6 @@
const React = require('react'); const Component = React.Component;
const { connect } = require('react-redux');
const { Platform, View, Text, Button, StyleSheet, TouchableOpacity, Image, ScrollView, Dimensions } = require('react-native');
const { Platform, View, Text, Button, StyleSheet, TouchableOpacity, Image } = require('react-native');
const Icon = require('react-native-vector-icons/Ionicons').default;
const { Log } = require('lib/log.js');
const { BackButtonService } = require('lib/services/back-button.js');
@@ -218,7 +218,7 @@ class ScreenHeaderComponent extends Component {
const itemListCsv = await service.basicItemList({ format: 'csv' });
const filePath = RNFS.ExternalDirectoryPath + '/syncReport-' + (new Date()).getTime() + '.txt';
const finalText = [logItemCsv, itemListCsv].join("\n================================================================================\n");
const finalText = [logItemCsv, itemListCsv].join("\n--------------------------------------------------------------------------------");
await RNFS.writeFile(filePath, finalText);
alert('Debug report exported to ' + filePath);
@@ -410,7 +410,6 @@ class ScreenHeaderComponent extends Component {
const backButtonComp = backButton(this.styles(), () => this.backButton_press(), !this.props.historyCanGoBack);
const searchButtonComp = this.props.noteSelectionEnabled ? null : searchButton(this.styles(), () => this.searchButton_press());
const deleteButtonComp = this.props.noteSelectionEnabled ? deleteButton(this.styles(), () => this.deleteButton_press()) : null;
const windowHeight = Dimensions.get('window').height - 50;
const menuComp = (
<Menu onSelect={(value) => this.menu_select(value)} style={this.styles().contextMenu}>
@@ -418,9 +417,7 @@ class ScreenHeaderComponent extends Component {
<Text style={this.styles().contextMenuTrigger}> &#8942;</Text>
</MenuTrigger>
<MenuOptions>
<ScrollView style={{ maxHeight: windowHeight }}>
{ menuOptionComponents }
</ScrollView>
{ menuOptionComponents }
</MenuOptions>
</Menu>
);

View File

@@ -29,11 +29,9 @@ shared.saveNoteButton_press = async function(comp) {
}
let isNew = !note.id;
let titleWasAutoAssigned = false;
if (isNew && !note.title) {
note.title = Note.defaultTitle(note);
titleWasAutoAssigned = true;
}
// Save only the properties that have changed
@@ -56,11 +54,8 @@ shared.saveNoteButton_press = async function(comp) {
// But we preserve the current title and body because
// the user might have changed them between the time
// saveNoteButton_press was called and the note was
// saved (it's done asynchronously).
//
// If the title was auto-assigned above, we don't restore
// it from the state because it will be empty there.
if (!titleWasAutoAssigned) note.title = stateNote.title;
// saved (it's done asynchronously)
note.title = stateNote.title;
note.body = stateNote.body;
}

View File

@@ -165,16 +165,6 @@ class Database {
throw new Error('Unknown enum type or value: ' + type + ', ' + s);
}
static enumName(type, id) {
if (type === 'fieldType') {
if (id === Database.TYPE_UNKNOWN) return 'unknown';
if (id === Database.TYPE_INT) return 'int';
if (id === Database.TYPE_TEXT) return 'text';
if (id === Database.TYPE_NUMERIC) return 'numeric';
throw new Error('Invalid type id: ' + id);
}
}
static formatValue(type, value) {
if (value === null || value === undefined) return null;
if (type == this.TYPE_INT) return Number(value);

View File

@@ -123,27 +123,15 @@ class FileApiDriverOneDrive {
return this.makeItem_(item);
}
async put(path, content, options = null) {
put(path, content, options = null) {
if (!options) options = {};
let response = null;
try {
if (options.source == 'file') {
response = await this.api_.exec('PUT', this.makePath_(path) + ':/content', null, null, options);
} else {
options.headers = { 'Content-Type': 'text/plain' };
response = await this.api_.exec('PUT', this.makePath_(path) + ':/content', null, content, options);
}
} catch (error) {
if (error && error.code === 'BadRequest' && error.message === 'Maximum request length exceeded.') {
error.code = 'cannotSync';
error.message = 'Resource exceeds OneDrive max file size (4MB)';
}
throw error;
if (options.source == 'file') {
return this.api_.exec('PUT', this.makePath_(path) + ':/content', null, null, options);
} else {
options.headers = { 'Content-Type': 'text/plain' };
return this.api_.exec('PUT', this.makePath_(path) + ':/content', null, content, options);
}
return response;
}
delete(path) {

View File

@@ -194,15 +194,11 @@ function addResourceTag(lines, resource, alt = "") {
function isBlockTag(n) {
return n=="div" || n=="p" || n=="dl" || n=="dd" || n == 'dt' || n=="center";
return n=="div" || n=="p" || n=="dl" || n=="dd" || n=="center";
}
function isStrongTag(n) {
return n == "strong" || n == "b" || n == 'big';
}
function isStrikeTag(n) {
return n == "strike" || n == "s" || n == 'del';
return n == "strong" || n == "b";
}
function isEmTag(n) {
@@ -214,7 +210,7 @@ function isAnchor(n) {
}
function isIgnoredEndTag(n) {
return n=="en-note" || n=="en-todo" || n=="span" || n=="body" || n=="html" || n=="font" || n=="br" || n=='hr' || n == 'tbody' || n == 'sup' || n == 'img' || n == 'abbr' || n == 'cite' || n == 'thead' || n == 'small' || n == 'tt' || n == 'sub';
return n=="en-note" || n=="en-todo" || n=="span" || n=="body" || n=="html" || n=="font" || n=="br" || n=='hr' || n=='s' || n == 'tbody' || n == 'sup';
}
function isListTag(n) {
@@ -223,7 +219,7 @@ function isListTag(n) {
// Elements that don't require any special treatment beside adding a newline character
function isNewLineOnlyEndTag(n) {
return n=="div" || n=="p" || n=="li" || n=="h1" || n=="h2" || n=="h3" || n=="h4" || n=="h5" || n=='h6' || n=="dl" || n=="dd" || n == 'dt' || n=="center";
return n=="div" || n=="p" || n=="li" || n=="h1" || n=="h2" || n=="h3" || n=="h4" || n=="h5" || n=="dl" || n=="dd" || n=="center";
}
function isCodeTag(n) {
@@ -257,27 +253,8 @@ function xmlNodeText(xmlNode) {
return xmlNode[0];
}
function attributeToLowerCase(node) {
if (!node.attributes) return {};
let output = {};
for (let n in node.attributes) {
if (!node.attributes.hasOwnProperty(n)) continue;
output[n.toLowerCase()] = node.attributes[n];
}
return output;
}
function enexXmlToMdArray(stream, resources) {
let remainingResources = resources.slice();
const removeRemainingResource = (id) => {
for (let i = 0; i < remainingResources.length; i++) {
const r = remainingResources[i];
if (r.id === id) {
remainingResources.splice(i, 1);
}
}
}
resources = resources.slice();
return new Promise((resolve, reject) => {
let state = {
@@ -288,7 +265,7 @@ function enexXmlToMdArray(stream, resources) {
};
let options = {};
let strict = false;
let strict = true;
var saxStream = require('sax').createStream(strict, options)
let section = {
@@ -298,18 +275,14 @@ function enexXmlToMdArray(stream, resources) {
};
saxStream.on('error', function(e) {
console.warn(e);
//reject(e);
reject(e);
})
saxStream.on('text', function(text) {
if (['table', 'tr', 'tbody'].indexOf(section.type) >= 0) return;
section.lines = collapseWhiteSpaceAndAppend(section.lines, state, text);
})
saxStream.on('opentag', function(node) {
const nodeAttributes = attributeToLowerCase(node);
let n = node.name.toLowerCase();
if (n == 'en-note') {
// Start of note
@@ -323,13 +296,10 @@ function enexXmlToMdArray(stream, resources) {
};
section.lines.push(newSection);
section = newSection;
} else if (n == 'tbody' || n == 'thead') {
} else if (n == 'tbody') {
// Ignore it
} else if (n == 'tr') {
if (section.type != 'table') {
console.warn('Found a <tr> tag outside of a table');
return;
}
if (section.type != 'table') throw new Error('Found a <tr> tag outside of a table');
let newSection = {
type: 'tr',
@@ -341,10 +311,7 @@ function enexXmlToMdArray(stream, resources) {
section.lines.push(newSection);
section = newSection;
} else if (n == 'td' || n == 'th') {
if (section.type != 'tr') {
console.warn('Found a <td> tag outside of a <tr>');
return;
}
if (section.type != 'tr') throw new Error('Found a <td> tag outside of a <tr>');
if (n == 'th') section.isHeader = true;
@@ -375,27 +342,17 @@ function enexXmlToMdArray(stream, resources) {
}
} else if (isStrongTag(n)) {
section.lines.push("**");
} else if (isStrikeTag(n)) {
section.lines.push('(');
} else if (n == 'samp') {
section.lines.push('`');
} else if (n == 's') {
// Not supported
} else if (n == 'q') {
section.lines.push('"');
} else if (n == 'img') {
// TODO: TEST IMAGE
if (nodeAttributes.src) { // Many (most?) img tags don't have no source associated, especially when they were imported from HTML
let s = '![';
if (nodeAttributes.alt) s += nodeAttributes.alt;
s += '](' + nodeAttributes.src + ')';
section.lines.push(s);
}
} else if (isAnchor(n)) {
state.anchorAttributes.push(nodeAttributes);
state.anchorAttributes.push(node.attributes);
section.lines.push('[');
} else if (isEmTag(n)) {
section.lines.push("*");
} else if (n == "en-todo") {
let x = nodeAttributes && nodeAttributes.checked && nodeAttributes.checked.toLowerCase() == 'true' ? 'X' : ' ';
let x = node.attributes && node.attributes.checked && node.attributes.checked.toLowerCase() == 'true' ? 'X' : ' ';
section.lines.push('- [' + x + '] ');
} else if (n == "hr") {
// Needs to be surrounded by new lines so that it's properly rendered as a line when converting to HTML
@@ -418,20 +375,20 @@ function enexXmlToMdArray(stream, resources) {
} else if (n == 'blockquote') {
section.lines.push(BLOCK_OPEN);
state.inQuote = true;
} else if (isCodeTag(n, nodeAttributes)) {
} else if (isCodeTag(n, node.attributes)) {
section.lines.push(BLOCK_OPEN);
state.inCode = true;
} else if (n == "br") {
section.lines.push(NEWLINE);
} else if (n == "en-media") {
const hash = nodeAttributes.hash;
const hash = node.attributes.hash;
let resource = null;
for (let i = 0; i < resources.length; i++) {
let r = resources[i];
if (r.id == hash) {
resource = r;
removeRemainingResource(r.id);
resources.splice(i, 1);
break;
}
}
@@ -473,11 +430,11 @@ function enexXmlToMdArray(stream, resources) {
// </en-export>
let found = false;
for (let i = 0; i < remainingResources.length; i++) {
let r = remainingResources[i];
for (let i = 0; i < resources.length; i++) {
let r = resources[i];
if (!r.id) {
r.id = hash;
remainingResources[i] = r;
resources[i] = r;
found = true;
break;
}
@@ -491,29 +448,27 @@ function enexXmlToMdArray(stream, resources) {
// means it's an attachement. It will be appended along with the
// other remaining resources at the bottom of the markdown text.
if (!!resource.id) {
section.lines = addResourceTag(section.lines, resource, nodeAttributes.alt);
section.lines = addResourceTag(section.lines, resource, node.attributes.alt);
}
}
} else if (n == "span" || n == "font" || n == 'sup' || n == 'cite' || n == 'abbr' || n == 'small' || n == 'tt' || n == 'sub') {
// Inline tags that can be ignored in Markdown
} else if (n == "span" || n == "font" || n == 'sup') {
// Ignore
} else {
console.warn("Unsupported start tag: " + n);
}
})
saxStream.on('closetag', function(n) {
n = n ? n.toLowerCase() : n;
if (n == 'en-note') {
// End of note
} else if (isNewLineOnlyEndTag(n)) {
section.lines.push(BLOCK_CLOSE);
} else if (n == 'td' || n == 'th') {
if (section && section.parent) section = section.parent;
section = section.parent;
} else if (n == 'tr') {
if (section && section.parent) section = section.parent;
section = section.parent;
} else if (n == 'table') {
if (section && section.parent) section = section.parent;
section = section.parent;
} else if (isIgnoredEndTag(n)) {
// Skip
} else if (isListTag(n)) {
@@ -521,10 +476,6 @@ function enexXmlToMdArray(stream, resources) {
state.lists.pop();
} else if (isStrongTag(n)) {
section.lines.push("**");
} else if (isStrikeTag(n)) {
section.lines.push(')');
} else if (n == 'samp') {
section.lines.push('`');
} else if (isEmTag(n)) {
section.lines.push("*");
} else if (n == 'q') {
@@ -541,10 +492,6 @@ function enexXmlToMdArray(stream, resources) {
if (section.lines.length < 1) throw new Error('Invalid anchor tag closing'); // Sanity check, but normally not possible
const pushEmptyAnchor = (url) => {
section.lines.push('[link](' + url + ')');
}
// When closing the anchor tag, check if there's is any text content. If not
// put the URL as is (don't wrap it in [](url)). The markdown parser, using
// GitHub flavour, will turn this URL into a link. This is to generate slightly
@@ -552,38 +499,13 @@ function enexXmlToMdArray(stream, resources) {
let previous = section.lines[section.lines.length - 1];
if (previous == '[') {
section.lines.pop();
pushEmptyAnchor(url);
section.lines.push(url);
} else if (!previous || previous == url) {
section.lines.pop();
section.lines.pop();
pushEmptyAnchor(url);
section.lines.push(url);
} else {
// Need to remove any new line character between the current ']' and the previous '['
// otherwise it won't render properly.
let allSpaces = true;
for (let i = section.lines.length - 1; i >= 0; i--) {
const c = section.lines[i];
if (c === '[') {
break;
} else {
if (c === BLOCK_CLOSE || c === BLOCK_OPEN || c === NEWLINE) {
section.lines[i] = SPACE;
} else {
if (!isWhiteSpace(c)) allSpaces = false;
}
}
}
if (allSpaces) {
for (let i = section.lines.length - 1; i >= 0; i--) {
const c = section.lines.pop();
if (c === '[') break;
}
//section.lines.push(url);
pushEmptyAnchor(url);
} else {
section.lines.push('](' + url + ')');
}
section.lines.push('](' + url + ')');
}
} else if (isListTag(n)) {
section.lines.push(BLOCK_CLOSE);
@@ -605,7 +527,7 @@ function enexXmlToMdArray(stream, resources) {
saxStream.on('end', function() {
resolve({
content: section,
resources: remainingResources,
resources: resources,
});
})
@@ -613,28 +535,50 @@ function enexXmlToMdArray(stream, resources) {
});
}
function removeTableCellNewLines(cellText) {
return cellText.replace(/\n+/g, " ");
function setTableCellContent(table) {
if (!table.type == 'table') throw new Error('Only for tables');
for (let trIndex = 0; trIndex < table.lines.length; trIndex++) {
const tr = table.lines[trIndex];
for (let tdIndex = 0; tdIndex < tr.lines.length; tdIndex++) {
let td = tr.lines[tdIndex];
td.content = processMdArrayNewLines(td.lines);
td.content = td.content.replace(/\n\n\n\n\n/g, ' ');
td.content = td.content.replace(/\n\n\n\n/g, ' ');
td.content = td.content.replace(/\n\n\n/g, ' ');
td.content = td.content.replace(/\n\n/g, ' ');
td.content = td.content.replace(/\n/g, ' ');
}
}
return table;
}
function tableHasSubTables(table) {
function cellWidth(cellText) {
const lines = cellText.split("\n");
let maxWidth = 0;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (line.length > maxWidth) maxWidth = line.length;
}
return maxWidth;
}
function colWidths(table) {
let output = [];
for (let trIndex = 0; trIndex < table.lines.length; trIndex++) {
const tr = table.lines[trIndex];
for (let tdIndex = 0; tdIndex < tr.lines.length; tdIndex++) {
const td = tr.lines[tdIndex];
for (let i = 0; i < td.lines.length; i++) {
if (typeof td.lines[i] === 'object') return true;
}
const w = cellWidth(td.content);
if (output.length <= tdIndex) output.push(0);
if (w > output[tdIndex]) output[tdIndex] = w;
}
}
return false;
return output;
}
// Markdown tables don't support tables within tables, which is common in notes that are complete web pages, for example when imported
// via Web Clipper. So to handle this, we render all the outer tables as regular text (as if replacing all the <table>, <tr> and <td>
// elements by <div>) and only the inner ones, those that don't contain any other tables, are rendered as actual tables. This is generally
// the required behaviour since the outer tables are usually for layout and the inner ones are the content.
function drawTable(table) {
function drawTable(table, colWidths) {
// | First Header | Second Header |
// | ------------- | ------------- |
// | Content Cell | Content Cell |
@@ -642,11 +586,8 @@ function drawTable(table) {
// There must be at least 3 dashes separating each header cell.
// https://gist.github.com/IanWang/28965e13cdafdef4e11dc91f578d160d#tables
const flatRender = tableHasSubTables(table); // Render the table has regular text
const minColWidth = 3;
let lines = [];
lines.push(BLOCK_OPEN);
let headerDone = false;
for (let trIndex = 0; trIndex < table.lines.length; trIndex++) {
const tr = table.lines[trIndex];
@@ -654,79 +595,37 @@ function drawTable(table) {
let line = [];
let headerLine = [];
let emptyHeader = null;
for (let tdIndex = 0; tdIndex < tr.lines.length; tdIndex++) {
const td = tr.lines[tdIndex];
for (let tdIndex = 0; tdIndex < colWidths.length; tdIndex++) {
const width = Math.max(minColWidth, colWidths[tdIndex]);
const cell = tr.lines[tdIndex] ? tr.lines[tdIndex].content : '';
line.push(stringPadding(cell, width, ' ', stringPadding.RIGHT));
if (flatRender) {
line.push(BLOCK_OPEN);
let currentCells = [];
const renderCurrentCells = () => {
if (!currentCells.length) return;
const cellText = processMdArrayNewLines(currentCells);
line.push(cellText);
currentCells = [];
if (!headerDone) {
if (!isHeader) {
if (!emptyHeader) emptyHeader = [];
let h = stringPadding(' ', width, ' ', stringPadding.RIGHT);
if (!width) h = '';
emptyHeader.push(h);
}
// In here, recursively render the tables
for (let i = 0; i < td.lines.length; i++) {
const c = td.lines[i];
if (typeof c === 'object') { // This is a table
renderCurrentCells();
currentCells = currentCells.concat(drawTable(c));
} else { // This is plain text
currentCells.push(c);
}
}
renderCurrentCells();
line.push(BLOCK_CLOSE);
} else { // Regular table rendering
// A cell in a Markdown table cannot have new lines so remove them
const cellText = removeTableCellNewLines(processMdArrayNewLines(td.lines));
const width = Math.max(cellText.length, 3);
line.push(stringPadding(cellText, width, ' ', stringPadding.RIGHT));
if (!headerDone) {
if (!isHeader) {
if (!emptyHeader) emptyHeader = [];
let h = stringPadding(' ', width, ' ', stringPadding.RIGHT);
emptyHeader.push(h);
}
headerLine.push('-'.repeat(width));
}
headerLine.push('-'.repeat(width));
}
}
if (flatRender) {
if (emptyHeader) {
lines.push('| ' + emptyHeader.join(' | ') + ' |');
lines.push('| ' + headerLine.join(' | ') + ' |');
headerDone = true;
lines.push(BLOCK_OPEN);
lines = lines.concat(line);
lines.push(BLOCK_CLOSE);
} else {
if (emptyHeader) {
lines.push('| ' + emptyHeader.join(' | ') + ' |');
lines.push('| ' + headerLine.join(' | ') + ' |');
headerDone = true;
}
}
lines.push('| ' + line.join(' | ') + ' |');
lines.push('| ' + line.join(' | ') + ' |');
if (!headerDone) {
lines.push('| ' + headerLine.join(' | ') + ' |');
headerDone = true;
}
if (!headerDone) {
lines.push('| ' + headerLine.join(' | ') + ' |');
headerDone = true;
}
}
lines.push(BLOCK_CLOSE);
return flatRender ? lines : lines.join('<<<<:D>>>>' + NEWLINE + '<<<<:D>>>>').split('<<<<:D>>>>');
return lines.join('<<<<:D>>>>' + NEWLINE + '<<<<:D>>>>').split('<<<<:D>>>>');
}
async function enexXmlToMd(stream, resources) {
@@ -737,9 +636,13 @@ async function enexXmlToMd(stream, resources) {
for (let i = 0; i < result.content.lines.length; i++) {
let line = result.content.lines[i];
if (typeof line === 'object') { // A table
const table = line;
const tableLines = drawTable(table);
let table = setTableCellContent(line);
//console.log(require('util').inspect(table, false, null))
const cw = colWidths(table);
const tableLines = drawTable(table, cw);
mdLines.push(BLOCK_OPEN);
mdLines = mdLines.concat(tableLines);
mdLines.push(BLOCK_CLOSE);
} else { // an actual line
mdLines.push(line);
}

View File

@@ -212,92 +212,51 @@ function importEnex(parentFolderId, filePath, importOptions = null) {
async function processNotes() {
if (processingNotes) return false;
try {
processingNotes = true;
stream.pause();
processingNotes = true;
stream.pause();
while (notes.length) {
let note = notes.shift();
const contentStream = stringToStream(note.bodyXml);
const body = await enexXmlToMd(contentStream, note.resources);
delete note.bodyXml;
let chain = [];
while (notes.length) {
let note = notes.shift();
const contentStream = stringToStream(note.bodyXml);
chain.push(() => {
return enexXmlToMd(contentStream, note.resources).then((body) => {
delete note.bodyXml;
// console.info('*************************************************************************');
// console.info(body);
// console.info('*************************************************************************');
// console.info('-----------------------------------------------------------');
// console.info(body);
// console.info('-----------------------------------------------------------');
note.id = uuid.create();
note.parent_id = parentFolderId;
note.body = body;
note.id = uuid.create();
note.parent_id = parentFolderId;
note.body = body;
// Notes in enex files always have a created timestamp but not always an
// updated timestamp (it the note has never been modified). For sync
// we require an updated_time property, so set it to create_time in that case
if (!note.updated_time) note.updated_time = note.created_time;
// Notes in enex files always have a created timestamp but not always an
// updated timestamp (it the note has never been modified). For sync
// we require an updated_time property, so set it to create_time in that case
if (!note.updated_time) note.updated_time = note.created_time;
const result = await saveNoteToStorage(note, importOptions.fuzzyMatching);
if (result.noteUpdated) {
progressState.updated++;
} else if (result.noteCreated) {
progressState.created++;
} else if (result.noteSkipped) {
progressState.skipped++;
}
progressState.resourcesCreated += result.resourcesCreated;
progressState.notesTagged += result.notesTagged;
importOptions.onProgress(progressState);
}
} catch(error) {
console.error(error);
return saveNoteToStorage(note, importOptions.fuzzyMatching);
}).then((result) => {
if (result.noteUpdated) {
progressState.updated++;
} else if (result.noteCreated) {
progressState.created++;
} else if (result.noteSkipped) {
progressState.skipped++;
}
progressState.resourcesCreated += result.resourcesCreated;
progressState.notesTagged += result.notesTagged;
importOptions.onProgress(progressState);
});
});
}
stream.resume();
processingNotes = false;
return true;
// let chain = [];
// while (notes.length) {
// let note = notes.shift();
// const contentStream = stringToStream(note.bodyXml);
// chain.push(() => {
// return enexXmlToMd(contentStream, note.resources).then((body) => {
// delete note.bodyXml;
// // console.info('-----------------------------------------------------------');
// // console.info(body);
// // console.info('-----------------------------------------------------------');
// note.id = uuid.create();
// note.parent_id = parentFolderId;
// note.body = body;
// // Notes in enex files always have a created timestamp but not always an
// // updated timestamp (it the note has never been modified). For sync
// // we require an updated_time property, so set it to create_time in that case
// if (!note.updated_time) note.updated_time = note.created_time;
// return saveNoteToStorage(note, importOptions.fuzzyMatching);
// }).then((result) => {
// if (result.noteUpdated) {
// progressState.updated++;
// } else if (result.noteCreated) {
// progressState.created++;
// } else if (result.noteSkipped) {
// progressState.skipped++;
// }
// progressState.resourcesCreated += result.resourcesCreated;
// progressState.notesTagged += result.notesTagged;
// importOptions.onProgress(progressState);
// });
// });
// }
// return promiseChain(chain).then(() => {
// stream.resume();
// processingNotes = false;
// return true;
// });
return promiseChain(chain).then(() => {
stream.resume();
processingNotes = false;
return true;
});
}
saxStream.on('error', (error) => {
@@ -364,11 +323,7 @@ function importEnex(parentFolderId, filePath, importOptions = null) {
noteResourceRecognition.objID = extractRecognitionObjId(data);
} else if (note) {
if (n == 'content') {
if ('bodyXml' in note) {
note.bodyXml += data;
} else {
note.bodyXml = data;
}
note.bodyXml = data;
}
}
});

View File

@@ -202,7 +202,7 @@ class JoplinDatabase extends Database {
// default value and thus might cause problems. In that case, the default value
// must be set in the synchronizer too.
const existingDatabaseVersions = [0, 1, 2, 3, 4, 5, 6, 7, 8];
const existingDatabaseVersions = [0, 1, 2, 3, 4, 5, 6, 7];
let currentVersionIndex = existingDatabaseVersions.indexOf(fromVersion);
// currentVersionIndex < 0 if for the case where an old version of Joplin used with a newer
@@ -265,11 +265,6 @@ class JoplinDatabase extends Database {
queries.push('ALTER TABLE resources ADD COLUMN file_extension TEXT NOT NULL DEFAULT ""');
}
if (targetVersion == 8) {
queries.push('ALTER TABLE sync_items ADD COLUMN sync_disabled INT NOT NULL DEFAULT "0"');
queries.push('ALTER TABLE sync_items ADD COLUMN sync_disabled_reason TEXT NOT NULL DEFAULT ""');
}
queries.push({ sql: 'UPDATE version SET version = ?', params: [targetVersion] });
await this.transactionExecBatch(queries);

View File

@@ -339,7 +339,6 @@ class BaseItem extends BaseModel {
JOIN sync_items s ON s.item_id = items.id
WHERE sync_target = %d
AND s.sync_time < items.updated_time
AND s.sync_disabled = 0
%s
LIMIT %d
`,
@@ -383,21 +382,7 @@ class BaseItem extends BaseModel {
throw new Error('Invalid type: ' + type);
}
static async syncDisabledItems(syncTargetId) {
const rows = await this.db().selectAll('SELECT * FROM sync_items WHERE sync_disabled = 1 AND sync_target = ?', [syncTargetId]);
let output = [];
for (let i = 0; i < rows.length; i++) {
const item = await this.loadItem(rows[i].item_type, rows[i].item_id);
if (!item) continue; // The referenced item no longer exist
output.push({
syncInfo: rows[i],
item: item,
});
}
return output;
}
static updateSyncTimeQueries(syncTarget, item, syncTime, syncDisabled = false, syncDisabledReason = '') {
static updateSyncTimeQueries(syncTarget, item, syncTime) {
const itemType = item.type_;
const itemId = item.id;
if (!itemType || !itemId || syncTime === undefined) throw new Error('Invalid parameters in updateSyncTimeQueries()');
@@ -408,8 +393,8 @@ class BaseItem extends BaseModel {
params: [syncTarget, itemType, itemId],
},
{
sql: 'INSERT INTO sync_items (sync_target, item_type, item_id, sync_time, sync_disabled, sync_disabled_reason) VALUES (?, ?, ?, ?, ?, ?)',
params: [syncTarget, itemType, itemId, syncTime, syncDisabled ? 1 : 0, syncDisabledReason + ''],
sql: 'INSERT INTO sync_items (sync_target, item_type, item_id, sync_time) VALUES (?, ?, ?, ?)',
params: [syncTarget, itemType, itemId, syncTime],
}
];
}
@@ -419,12 +404,6 @@ class BaseItem extends BaseModel {
return this.db().transactionExecBatch(queries);
}
static async saveSyncDisabled(syncTargetId, item, syncDisabledReason) {
const syncTime = 'sync_time' in item ? item.sync_time : 0;
const queries = this.updateSyncTimeQueries(syncTargetId, item, syncTime, true, syncDisabledReason);
return this.db().transactionExecBatch(queries);
}
// When an item is deleted, its associated sync_items data is not immediately deleted for
// performance reason. So this function is used to look for these remaining sync_items and
// delete them.

View File

@@ -126,11 +126,7 @@ class Note extends BaseItem {
let r = null;
r = noteFieldComp(a.user_updated_time, b.user_updated_time); if (r) return r;
r = noteFieldComp(a.user_created_time, b.user_created_time); if (r) return r;
const titleA = a.title ? a.title.toLowerCase() : '';
const titleB = b.title ? b.title.toLowerCase() : '';
r = noteFieldComp(titleA, titleB); if (r) return r;
r = noteFieldComp(a.title.toLowerCase(), b.title.toLowerCase()); if (r) return r;
return noteFieldComp(a.id, b.id);
}

View File

@@ -22,6 +22,15 @@ class Setting extends BaseModel {
this.metadata_ = {
'activeFolderId': { value: '', type: Setting.TYPE_STRING, public: false },
'firstStart': { value: true, type: Setting.TYPE_BOOL, public: false },
'sync.2.path': { value: '', type: Setting.TYPE_STRING, public: true, appTypes: ['cli'], label: () => _('File system synchronisation target directory'), description: () => _('The path to synchronise with when file system synchronisation is enabled. See `sync.target`.') },
'sync.3.auth': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.4.auth': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.1.context': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.2.context': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.3.context': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.4.context': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.5.context': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.6.context': { value: '', type: Setting.TYPE_STRING, public: false },
'editor': { value: '', type: Setting.TYPE_STRING, public: true, appTypes: ['cli'], label: () => _('Text editor'), description: () => _('The editor that will be used to open a note. If none is provided it will try to auto-detect the default editor.') },
'locale': { value: defaultLocale(), type: Setting.TYPE_STRING, isEnum: true, public: true, label: () => _('Language'), options: () => {
return supportedLocalesToLanguages();
@@ -77,15 +86,6 @@ class Setting extends BaseModel {
'sync.target': { value: SyncTargetRegistry.nameToId('onedrive'), type: Setting.TYPE_INT, isEnum: true, public: true, label: () => _('Synchronisation target'), description: () => _('The target to synchonise to. If synchronising with the file system, set `sync.2.path` to specify the target directory.'), options: () => {
return SyncTargetRegistry.idAndLabelPlainObject();
}},
'sync.2.path': { value: '', type: Setting.TYPE_STRING, show: (settings) => { return settings['sync.target'] == SyncTargetRegistry.nameToId('filesystem') }, public: true, label: () => _('Directory to synchronise with (absolute path)'), description: () => _('The path to synchronise with when file system synchronisation is enabled. See `sync.target`.') },
'sync.3.auth': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.4.auth': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.1.context': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.2.context': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.3.context': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.4.context': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.5.context': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.6.context': { value: '', type: Setting.TYPE_STRING, public: false },
};
return this.metadata_;

View File

@@ -25,8 +25,7 @@ const defaultState = {
searchQuery: '',
settings: {},
appState: 'starting',
//windowContentSize: { width: 0, height: 0 },
hasDisabledSyncItems: false,
windowContentSize: { width: 0, height: 0 },
};
// When deleting a note, tag or folder
@@ -396,12 +395,6 @@ const reducer = (state = defaultState, action) => {
newState.appState = action.state;
break;
case 'SYNC_HAS_DISABLED_SYNC_ITEMS':
newState = Object.assign({}, state);
newState.hasDisabledSyncItems = true;
break;
}
} catch (error) {
error.message = 'In reducer: ' + error.message + ' Action: ' + JSON.stringify(action);

View File

@@ -109,21 +109,8 @@ class ReportService {
async status(syncTarget) {
let r = await this.syncStatus(syncTarget);
let sections = [];
let section = null;
const disabledItems = await BaseItem.syncDisabledItems(syncTarget);
if (disabledItems.length) {
section = { title: _('Items that cannot be synchronised'), body: [] };
for (let i = 0; i < disabledItems.length; i++) {
const row = disabledItems[i];
section.body.push(_('"%s": "%s"', row.item.title, row.syncInfo.sync_disabled_reason));
}
sections.push(section);
}
section = { title: _('Sync status (synced items / total items)'), body: [] };
let section = { title: _('Sync status (synced items / total items)'), body: [] };
for (let n in r.items) {
if (!r.items.hasOwnProperty(n)) continue;
@@ -151,20 +138,17 @@ class ReportService {
sections.push(section);
section = { title: _('Coming alarms'), body: [] };
const alarms = await Alarm.allDue();
if (alarms.length) {
section = { title: _('Coming alarms'), body: [] };
for (let i = 0; i < alarms.length; i++) {
const alarm = alarms[i];
const note = await Note.load(alarm.note_id);
section.body.push(_('On %s: %s', time.formatMsToLocal(alarm.trigger_time), note.title));
}
sections.push(section);
for (let i = 0; i < alarms.length; i++) {
const alarm = alarms[i];
const note = await Note.load(alarm.note_id);
section.body.push(_('On %s: %s', time.formatMsToLocal(alarm.trigger_time), note.title));
}
sections.push(section);
return sections;
}

View File

@@ -253,36 +253,22 @@ class Synchronizer {
this.logSyncOperation(action, local, remote, reason);
const handleCannotSyncItem = async (syncTargetId, item, cannotSyncReason) => {
await ItemClass.saveSyncDisabled(syncTargetId, item, cannotSyncReason);
this.dispatch({ type: 'SYNC_HAS_DISABLED_SYNC_ITEMS' });
}
if (local.type_ == BaseModel.TYPE_RESOURCE && (action == 'createRemote' || (action == 'itemConflict' && remote))) {
let remoteContentPath = this.resourceDirName_ + '/' + local.id;
try {
// TODO: handle node and mobile in the same way
if (shim.isNode()) {
let resourceContent = '';
try {
resourceContent = await Resource.content(local);
} catch (error) {
error.message = 'Cannot read resource content: ' + local.id + ': ' + error.message;
this.logger().error(error);
this.progressReport_.errors.push(error);
}
await this.api().put(remoteContentPath, resourceContent);
} else {
const localResourceContentPath = Resource.fullPath(local);
await this.api().put(remoteContentPath, null, { path: localResourceContentPath, source: 'file' });
}
} catch (error) {
if (error && error.code === 'cannotSync') {
await handleCannotSyncItem(syncTargetId, local, error.message);
action = null;
} else {
throw error;
// TODO: handle node and mobile in the same way
if (shim.isNode()) {
let resourceContent = '';
try {
resourceContent = await Resource.content(local);
} catch (error) {
error.message = 'Cannot read resource content: ' + local.id + ': ' + error.message;
this.logger().error(error);
this.progressReport_.errors.push(error);
}
await this.api().put(remoteContentPath, resourceContent);
} else {
const localResourceContentPath = Resource.fullPath(local);
await this.api().put(remoteContentPath, null, { path: localResourceContentPath, source: 'file' });
}
}
@@ -299,27 +285,9 @@ class Synchronizer {
// await this.api().setTimestamp(tempPath, local.updated_time);
// await this.api().move(tempPath, path);
let canSync = true;
try {
if (this.debugFlags_.indexOf('cannotSync') >= 0) {
const error = new Error('Testing cannotSync');
error.code = 'cannotSync';
throw error;
}
await this.api().put(path, content);
} catch (error) {
if (error && error.code === 'cannotSync') {
await handleCannotSyncItem(syncTargetId, local, error.message);
canSync = false;
} else {
throw error;
}
}
if (canSync) {
await this.api().setTimestamp(path, local.updated_time);
await ItemClass.saveSyncTime(syncTargetId, local, time.unixMs());
}
await this.api().put(path, content);
await this.api().setTimestamp(path, local.updated_time);
await ItemClass.saveSyncTime(syncTargetId, local, time.unixMs());
} else if (action == 'itemConflict') {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,5 @@
var locales = {};
locales['en_GB'] = require('./en_GB.json');
locales['de_DE'] = require('./de_DE.json');
locales['es_CR'] = require('./es_CR.json');
locales['fr_FR'] = require('./fr_FR.json');
module.exports = { locales: locales };

View File

@@ -549,15 +549,6 @@
"babel-template": "6.26.0"
}
},
"babel-plugin-transform-define": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-define/-/babel-plugin-transform-define-1.3.0.tgz",
"integrity": "sha1-lMX5RZyBDHOMx8UMvUSjGCnW8xk=",
"requires": {
"lodash": "4.17.4",
"traverse": "0.6.6"
}
},
"babel-plugin-transform-es2015-arrow-functions": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz",
@@ -1201,11 +1192,6 @@
"integrity": "sha512-vHDDF/bP9RYpTWtUhpJRhCFdvvp3iDWvEbuDbWgvjUrNGV1MXJrE0MPcwGtEled04m61iwdBLUIHZtDgzWS4ZQ==",
"dev": true
},
"clamp": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/clamp/-/clamp-1.0.1.tgz",
"integrity": "sha1-ZqDmQBGBbjcZaCj9yMjBRzEshjQ="
},
"cli-cursor": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
@@ -2101,6 +2087,795 @@
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"fsevents": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz",
"integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==",
"optional": true,
"requires": {
"nan": "2.8.0",
"node-pre-gyp": "0.6.39"
},
"dependencies": {
"abbrev": {
"version": "1.1.0",
"bundled": true,
"optional": true
},
"ajv": {
"version": "4.11.8",
"bundled": true,
"optional": true,
"requires": {
"co": "4.6.0",
"json-stable-stringify": "1.0.1"
}
},
"ansi-regex": {
"version": "2.1.1",
"bundled": true
},
"aproba": {
"version": "1.1.1",
"bundled": true,
"optional": true
},
"are-we-there-yet": {
"version": "1.1.4",
"bundled": true,
"optional": true,
"requires": {
"delegates": "1.0.0",
"readable-stream": "2.2.9"
}
},
"asn1": {
"version": "0.2.3",
"bundled": true,
"optional": true
},
"assert-plus": {
"version": "0.2.0",
"bundled": true,
"optional": true
},
"asynckit": {
"version": "0.4.0",
"bundled": true,
"optional": true
},
"aws-sign2": {
"version": "0.6.0",
"bundled": true,
"optional": true
},
"aws4": {
"version": "1.6.0",
"bundled": true,
"optional": true
},
"balanced-match": {
"version": "0.4.2",
"bundled": true
},
"bcrypt-pbkdf": {
"version": "1.0.1",
"bundled": true,
"optional": true,
"requires": {
"tweetnacl": "0.14.5"
}
},
"block-stream": {
"version": "0.0.9",
"bundled": true,
"requires": {
"inherits": "2.0.3"
}
},
"boom": {
"version": "2.10.1",
"bundled": true,
"requires": {
"hoek": "2.16.3"
}
},
"brace-expansion": {
"version": "1.1.7",
"bundled": true,
"requires": {
"balanced-match": "0.4.2",
"concat-map": "0.0.1"
}
},
"buffer-shims": {
"version": "1.0.0",
"bundled": true
},
"caseless": {
"version": "0.12.0",
"bundled": true,
"optional": true
},
"co": {
"version": "4.6.0",
"bundled": true,
"optional": true
},
"code-point-at": {
"version": "1.1.0",
"bundled": true
},
"combined-stream": {
"version": "1.0.5",
"bundled": true,
"requires": {
"delayed-stream": "1.0.0"
}
},
"concat-map": {
"version": "0.0.1",
"bundled": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true
},
"core-util-is": {
"version": "1.0.2",
"bundled": true
},
"cryptiles": {
"version": "2.0.5",
"bundled": true,
"requires": {
"boom": "2.10.1"
}
},
"dashdash": {
"version": "1.14.1",
"bundled": true,
"optional": true,
"requires": {
"assert-plus": "1.0.0"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"bundled": true,
"optional": true
}
}
},
"debug": {
"version": "2.6.8",
"bundled": true,
"optional": true,
"requires": {
"ms": "2.0.0"
}
},
"deep-extend": {
"version": "0.4.2",
"bundled": true,
"optional": true
},
"delayed-stream": {
"version": "1.0.0",
"bundled": true
},
"delegates": {
"version": "1.0.0",
"bundled": true,
"optional": true
},
"detect-libc": {
"version": "1.0.2",
"bundled": true,
"optional": true
},
"ecc-jsbn": {
"version": "0.1.1",
"bundled": true,
"optional": true,
"requires": {
"jsbn": "0.1.1"
}
},
"extend": {
"version": "3.0.1",
"bundled": true,
"optional": true
},
"extsprintf": {
"version": "1.0.2",
"bundled": true
},
"forever-agent": {
"version": "0.6.1",
"bundled": true,
"optional": true
},
"form-data": {
"version": "2.1.4",
"bundled": true,
"optional": true,
"requires": {
"asynckit": "0.4.0",
"combined-stream": "1.0.5",
"mime-types": "2.1.15"
}
},
"fs.realpath": {
"version": "1.0.0",
"bundled": true
},
"fstream": {
"version": "1.0.11",
"bundled": true,
"requires": {
"graceful-fs": "4.1.11",
"inherits": "2.0.3",
"mkdirp": "0.5.1",
"rimraf": "2.6.1"
}
},
"fstream-ignore": {
"version": "1.0.5",
"bundled": true,
"optional": true,
"requires": {
"fstream": "1.0.11",
"inherits": "2.0.3",
"minimatch": "3.0.4"
}
},
"gauge": {
"version": "2.7.4",
"bundled": true,
"optional": true,
"requires": {
"aproba": "1.1.1",
"console-control-strings": "1.1.0",
"has-unicode": "2.0.1",
"object-assign": "4.1.1",
"signal-exit": "3.0.2",
"string-width": "1.0.2",
"strip-ansi": "3.0.1",
"wide-align": "1.1.2"
}
},
"getpass": {
"version": "0.1.7",
"bundled": true,
"optional": true,
"requires": {
"assert-plus": "1.0.0"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"bundled": true,
"optional": true
}
}
},
"glob": {
"version": "7.1.2",
"bundled": true,
"requires": {
"fs.realpath": "1.0.0",
"inflight": "1.0.6",
"inherits": "2.0.3",
"minimatch": "3.0.4",
"once": "1.4.0",
"path-is-absolute": "1.0.1"
}
},
"graceful-fs": {
"version": "4.1.11",
"bundled": true
},
"har-schema": {
"version": "1.0.5",
"bundled": true,
"optional": true
},
"har-validator": {
"version": "4.2.1",
"bundled": true,
"optional": true,
"requires": {
"ajv": "4.11.8",
"har-schema": "1.0.5"
}
},
"has-unicode": {
"version": "2.0.1",
"bundled": true,
"optional": true
},
"hawk": {
"version": "3.1.3",
"bundled": true,
"requires": {
"boom": "2.10.1",
"cryptiles": "2.0.5",
"hoek": "2.16.3",
"sntp": "1.0.9"
}
},
"hoek": {
"version": "2.16.3",
"bundled": true
},
"http-signature": {
"version": "1.1.1",
"bundled": true,
"optional": true,
"requires": {
"assert-plus": "0.2.0",
"jsprim": "1.4.0",
"sshpk": "1.13.0"
}
},
"inflight": {
"version": "1.0.6",
"bundled": true,
"requires": {
"once": "1.4.0",
"wrappy": "1.0.2"
}
},
"inherits": {
"version": "2.0.3",
"bundled": true
},
"ini": {
"version": "1.3.4",
"bundled": true,
"optional": true
},
"is-fullwidth-code-point": {
"version": "1.0.0",
"bundled": true,
"requires": {
"number-is-nan": "1.0.1"
}
},
"is-typedarray": {
"version": "1.0.0",
"bundled": true,
"optional": true
},
"isarray": {
"version": "1.0.0",
"bundled": true
},
"isstream": {
"version": "0.1.2",
"bundled": true,
"optional": true
},
"jodid25519": {
"version": "1.0.2",
"bundled": true,
"optional": true,
"requires": {
"jsbn": "0.1.1"
}
},
"jsbn": {
"version": "0.1.1",
"bundled": true,
"optional": true
},
"json-schema": {
"version": "0.2.3",
"bundled": true,
"optional": true
},
"json-stable-stringify": {
"version": "1.0.1",
"bundled": true,
"optional": true,
"requires": {
"jsonify": "0.0.0"
}
},
"json-stringify-safe": {
"version": "5.0.1",
"bundled": true,
"optional": true
},
"jsonify": {
"version": "0.0.0",
"bundled": true,
"optional": true
},
"jsprim": {
"version": "1.4.0",
"bundled": true,
"optional": true,
"requires": {
"assert-plus": "1.0.0",
"extsprintf": "1.0.2",
"json-schema": "0.2.3",
"verror": "1.3.6"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"bundled": true,
"optional": true
}
}
},
"mime-db": {
"version": "1.27.0",
"bundled": true
},
"mime-types": {
"version": "2.1.15",
"bundled": true,
"requires": {
"mime-db": "1.27.0"
}
},
"minimatch": {
"version": "3.0.4",
"bundled": true,
"requires": {
"brace-expansion": "1.1.7"
}
},
"minimist": {
"version": "0.0.8",
"bundled": true
},
"mkdirp": {
"version": "0.5.1",
"bundled": true,
"requires": {
"minimist": "0.0.8"
}
},
"ms": {
"version": "2.0.0",
"bundled": true,
"optional": true
},
"node-pre-gyp": {
"version": "0.6.39",
"bundled": true,
"optional": true,
"requires": {
"detect-libc": "1.0.2",
"hawk": "3.1.3",
"mkdirp": "0.5.1",
"nopt": "4.0.1",
"npmlog": "4.1.0",
"rc": "1.2.1",
"request": "2.81.0",
"rimraf": "2.6.1",
"semver": "5.3.0",
"tar": "2.2.1",
"tar-pack": "3.4.0"
}
},
"nopt": {
"version": "4.0.1",
"bundled": true,
"optional": true,
"requires": {
"abbrev": "1.1.0",
"osenv": "0.1.4"
}
},
"npmlog": {
"version": "4.1.0",
"bundled": true,
"optional": true,
"requires": {
"are-we-there-yet": "1.1.4",
"console-control-strings": "1.1.0",
"gauge": "2.7.4",
"set-blocking": "2.0.0"
}
},
"number-is-nan": {
"version": "1.0.1",
"bundled": true
},
"oauth-sign": {
"version": "0.8.2",
"bundled": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
"bundled": true,
"optional": true
},
"once": {
"version": "1.4.0",
"bundled": true,
"requires": {
"wrappy": "1.0.2"
}
},
"os-homedir": {
"version": "1.0.2",
"bundled": true,
"optional": true
},
"os-tmpdir": {
"version": "1.0.2",
"bundled": true,
"optional": true
},
"osenv": {
"version": "0.1.4",
"bundled": true,
"optional": true,
"requires": {
"os-homedir": "1.0.2",
"os-tmpdir": "1.0.2"
}
},
"path-is-absolute": {
"version": "1.0.1",
"bundled": true
},
"performance-now": {
"version": "0.2.0",
"bundled": true,
"optional": true
},
"process-nextick-args": {
"version": "1.0.7",
"bundled": true
},
"punycode": {
"version": "1.4.1",
"bundled": true,
"optional": true
},
"qs": {
"version": "6.4.0",
"bundled": true,
"optional": true
},
"rc": {
"version": "1.2.1",
"bundled": true,
"optional": true,
"requires": {
"deep-extend": "0.4.2",
"ini": "1.3.4",
"minimist": "1.2.0",
"strip-json-comments": "2.0.1"
},
"dependencies": {
"minimist": {
"version": "1.2.0",
"bundled": true,
"optional": true
}
}
},
"readable-stream": {
"version": "2.2.9",
"bundled": true,
"requires": {
"buffer-shims": "1.0.0",
"core-util-is": "1.0.2",
"inherits": "2.0.3",
"isarray": "1.0.0",
"process-nextick-args": "1.0.7",
"string_decoder": "1.0.1",
"util-deprecate": "1.0.2"
}
},
"request": {
"version": "2.81.0",
"bundled": true,
"optional": true,
"requires": {
"aws-sign2": "0.6.0",
"aws4": "1.6.0",
"caseless": "0.12.0",
"combined-stream": "1.0.5",
"extend": "3.0.1",
"forever-agent": "0.6.1",
"form-data": "2.1.4",
"har-validator": "4.2.1",
"hawk": "3.1.3",
"http-signature": "1.1.1",
"is-typedarray": "1.0.0",
"isstream": "0.1.2",
"json-stringify-safe": "5.0.1",
"mime-types": "2.1.15",
"oauth-sign": "0.8.2",
"performance-now": "0.2.0",
"qs": "6.4.0",
"safe-buffer": "5.0.1",
"stringstream": "0.0.5",
"tough-cookie": "2.3.2",
"tunnel-agent": "0.6.0",
"uuid": "3.0.1"
}
},
"rimraf": {
"version": "2.6.1",
"bundled": true,
"requires": {
"glob": "7.1.2"
}
},
"safe-buffer": {
"version": "5.0.1",
"bundled": true
},
"semver": {
"version": "5.3.0",
"bundled": true,
"optional": true
},
"set-blocking": {
"version": "2.0.0",
"bundled": true,
"optional": true
},
"signal-exit": {
"version": "3.0.2",
"bundled": true,
"optional": true
},
"sntp": {
"version": "1.0.9",
"bundled": true,
"requires": {
"hoek": "2.16.3"
}
},
"sshpk": {
"version": "1.13.0",
"bundled": true,
"optional": true,
"requires": {
"asn1": "0.2.3",
"assert-plus": "1.0.0",
"bcrypt-pbkdf": "1.0.1",
"dashdash": "1.14.1",
"ecc-jsbn": "0.1.1",
"getpass": "0.1.7",
"jodid25519": "1.0.2",
"jsbn": "0.1.1",
"tweetnacl": "0.14.5"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"bundled": true,
"optional": true
}
}
},
"string-width": {
"version": "1.0.2",
"bundled": true,
"requires": {
"code-point-at": "1.1.0",
"is-fullwidth-code-point": "1.0.0",
"strip-ansi": "3.0.1"
}
},
"string_decoder": {
"version": "1.0.1",
"bundled": true,
"requires": {
"safe-buffer": "5.0.1"
}
},
"stringstream": {
"version": "0.0.5",
"bundled": true,
"optional": true
},
"strip-ansi": {
"version": "3.0.1",
"bundled": true,
"requires": {
"ansi-regex": "2.1.1"
}
},
"strip-json-comments": {
"version": "2.0.1",
"bundled": true,
"optional": true
},
"tar": {
"version": "2.2.1",
"bundled": true,
"requires": {
"block-stream": "0.0.9",
"fstream": "1.0.11",
"inherits": "2.0.3"
}
},
"tar-pack": {
"version": "3.4.0",
"bundled": true,
"optional": true,
"requires": {
"debug": "2.6.8",
"fstream": "1.0.11",
"fstream-ignore": "1.0.5",
"once": "1.4.0",
"readable-stream": "2.2.9",
"rimraf": "2.6.1",
"tar": "2.2.1",
"uid-number": "0.0.6"
}
},
"tough-cookie": {
"version": "2.3.2",
"bundled": true,
"optional": true,
"requires": {
"punycode": "1.4.1"
}
},
"tunnel-agent": {
"version": "0.6.0",
"bundled": true,
"optional": true,
"requires": {
"safe-buffer": "5.0.1"
}
},
"tweetnacl": {
"version": "0.14.5",
"bundled": true,
"optional": true
},
"uid-number": {
"version": "0.0.6",
"bundled": true,
"optional": true
},
"util-deprecate": {
"version": "1.0.2",
"bundled": true
},
"uuid": {
"version": "3.0.1",
"bundled": true,
"optional": true
},
"verror": {
"version": "1.3.6",
"bundled": true,
"optional": true,
"requires": {
"extsprintf": "1.0.2"
}
},
"wide-align": {
"version": "1.1.2",
"bundled": true,
"optional": true,
"requires": {
"string-width": "1.0.2"
}
},
"wrappy": {
"version": "1.0.2",
"bundled": true
}
}
},
"gauge": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-1.2.7.tgz",
@@ -3984,6 +4759,12 @@
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
"integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s="
},
"nan": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz",
"integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=",
"optional": true
},
"natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -4264,21 +5045,6 @@
"integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=",
"dev": true
},
"path-to-regexp": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
"integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
"requires": {
"isarray": "0.0.1"
},
"dependencies": {
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
}
}
},
"path-type": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
@@ -4636,32 +5402,11 @@
"prop-types": "15.6.0"
}
},
"react-native-dismiss-keyboard": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/react-native-dismiss-keyboard/-/react-native-dismiss-keyboard-1.0.0.tgz",
"integrity": "sha1-MohiQrPyMX4SHzrrmwpYXiuHm0k="
},
"react-native-document-picker": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/react-native-document-picker/-/react-native-document-picker-2.1.0.tgz",
"integrity": "sha512-BFCBXwz8xuLvHLVFVeQM+RhaY8yZ38PEWt9WSbq5VIoZ/VssP6uu51XxOfdwaMALOrAHIojK0SiYnd155upZAg=="
},
"react-native-drawer-layout": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/react-native-drawer-layout/-/react-native-drawer-layout-1.3.2.tgz",
"integrity": "sha512-fjO0scqbJUfNu2wuEpvywL7DYLXuCXJ2W/zYhWz986rdLytidbys1QGVvkaszHrb4Y7OqO96mTkgpOcP8KWevw==",
"requires": {
"react-native-dismiss-keyboard": "1.0.0"
}
},
"react-native-drawer-layout-polyfill": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/react-native-drawer-layout-polyfill/-/react-native-drawer-layout-polyfill-1.3.2.tgz",
"integrity": "sha512-XzPhfLDJrYHru+e8+dFwhf0FtTeAp7JXPpFYezYV6P1nTeA1Tia/kDpFT+O2DWTrBKBEI8FGhZnThrroZmHIxg==",
"requires": {
"react-native-drawer-layout": "1.3.2"
}
},
"react-native-dropdownalert": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/react-native-dropdownalert/-/react-native-dropdownalert-3.1.2.tgz",
@@ -4770,9 +5515,9 @@
}
},
"react-native-popup-menu": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/react-native-popup-menu/-/react-native-popup-menu-0.10.0.tgz",
"integrity": "sha1-zhU2eo1WKIfVypB+IyMB1BLd5+c="
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/react-native-popup-menu/-/react-native-popup-menu-0.8.3.tgz",
"integrity": "sha1-HbsLT4iclBC2myKKidV7Vq6lzC4="
},
"react-native-push-notification": {
"version": "3.0.1",
@@ -4845,14 +5590,6 @@
}
}
},
"react-native-tab-view": {
"version": "0.0.70",
"resolved": "https://registry.npmjs.org/react-native-tab-view/-/react-native-tab-view-0.0.70.tgz",
"integrity": "sha512-VOTZs2UCamYrTyBtcxpn4Ko9RkWrq6rZGOY7AagHF/gcCUQBMSWADoplsBopSc5vXnHHeCSR458BkceEuSEaQA==",
"requires": {
"prop-types": "15.6.0"
}
},
"react-native-vector-icons": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-4.4.2.tgz",
@@ -4994,27 +5731,6 @@
}
}
},
"react-navigation": {
"version": "1.0.0-beta.21",
"resolved": "https://registry.npmjs.org/react-navigation/-/react-navigation-1.0.0-beta.21.tgz",
"integrity": "sha512-bNAiNBtxrumZQmIj6uH2l1jHIkH8mILoGse5BMHOeFWACADl8LnbE91mzAkJ2EbAzAQ5LNpkSjGSIGxbvfsRCw==",
"requires": {
"babel-plugin-transform-define": "1.3.0",
"clamp": "1.0.1",
"hoist-non-react-statics": "2.3.1",
"path-to-regexp": "1.7.0",
"prop-types": "15.6.0",
"react-native-drawer-layout-polyfill": "1.3.2",
"react-native-tab-view": "0.0.70"
},
"dependencies": {
"hoist-non-react-statics": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz",
"integrity": "sha1-ND24TGAYxlB3iJgkATWhQg7iLOA="
}
}
},
"react-proxy": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/react-proxy/-/react-proxy-1.1.8.tgz",
@@ -5330,6 +6046,7 @@
"anymatch": "1.3.2",
"exec-sh": "0.2.1",
"fb-watchman": "2.0.0",
"fsevents": "1.1.3",
"minimatch": "3.0.4",
"minimist": "1.2.0",
"walker": "1.0.7",
@@ -5832,11 +6549,6 @@
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=",
"dev": true
},
"traverse": {
"version": "0.6.6",
"resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz",
"integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc="
},
"trim-right": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",

View File

@@ -29,12 +29,11 @@
"react-native-image-resizer": "^1.0.0",
"react-native-material-dropdown": "^0.5.2",
"react-native-popup-dialog": "^0.9.35",
"react-native-popup-menu": "^0.10.0",
"react-native-popup-menu": "^0.8.3",
"react-native-push-notification": "^3.0.1",
"react-native-side-menu": "^1.1.3",
"react-native-sqlite-storage": "3.3.*",
"react-native-vector-icons": "^4.4.2",
"react-navigation": "^1.0.0-beta.21",
"react-redux": "4.4.8",
"redux": "3.6.0",
"uuid": "^3.0.1"

View File

@@ -1,6 +1,5 @@
const React = require('react'); const Component = React.Component;
const { Keyboard, NativeModules, BackHandler } = require('react-native');
const { SafeAreaView } = require('react-navigation');
const { connect, Provider } = require('react-redux');
const { BackButtonService } = require('lib/services/back-button.js');
const AlarmService = require('lib/services/AlarmService.js');
@@ -462,7 +461,7 @@ class AppComponent extends React.Component {
render() {
if (this.props.appState != 'ready') return null;
const sideMenuContent = <SafeAreaView style={{flex:1}}><SideMenuContent/></SafeAreaView>;
const sideMenuContent = <SideMenuContent/>;
const appNavInit = {
Welcome: { screen: WelcomeScreen },
@@ -488,9 +487,7 @@ class AppComponent extends React.Component {
}}
>
<MenuContext style={{ flex: 1 }}>
<SafeAreaView style={{flex:1}}>
<AppNav screens={appNavInit} />
</SafeAreaView>
<AppNav screens={appNavInit} />
<DropdownAlert ref={ref => this.dropdownAlert_ = ref} tapToCloseEnabled={true} />
</MenuContext>
</SideMenu>

View File

@@ -218,15 +218,15 @@
<tbody>
<tr>
<td>Windows</td>
<td><a href='https://github.com/laurent22/joplin/releases/download/v0.10.37/Joplin-Setup-0.10.37.exe'><img alt='Get it on Windows' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeWindows.png'/></a></td>
<td><a href='https://github.com/laurent22/joplin/releases/download/v0.10.36/Joplin-Setup-0.10.36.exe'><img alt='Get it on Windows' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeWindows.png'/></a></td>
</tr>
<tr>
<td>macOS</td>
<td><a href='https://github.com/laurent22/joplin/releases/download/v0.10.37/Joplin-0.10.37.dmg'><img alt='Get it on macOS' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeMacOS.png'/></a></td>
<td><a href='https://github.com/laurent22/joplin/releases/download/v0.10.36/Joplin-0.10.36.dmg'><img alt='Get it on macOS' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeMacOS.png'/></a></td>
</tr>
<tr>
<td>Linux</td>
<td><a href='https://github.com/laurent22/joplin/releases/download/v0.10.37/Joplin-0.10.37-x86_64.AppImage'><img alt='Get it on macOS' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeLinux.png'/></a></td>
<td><a href='https://github.com/laurent22/joplin/releases/download/v0.10.36/Joplin-0.10.36-x86_64.AppImage'><img alt='Get it on macOS' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeLinux.png'/></a></td>
</tr>
</tbody>
</table>

View File

@@ -309,18 +309,7 @@ sudo ln -s ~/.joplin-bin/bin/joplin /usr/bin/joplin
<p>Since this is still an actual URL, the terminal will still make it clickable. And with shorter URLs, the text is more readable and the links unlikely to be cut. Both resources (files that are attached to notes) and external links are handled in this way.</p>
<h1 id="attachments-resources">Attachments / Resources</h1>
<p>In Markdown, links to resources are represented as a simple ID to the resource. In order to give access to these resources, they will be, like links, converted to local URLs. Clicking this link will then open a browser, which will handle the file - i.e. display the image, open the PDF file, etc.</p>
<h1 id="shell-mode">Shell mode</h1>
<p>Commands can also be used directly from a shell. To view the list of available commands, type <code>joplin help all</code>. To reference a note, notebook or tag you can either use the ID (type <code>joplin ls -l</code> to view the ID) or by title.</p>
<p>For example, this will create a new note &quot;My note&quot; in the notebook &quot;My notebook&quot;:</p>
<pre><code>$ joplin mkbook &quot;My notebook&quot;
$ joplin use &quot;My notebook&quot;
$ joplin mknote &quot;My note&quot;
</code></pre><p>To view the newly created note:</p>
<pre><code>$ joplin ls -l
fe889 07/12/2017 17:57 My note
</code></pre><p>Give a new title to the note:</p>
<pre><code>$ joplin set fe889 title &quot;New title&quot;
</code></pre><h1 id="available-shortcuts">Available shortcuts</h1>
<h1 id="available-shortcuts">Available shortcuts</h1>
<p>There are two types of shortcuts: those that manipulate the user interface directly, such as <code>TAB</code> to move from one pane to another, and those that are simply shortcuts to actual commands. In a way similar to Vim, these shortcuts are generally a verb followed by an object. For example, typing <code>mn</code> ([m]ake [n]ote), is used to create a new note: it will switch the interface to command line mode and pre-fill it with <code>mknote &quot;&quot;</code> from where the title of the note can be entered. See below for the full list of shortcuts:</p>
<pre><code>Tab Give focus to next pane
Shift+Tab Give focus to previous pane