mirror of
https://github.com/laurent22/joplin.git
synced 2024-11-24 08:12:24 +02:00
Electron, Mobile: Created alarm service and drivers
This commit is contained in:
parent
748acdf03f
commit
9a40851c77
@ -1,40 +0,0 @@
|
|||||||
const { time } = require('lib/time-utils.js');
|
|
||||||
const { setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient } = require('test-utils.js');
|
|
||||||
const { Folder } = require('lib/models/folder.js');
|
|
||||||
const { Note } = require('lib/models/note.js');
|
|
||||||
const { Setting } = require('lib/models/setting.js');
|
|
||||||
const { BaseItem } = require('lib/models/base-item.js');
|
|
||||||
const { BaseModel } = require('lib/base-model.js');
|
|
||||||
|
|
||||||
process.on('unhandledRejection', (reason, p) => {
|
|
||||||
console.error('Unhandled promise rejection at: Promise', p, 'reason:', reason);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('BaseItem', function() {
|
|
||||||
|
|
||||||
beforeEach( async (done) => {
|
|
||||||
await setupDatabaseAndSynchronizer(1);
|
|
||||||
switchClient(1);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create a deleted_items record', async (done) => {
|
|
||||||
let folder1 = await Folder.save({ title: 'folder1' });
|
|
||||||
let folder2 = await Folder.save({ title: 'folder2' });
|
|
||||||
|
|
||||||
await Folder.delete(folder1.id);
|
|
||||||
|
|
||||||
let items = await BaseItem.deletedItems();
|
|
||||||
|
|
||||||
expect(items.length).toBe(1);
|
|
||||||
expect(items[0].item_id).toBe(folder1.id);
|
|
||||||
expect(items[0].item_type).toBe(folder1.type_);
|
|
||||||
|
|
||||||
let folders = await Folder.all();
|
|
||||||
|
|
||||||
expect(folders.length).toBe(1);
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
@ -19,6 +19,7 @@ const { time } = require('lib/time-utils.js');
|
|||||||
const SyncTargetRegistry = require('lib/SyncTargetRegistry.js');
|
const SyncTargetRegistry = require('lib/SyncTargetRegistry.js');
|
||||||
const SyncTargetMemory = require('lib/SyncTargetMemory.js');
|
const SyncTargetMemory = require('lib/SyncTargetMemory.js');
|
||||||
const SyncTargetFilesystem = require('lib/SyncTargetFilesystem.js');
|
const SyncTargetFilesystem = require('lib/SyncTargetFilesystem.js');
|
||||||
|
const SyncTargetOneDrive = require('lib/SyncTargetOneDrive.js');
|
||||||
|
|
||||||
let databases_ = [];
|
let databases_ = [];
|
||||||
let synchronizers_ = [];
|
let synchronizers_ = [];
|
||||||
@ -34,6 +35,7 @@ fs.mkdirpSync(logDir, 0o755);
|
|||||||
|
|
||||||
SyncTargetRegistry.addClass(SyncTargetMemory);
|
SyncTargetRegistry.addClass(SyncTargetMemory);
|
||||||
SyncTargetRegistry.addClass(SyncTargetFilesystem);
|
SyncTargetRegistry.addClass(SyncTargetFilesystem);
|
||||||
|
SyncTargetRegistry.addClass(SyncTargetOneDrive);
|
||||||
|
|
||||||
const syncTargetId_ = SyncTargetRegistry.nameToId('memory');
|
const syncTargetId_ = SyncTargetRegistry.nameToId('memory');
|
||||||
const syncDir = __dirname + '/../tests/sync';
|
const syncDir = __dirname + '/../tests/sync';
|
||||||
|
@ -51,7 +51,8 @@ class ElectronAppWrapper {
|
|||||||
slashes: true
|
slashes: true
|
||||||
}))
|
}))
|
||||||
|
|
||||||
//if (this.env_ === 'dev') this.win_.webContents.openDevTools();
|
// Uncomment this to view errors if the application does not start
|
||||||
|
// if (this.env_ === 'dev') this.win_.webContents.openDevTools();
|
||||||
|
|
||||||
this.win_.on('close', (event) => {
|
this.win_.on('close', (event) => {
|
||||||
if (this.willQuitApp_ || process.platform !== 'darwin') {
|
if (this.willQuitApp_ || process.platform !== 'darwin') {
|
||||||
|
@ -15,6 +15,8 @@ const { JoplinDatabase } = require('lib/joplin-database.js');
|
|||||||
const { DatabaseDriverNode } = require('lib/database-driver-node.js');
|
const { DatabaseDriverNode } = require('lib/database-driver-node.js');
|
||||||
const { ElectronAppWrapper } = require('./ElectronAppWrapper');
|
const { ElectronAppWrapper } = require('./ElectronAppWrapper');
|
||||||
const { defaultState } = require('lib/reducer.js');
|
const { defaultState } = require('lib/reducer.js');
|
||||||
|
const AlarmService = require('lib/services/AlarmService.js');
|
||||||
|
const AlarmServiceDriverNode = require('lib/services/AlarmServiceDriverNode');
|
||||||
|
|
||||||
const { bridge } = require('electron').remote.require('./bridge');
|
const { bridge } = require('electron').remote.require('./bridge');
|
||||||
const Menu = bridge().Menu;
|
const Menu = bridge().Menu;
|
||||||
@ -135,6 +137,10 @@ class Application extends BaseApplication {
|
|||||||
if (!await reg.syncTarget().syncStarted()) reg.scheduleSync();
|
if (!await reg.syncTarget().syncStarted()) reg.scheduleSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (['EVENT_NOTE_ALARM_FIELD_CHANGE', 'NOTE_DELETE'].indexOf(action.type) >= 0) {
|
||||||
|
await AlarmService.updateNoteNotification(action.id, action.type === 'NOTE_DELETE');
|
||||||
|
}
|
||||||
|
|
||||||
const result = await super.generalMiddleware(store, next, action);
|
const result = await super.generalMiddleware(store, next, action);
|
||||||
const newState = store.getState();
|
const newState = store.getState();
|
||||||
|
|
||||||
@ -305,6 +311,9 @@ class Application extends BaseApplication {
|
|||||||
async start(argv) {
|
async start(argv) {
|
||||||
argv = await super.start(argv);
|
argv = await super.start(argv);
|
||||||
|
|
||||||
|
AlarmService.setDriver(new AlarmServiceDriverNode());
|
||||||
|
AlarmService.setLogger(reg.logger());
|
||||||
|
|
||||||
if (Setting.value('openDevTools')) {
|
if (Setting.value('openDevTools')) {
|
||||||
bridge().window().webContents.openDevTools();
|
bridge().window().webContents.openDevTools();
|
||||||
}
|
}
|
||||||
|
@ -175,6 +175,38 @@ class MainScreenComponent extends React.Component {
|
|||||||
id: searchId,
|
id: searchId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
this.setState({ promptOptions: null });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else if (command.name === 'editAlarm') {
|
||||||
|
const note = await Note.load(command.noteId);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
promptOptions: {
|
||||||
|
label: _('Set or clear alarm:'),
|
||||||
|
inputType: 'datetime',
|
||||||
|
buttons: ['ok', 'cancel', 'clear'],
|
||||||
|
value: note.todo_due ? new Date(note.todo_due) : null,
|
||||||
|
onClose: async (answer, buttonType) => {
|
||||||
|
let newNote = null;
|
||||||
|
|
||||||
|
if (buttonType === 'clear') {
|
||||||
|
newNote = {
|
||||||
|
id: note.id,
|
||||||
|
todo_due: 0,
|
||||||
|
};
|
||||||
|
} else if (answer !== null) {
|
||||||
|
newNote = {
|
||||||
|
id: note.id,
|
||||||
|
todo_due: answer.getTime(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newNote) {
|
||||||
|
await Note.save(newNote);
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({ promptOptions: null });
|
this.setState({ promptOptions: null });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -274,10 +306,12 @@ class MainScreenComponent extends React.Component {
|
|||||||
value={promptOptions && promptOptions.value ? promptOptions.value : ''}
|
value={promptOptions && promptOptions.value ? promptOptions.value : ''}
|
||||||
theme={this.props.theme}
|
theme={this.props.theme}
|
||||||
style={promptStyle}
|
style={promptStyle}
|
||||||
onClose={(answer) => promptOptions.onClose(answer)}
|
onClose={(answer, buttonType) => promptOptions.onClose(answer, buttonType)}
|
||||||
label={promptOptions ? promptOptions.label : ''}
|
label={promptOptions ? promptOptions.label : ''}
|
||||||
description={promptOptions ? promptOptions.description : null}
|
description={promptOptions ? promptOptions.description : null}
|
||||||
visible={!!this.state.promptOptions} />
|
visible={!!this.state.promptOptions}
|
||||||
|
buttons={promptOptions && ('buttons' in promptOptions) ? promptOptions.buttons : null}
|
||||||
|
inputType={promptOptions && ('inputType' in promptOptions) ? promptOptions.inputType : null} />
|
||||||
<Header style={headerStyle} showBackButton={false} buttons={headerButtons} />
|
<Header style={headerStyle} showBackButton={false} buttons={headerButtons} />
|
||||||
<SideBar style={sideBarStyle} />
|
<SideBar style={sideBarStyle} />
|
||||||
<NoteList style={noteListStyle} />
|
<NoteList style={noteListStyle} />
|
||||||
|
@ -336,6 +336,14 @@ class NoteTextComponent extends React.Component {
|
|||||||
}
|
}
|
||||||
}}));
|
}}));
|
||||||
|
|
||||||
|
menu.append(new MenuItem({label: _('Set or clear alarm'), click: async () => {
|
||||||
|
this.props.dispatch({
|
||||||
|
type: 'WINDOW_COMMAND',
|
||||||
|
name: 'editAlarm',
|
||||||
|
noteId: noteId,
|
||||||
|
});
|
||||||
|
}}));
|
||||||
|
|
||||||
menu.popup(bridge().window());
|
menu.popup(bridge().window());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ const React = require('react');
|
|||||||
const { connect } = require('react-redux');
|
const { connect } = require('react-redux');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const { themeStyle } = require('../theme.js');
|
const { themeStyle } = require('../theme.js');
|
||||||
|
const Datetime = require('react-datetime');
|
||||||
|
|
||||||
class PromptDialog extends React.Component {
|
class PromptDialog extends React.Component {
|
||||||
|
|
||||||
@ -32,6 +33,7 @@ class PromptDialog extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
const style = this.props.style;
|
const style = this.props.style;
|
||||||
const theme = themeStyle(this.props.theme);
|
const theme = themeStyle(this.props.theme);
|
||||||
|
const buttonTypes = this.props.buttons ? this.props.buttons : ['ok', 'cancel'];
|
||||||
|
|
||||||
const modalLayerStyle = {
|
const modalLayerStyle = {
|
||||||
zIndex: 9999,
|
zIndex: 9999,
|
||||||
@ -76,8 +78,8 @@ class PromptDialog extends React.Component {
|
|||||||
marginTop: 10,
|
marginTop: 10,
|
||||||
});
|
});
|
||||||
|
|
||||||
const onClose = (accept) => {
|
const onClose = (accept, buttonType) => {
|
||||||
if (this.props.onClose) this.props.onClose(accept ? this.state.answer : null);
|
if (this.props.onClose) this.props.onClose(accept ? this.state.answer : null, buttonType);
|
||||||
this.setState({ visible: false, answer: '' });
|
this.setState({ visible: false, answer: '' });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,6 +87,10 @@ class PromptDialog extends React.Component {
|
|||||||
this.setState({ answer: event.target.value });
|
this.setState({ answer: event.target.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onDateTimeChange = (momentObject) => {
|
||||||
|
this.setState({ answer: momentObject.toDate() });
|
||||||
|
}
|
||||||
|
|
||||||
const onKeyDown = (event) => {
|
const onKeyDown = (event) => {
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
onClose(true);
|
onClose(true);
|
||||||
@ -95,23 +101,41 @@ class PromptDialog extends React.Component {
|
|||||||
|
|
||||||
const descComp = this.props.description ? <div style={descStyle}>{this.props.description}</div> : null;
|
const descComp = this.props.description ? <div style={descStyle}>{this.props.description}</div> : null;
|
||||||
|
|
||||||
|
let inputComp = null;
|
||||||
|
|
||||||
|
if (this.props.inputType === 'datetime') {
|
||||||
|
inputComp = <Datetime
|
||||||
|
value={this.state.answer}
|
||||||
|
dateFormat="DD/MM/YYYY"
|
||||||
|
timeFormat="HH:mm"
|
||||||
|
onChange={(momentObject) => onDateTimeChange(momentObject)}
|
||||||
|
/>
|
||||||
|
} else {
|
||||||
|
inputComp = <input
|
||||||
|
style={inputStyle}
|
||||||
|
ref={input => this.answerInput_ = input}
|
||||||
|
value={this.state.answer}
|
||||||
|
type="text"
|
||||||
|
onChange={(event) => onChange(event)}
|
||||||
|
onKeyDown={(event) => onKeyDown(event)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
|
const buttonComps = [];
|
||||||
|
if (buttonTypes.indexOf('ok') >= 0) buttonComps.push(<button key="ok" style={buttonStyle} onClick={() => onClose(true, 'ok')}>{_('OK')}</button>);
|
||||||
|
if (buttonTypes.indexOf('cancel') >= 0) buttonComps.push(<button key="cancel" style={buttonStyle} onClick={() => onClose(false, 'cancel')}>{_('Cancel')}</button>);
|
||||||
|
if (buttonTypes.indexOf('clear') >= 0) buttonComps.push(<button key="clear" style={buttonStyle} onClick={() => onClose(false, 'clear')}>{_('Clear')}</button>);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={modalLayerStyle}>
|
<div style={modalLayerStyle}>
|
||||||
<div style={promptDialogStyle}>
|
<div style={promptDialogStyle}>
|
||||||
<label style={labelStyle}>{this.props.label ? this.props.label : ''}</label>
|
<label style={labelStyle}>{this.props.label ? this.props.label : ''}</label>
|
||||||
<div style={{display: 'inline-block'}}>
|
<div style={{display: 'inline-block'}}>
|
||||||
<input
|
{inputComp}
|
||||||
style={inputStyle}
|
|
||||||
ref={input => this.answerInput_ = input}
|
|
||||||
value={this.state.answer}
|
|
||||||
type="text"
|
|
||||||
onChange={(event) => onChange(event)}
|
|
||||||
onKeyDown={(event) => onKeyDown(event)} />
|
|
||||||
{descComp}
|
{descComp}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ textAlign: 'right', marginTop: 10 }}>
|
<div style={{ textAlign: 'right', marginTop: 10 }}>
|
||||||
<button style={buttonStyle} onClick={() => onClose(true)}>OK</button>
|
{buttonComps}
|
||||||
<button style={buttonStyle} onClick={() => onClose(false)}>Cancel</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
<title>Joplin</title>
|
<title>Joplin</title>
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
<link rel="stylesheet" href="css/font-awesome.min.css">
|
<link rel="stylesheet" href="css/font-awesome.min.css">
|
||||||
|
<link rel="stylesheet" href="node_modules/react-datetime/css/react-datetime.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="react-root"></div>
|
<div id="react-root"></div>
|
||||||
|
33
ElectronClient/app/package-lock.json
generated
33
ElectronClient/app/package-lock.json
generated
@ -1080,6 +1080,16 @@
|
|||||||
"capture-stack-trace": "1.0.0"
|
"capture-stack-trace": "1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"create-react-class": {
|
||||||
|
"version": "15.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.2.tgz",
|
||||||
|
"integrity": "sha1-zx7RXxKq1/FO9fLf4F5sQvke8Co=",
|
||||||
|
"requires": {
|
||||||
|
"fbjs": "0.8.16",
|
||||||
|
"loose-envify": "1.3.1",
|
||||||
|
"object-assign": "4.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"cross-spawn": {
|
"cross-spawn": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
|
||||||
@ -3561,6 +3571,24 @@
|
|||||||
"prop-types": "15.6.0"
|
"prop-types": "15.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-datetime": {
|
||||||
|
"version": "2.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-datetime/-/react-datetime-2.11.0.tgz",
|
||||||
|
"integrity": "sha512-LfToQVZrFjH9b+R4PWemo/jJcWawHWHL+eF1HFWGZ9zGXXgy7d/X1+PzJUBYiZfteFM+VSdaHvYyqReqP/nGaQ==",
|
||||||
|
"requires": {
|
||||||
|
"create-react-class": "15.6.2",
|
||||||
|
"object-assign": "3.0.0",
|
||||||
|
"prop-types": "15.6.0",
|
||||||
|
"react-onclickoutside": "6.7.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"object-assign": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz",
|
||||||
|
"integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-dom": {
|
"react-dom": {
|
||||||
"version": "16.1.1",
|
"version": "16.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.1.1.tgz",
|
||||||
@ -3572,6 +3600,11 @@
|
|||||||
"prop-types": "15.6.0"
|
"prop-types": "15.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-onclickoutside": {
|
||||||
|
"version": "6.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.7.0.tgz",
|
||||||
|
"integrity": "sha512-IBivBP7xayM7SbbVlAnKgHgoWdfCVqnNBNgQRY5x9iFQm55tFdolR02hX1fCJJtTEKnbaL1stB72/TZc6+p2+Q=="
|
||||||
|
},
|
||||||
"react-redux": {
|
"react-redux": {
|
||||||
"version": "5.0.6",
|
"version": "5.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.0.6.tgz",
|
||||||
|
@ -69,6 +69,7 @@
|
|||||||
"query-string": "^5.0.1",
|
"query-string": "^5.0.1",
|
||||||
"react": "^16.0.0",
|
"react": "^16.0.0",
|
||||||
"react-ace": "^5.5.0",
|
"react-ace": "^5.5.0",
|
||||||
|
"react-datetime": "^2.11.0",
|
||||||
"react-dom": "^16.0.0",
|
"react-dom": "^16.0.0",
|
||||||
"react-redux": "^5.0.6",
|
"react-redux": "^5.0.6",
|
||||||
"redux": "^3.7.2",
|
"redux": "^3.7.2",
|
||||||
|
@ -354,7 +354,7 @@ class BaseApplication {
|
|||||||
this.logger_.info('Profile directory: ' + profileDir);
|
this.logger_.info('Profile directory: ' + profileDir);
|
||||||
|
|
||||||
this.database_ = new JoplinDatabase(new DatabaseDriverNode());
|
this.database_ = new JoplinDatabase(new DatabaseDriverNode());
|
||||||
//this.database_.setLogExcludedQueryTypes(['SELECT']);
|
this.database_.setLogExcludedQueryTypes(['SELECT']);
|
||||||
this.database_.setLogger(this.dbLogger_);
|
this.database_.setLogger(this.dbLogger_);
|
||||||
await this.database_.open({ name: profileDir + '/database.sqlite' });
|
await this.database_.open({ name: profileDir + '/database.sqlite' });
|
||||||
|
|
||||||
|
@ -286,7 +286,7 @@ class BaseModel {
|
|||||||
|
|
||||||
return this.db().transactionExecBatch(queries).then(() => {
|
return this.db().transactionExecBatch(queries).then(() => {
|
||||||
o = Object.assign({}, o);
|
o = Object.assign({}, o);
|
||||||
o.id = modelId;
|
if (modelId) o.id = modelId;
|
||||||
if ('updated_time' in saveQuery.modObject) o.updated_time = saveQuery.modObject.updated_time;
|
if ('updated_time' in saveQuery.modObject) o.updated_time = saveQuery.modObject.updated_time;
|
||||||
if ('created_time' in saveQuery.modObject) o.created_time = saveQuery.modObject.created_time;
|
if ('created_time' in saveQuery.modObject) o.created_time = saveQuery.modObject.created_time;
|
||||||
if ('user_updated_time' in saveQuery.modObject) o.user_updated_time = saveQuery.modObject.user_updated_time;
|
if ('user_updated_time' in saveQuery.modObject) o.user_updated_time = saveQuery.modObject.user_updated_time;
|
||||||
@ -360,6 +360,7 @@ BaseModel.TYPE_RESOURCE = 4;
|
|||||||
BaseModel.TYPE_TAG = 5;
|
BaseModel.TYPE_TAG = 5;
|
||||||
BaseModel.TYPE_NOTE_TAG = 6;
|
BaseModel.TYPE_NOTE_TAG = 6;
|
||||||
BaseModel.TYPE_SEARCH = 7;
|
BaseModel.TYPE_SEARCH = 7;
|
||||||
|
BaseModel.TYPE_ALARM = 8;
|
||||||
|
|
||||||
BaseModel.db_ = null;
|
BaseModel.db_ = null;
|
||||||
BaseModel.dispatch = function(o) {};
|
BaseModel.dispatch = function(o) {};
|
||||||
|
@ -30,6 +30,7 @@ const { DocumentPicker, DocumentPickerUtil } = require('react-native-document-pi
|
|||||||
const ImageResizer = require('react-native-image-resizer').default;
|
const ImageResizer = require('react-native-image-resizer').default;
|
||||||
const shared = require('lib/components/shared/note-screen-shared.js');
|
const shared = require('lib/components/shared/note-screen-shared.js');
|
||||||
const ImagePicker = require('react-native-image-picker');
|
const ImagePicker = require('react-native-image-picker');
|
||||||
|
const AlarmService = require('lib/services/AlarmService.js');
|
||||||
const { SelectDateTimeDialog } = require('lib/components/select-date-time-dialog.js');
|
const { SelectDateTimeDialog } = require('lib/components/select-date-time-dialog.js');
|
||||||
|
|
||||||
class NoteScreenComponent extends BaseScreenComponent {
|
class NoteScreenComponent extends BaseScreenComponent {
|
||||||
@ -347,12 +348,9 @@ class NoteScreenComponent extends BaseScreenComponent {
|
|||||||
let newNote = Object.assign({}, this.state.note);
|
let newNote = Object.assign({}, this.state.note);
|
||||||
newNote.todo_due = date ? date.getTime() : 0;
|
newNote.todo_due = date ? date.getTime() : 0;
|
||||||
|
|
||||||
this.setState({
|
await this.saveOneProperty('todo_due', date ? date.getTime() : 0);
|
||||||
alarmDialogShown: false,
|
|
||||||
note: newNote,
|
this.setState({ alarmDialogShown: false });
|
||||||
});
|
|
||||||
//await this.saveOneProperty('todo_due', date ? date.getTime() : 0);
|
|
||||||
//this.forceUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onAlarmDialogReject() {
|
onAlarmDialogReject() {
|
||||||
@ -389,14 +387,12 @@ class NoteScreenComponent extends BaseScreenComponent {
|
|||||||
output.push({ title: _('Attach image'), onPress: () => { this.attachImage_onPress(); } });
|
output.push({ title: _('Attach image'), onPress: () => { this.attachImage_onPress(); } });
|
||||||
output.push({ title: _('Attach any other file'), onPress: () => { this.attachFile_onPress(); } });
|
output.push({ title: _('Attach any other file'), onPress: () => { this.attachFile_onPress(); } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isTodo) {
|
||||||
|
output.push({ title: _('Set or clear alarm'), onPress: () => { this.setState({ alarmDialogShown: true }) }});;
|
||||||
|
}
|
||||||
|
|
||||||
output.push({ title: _('Delete note'), onPress: () => { this.deleteNote_onPress(); } });
|
output.push({ title: _('Delete note'), onPress: () => { this.deleteNote_onPress(); } });
|
||||||
output.push({ title: _('Alarm'), onPress: () => { this.setState({ alarmDialogShown: true }) }});;
|
|
||||||
|
|
||||||
// if (isTodo) {
|
|
||||||
// let text = note.todo_due ? _('Edit/Clear alarm') : _('Set an alarm');
|
|
||||||
// output.push({ title: text, onPress: () => { this.setAlarm_onPress(); } });
|
|
||||||
// }
|
|
||||||
|
|
||||||
output.push({ title: isTodo ? _('Convert to regular note') : _('Convert to todo'), onPress: () => { this.toggleIsTodo_onPress(); } });
|
output.push({ title: isTodo ? _('Convert to regular note') : _('Convert to todo'), onPress: () => { this.toggleIsTodo_onPress(); } });
|
||||||
if (this.props.showAdvancedOptions) output.push({ title: this.state.showNoteMetadata ? _('Hide metadata') : _('Show metadata'), onPress: () => { this.showMetadata_onPress(); } });
|
if (this.props.showAdvancedOptions) output.push({ title: this.state.showNoteMetadata ? _('Hide metadata') : _('Show metadata'), onPress: () => { this.showMetadata_onPress(); } });
|
||||||
output.push({ title: _('View location on map'), onPress: () => { this.showOnMap_onPress(); } });
|
output.push({ title: _('View location on map'), onPress: () => { this.showOnMap_onPress(); } });
|
||||||
|
@ -33,12 +33,22 @@ shared.saveNoteButton_press = async function(comp) {
|
|||||||
if (isNew && !note.title) {
|
if (isNew && !note.title) {
|
||||||
note.title = Note.defaultTitle(note);
|
note.title = Note.defaultTitle(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
note = await Note.save(note);
|
// Save only the properties that have changed
|
||||||
|
const diff = BaseModel.diffObjects(comp.state.lastSavedNote, note);
|
||||||
|
diff.type_ = note.type_;
|
||||||
|
diff.id = note.id;
|
||||||
|
|
||||||
|
const savedNote = await Note.save(diff);
|
||||||
|
|
||||||
|
// Re-assign any property that might have changed during saving (updated_time, etc.)
|
||||||
|
note = Object.assign(note, savedNote);
|
||||||
|
|
||||||
comp.setState({
|
comp.setState({
|
||||||
lastSavedNote: Object.assign({}, note),
|
lastSavedNote: Object.assign({}, note),
|
||||||
note: note,
|
note: note,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isNew) Note.updateGeolocation(note.id);
|
if (isNew) Note.updateGeolocation(note.id);
|
||||||
comp.refreshNoteMetadata();
|
comp.refreshNoteMetadata();
|
||||||
}
|
}
|
||||||
|
@ -67,6 +67,10 @@ class DatabaseDriverNode {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastInsertId() {
|
||||||
|
throw new Error('NOT IMPLEMENTED');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { DatabaseDriverNode };
|
module.exports = { DatabaseDriverNode };
|
@ -2,6 +2,10 @@ const SQLite = require('react-native-sqlite-storage');
|
|||||||
|
|
||||||
class DatabaseDriverReactNative {
|
class DatabaseDriverReactNative {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.lastInsertId_ = null;
|
||||||
|
}
|
||||||
|
|
||||||
open(options) {
|
open(options) {
|
||||||
//SQLite.DEBUG(true);
|
//SQLite.DEBUG(true);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@ -45,6 +49,7 @@ class DatabaseDriverReactNative {
|
|||||||
exec(sql, params = null) {
|
exec(sql, params = null) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.db_.executeSql(sql, params, (r) => {
|
this.db_.executeSql(sql, params, (r) => {
|
||||||
|
if ('insertId' in r) this.lastInsertId_ = r.insertId;
|
||||||
resolve(r);
|
resolve(r);
|
||||||
}, (error) => {
|
}, (error) => {
|
||||||
reject(error);
|
reject(error);
|
||||||
@ -52,6 +57,10 @@ class DatabaseDriverReactNative {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastInsertId() {
|
||||||
|
return this.lastInsertId_;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { DatabaseDriverReactNative };
|
module.exports = { DatabaseDriverReactNative };
|
@ -78,8 +78,10 @@ class Database {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error && (error.code == 'SQLITE_IOERR' || error.code == 'SQLITE_BUSY')) {
|
if (error && (error.code == 'SQLITE_IOERR' || error.code == 'SQLITE_BUSY')) {
|
||||||
if (totalWaitTime >= 20000) throw this.sqliteErrorToJsError(error, sql, params);
|
if (totalWaitTime >= 20000) throw this.sqliteErrorToJsError(error, sql, params);
|
||||||
this.logger().warn(sprintf('Error %s: will retry in %s milliseconds', error.code, waitTime));
|
// NOTE: don't put logger statements here because it might log to the database, which
|
||||||
this.logger().warn('Error was: ' + error.toString());
|
// could result in an error being thrown again.
|
||||||
|
// this.logger().warn(sprintf('Error %s: will retry in %s milliseconds', error.code, waitTime));
|
||||||
|
// this.logger().warn('Error was: ' + error.toString());
|
||||||
await time.msleep(waitTime);
|
await time.msleep(waitTime);
|
||||||
totalWaitTime += waitTime;
|
totalWaitTime += waitTime;
|
||||||
waitTime *= 1.5;
|
waitTime *= 1.5;
|
||||||
|
@ -160,6 +160,7 @@ class JoplinDatabase extends Database {
|
|||||||
let tableName = tableRows[i].name;
|
let tableName = tableRows[i].name;
|
||||||
if (tableName == 'android_metadata') continue;
|
if (tableName == 'android_metadata') continue;
|
||||||
if (tableName == 'table_fields') continue;
|
if (tableName == 'table_fields') continue;
|
||||||
|
if (tableName == 'sqlite_sequence') continue;
|
||||||
chain.push(() => {
|
chain.push(() => {
|
||||||
return this.selectAll('PRAGMA table_info("' + tableName + '")').then((pragmas) => {
|
return this.selectAll('PRAGMA table_info("' + tableName + '")').then((pragmas) => {
|
||||||
for (let i = 0; i < pragmas.length; i++) {
|
for (let i = 0; i < pragmas.length; i++) {
|
||||||
@ -201,7 +202,7 @@ class JoplinDatabase extends Database {
|
|||||||
// default value and thus might cause problems. In that case, the default value
|
// default value and thus might cause problems. In that case, the default value
|
||||||
// must be set in the synchronizer too.
|
// must be set in the synchronizer too.
|
||||||
|
|
||||||
const existingDatabaseVersions = [0, 1, 2, 3, 4, 5];
|
const existingDatabaseVersions = [0, 1, 2, 3, 4, 5, 6];
|
||||||
|
|
||||||
let currentVersionIndex = existingDatabaseVersions.indexOf(fromVersion);
|
let currentVersionIndex = existingDatabaseVersions.indexOf(fromVersion);
|
||||||
if (currentVersionIndex == existingDatabaseVersions.length - 1) return false;
|
if (currentVersionIndex == existingDatabaseVersions.length - 1) return false;
|
||||||
@ -253,6 +254,11 @@ class JoplinDatabase extends Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (targetVersion == 6) {
|
||||||
|
queries.push('CREATE TABLE alarms (id INTEGER PRIMARY KEY AUTOINCREMENT, note_id TEXT NOT NULL, trigger_time INT NOT NULL)');
|
||||||
|
queries.push('CREATE INDEX alarm_note_id ON alarms (note_id)');
|
||||||
|
}
|
||||||
|
|
||||||
queries.push({ sql: 'UPDATE version SET version = ?', params: [targetVersion] });
|
queries.push({ sql: 'UPDATE version SET version = ?', params: [targetVersion] });
|
||||||
await this.transactionExecBatch(queries);
|
await this.transactionExecBatch(queries);
|
||||||
|
|
||||||
|
31
ReactNativeClient/lib/models/Alarm.js
Normal file
31
ReactNativeClient/lib/models/Alarm.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
const { BaseModel } = require('lib/base-model.js');
|
||||||
|
|
||||||
|
class Alarm extends BaseModel {
|
||||||
|
|
||||||
|
static tableName() {
|
||||||
|
return 'alarms';
|
||||||
|
}
|
||||||
|
|
||||||
|
static modelType() {
|
||||||
|
return BaseModel.TYPE_ALARM;
|
||||||
|
}
|
||||||
|
|
||||||
|
static byNoteId(noteId) {
|
||||||
|
return this.modelSelectOne('SELECT * FROM alarms WHERE note_id = ?', [noteId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async garbageCollect() {
|
||||||
|
// Delete alarms that have already been triggered
|
||||||
|
await this.db().exec('DELETE FROM alarms WHERE trigger_time <= ?', [Date.now()]);
|
||||||
|
|
||||||
|
// Delete alarms that correspond to non-existent notes
|
||||||
|
// https://stackoverflow.com/a/4967229/561309
|
||||||
|
await this.db().exec('DELETE FROM alarms WHERE id IN (SELECT alarms.id FROM alarms LEFT JOIN notes ON alarms.note_id = notes.id WHERE notes.id IS NULL)');
|
||||||
|
|
||||||
|
// TODO: Check for duplicate alarms for a note
|
||||||
|
// const rows = await this.db().exec('SELECT count(*) as note_count, note_id from alarms group by note_id having note_count >= 2');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Alarm;
|
@ -389,6 +389,13 @@ class Note extends BaseItem {
|
|||||||
type: 'NOTE_UPDATE_ONE',
|
type: 'NOTE_UPDATE_ONE',
|
||||||
note: note,
|
note: note,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if ('todo_due' in o || 'todo_completed' in o || 'is_todo' in o || 'is_conflict' in o) {
|
||||||
|
this.dispatch({
|
||||||
|
type: 'EVENT_NOTE_ALARM_FIELD_CHANGE',
|
||||||
|
id: note.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return note;
|
return note;
|
||||||
});
|
});
|
||||||
@ -414,6 +421,14 @@ class Note extends BaseItem {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static dueNotes() {
|
||||||
|
return this.modelSelectAll('SELECT id, title, body, todo_due FROM notes WHERE is_conflict = 0 AND is_todo = 1 AND todo_completed = 0 AND todo_due > ?', [time.unixMs()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static needAlarm(note) {
|
||||||
|
return note.is_todo && !note.todo_completed && note.todo_due >= time.unixMs() && !note.is_conflict;
|
||||||
|
}
|
||||||
|
|
||||||
// Tells whether the conflict between the local and remote note can be ignored.
|
// Tells whether the conflict between the local and remote note can be ignored.
|
||||||
static mustHandleConflict(localNote, remoteNote) {
|
static mustHandleConflict(localNote, remoteNote) {
|
||||||
// That shouldn't happen so throw an exception
|
// That shouldn't happen so throw an exception
|
||||||
|
72
ReactNativeClient/lib/services/AlarmService.js
Normal file
72
ReactNativeClient/lib/services/AlarmService.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
const { Note } = require('lib/models/note.js');
|
||||||
|
const Alarm = require('lib/models/Alarm.js');
|
||||||
|
|
||||||
|
class AlarmService {
|
||||||
|
|
||||||
|
static setDriver(v) {
|
||||||
|
this.driver_ = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static driver() {
|
||||||
|
if (!this.driver_) throw new Error('AlarmService driver not set!');
|
||||||
|
return this.driver_;
|
||||||
|
}
|
||||||
|
|
||||||
|
static setLogger(v) {
|
||||||
|
this.logger_ = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static logger() {
|
||||||
|
return this.logger_;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async updateNoteNotification(noteId, isDeleted = false) {
|
||||||
|
const note = await Note.load(noteId);
|
||||||
|
if (!note && !isDeleted) return;
|
||||||
|
|
||||||
|
let alarm = await Alarm.byNoteId(note.id);
|
||||||
|
let clearAlarm = false;
|
||||||
|
|
||||||
|
if (isDeleted ||
|
||||||
|
!Note.needAlarm(note) ||
|
||||||
|
(alarm && alarm.trigger_time !== note.todo_due))
|
||||||
|
{
|
||||||
|
clearAlarm = !!alarm;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!clearAlarm && alarm) return; // Alarm already exists and set at the right time
|
||||||
|
|
||||||
|
if (clearAlarm) {
|
||||||
|
this.logger().info('Clearing notification for note ' + noteId);
|
||||||
|
await this.driver().clearNotification(alarm.id);
|
||||||
|
await Alarm.delete(alarm.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDeleted || !Note.needAlarm(note)) return;
|
||||||
|
|
||||||
|
await Alarm.save({
|
||||||
|
note_id: note.id,
|
||||||
|
trigger_time: note.todo_due,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reload alarm to get its ID
|
||||||
|
alarm = await Alarm.byNoteId(note.id);
|
||||||
|
|
||||||
|
const notification = {
|
||||||
|
id: alarm.id,
|
||||||
|
date: new Date(note.todo_due),
|
||||||
|
title: note.title,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (note.body) notification.body = note.body;
|
||||||
|
|
||||||
|
this.logger().info('Scheduling notification for note ' + note.id, notification);
|
||||||
|
await this.driver().scheduleNotification(notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: inner notifications (when app is active)
|
||||||
|
// TODO: locale-dependent format
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = AlarmService;
|
23
ReactNativeClient/lib/services/AlarmServiceDriver.android.js
Normal file
23
ReactNativeClient/lib/services/AlarmServiceDriver.android.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
const PushNotification = require('react-native-push-notification');
|
||||||
|
|
||||||
|
class AlarmServiceDriver {
|
||||||
|
|
||||||
|
async clearNotification(id) {
|
||||||
|
PushNotification.cancelLocalNotifications({ id: id });
|
||||||
|
}
|
||||||
|
|
||||||
|
async scheduleNotification(notification) {
|
||||||
|
const androidNotification = {
|
||||||
|
id: notification.id,
|
||||||
|
message: notification.title.substr(0, 100), // No idea what the limits are for title and body but set something reasonable anyway
|
||||||
|
date: notification.date,
|
||||||
|
};
|
||||||
|
|
||||||
|
if ('body' in notification) androidNotification.body = notification.body.substr(0, 512);
|
||||||
|
|
||||||
|
PushNotification.localNotificationSchedule(androidNotification);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = AlarmServiceDriver;
|
23
ReactNativeClient/lib/services/AlarmServiceDriverNode.js
Normal file
23
ReactNativeClient/lib/services/AlarmServiceDriverNode.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
class AlarmServiceDriverNode {
|
||||||
|
|
||||||
|
async clearNotification(id) {
|
||||||
|
console.info('AlarmServiceDriverNode::clearNotification', id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async scheduleNotification(notification) {
|
||||||
|
console.info('AlarmServiceDriverNode::scheduleNotification', notification);
|
||||||
|
|
||||||
|
// const androidNotification = {
|
||||||
|
// id: notification.id,
|
||||||
|
// message: notification.title.substr(0, 100), // No idea what the limits are for title and body but set something reasonable anyway
|
||||||
|
// date: notification.date,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// if ('body' in notification) androidNotification.body = notification.body.substr(0, 512);
|
||||||
|
|
||||||
|
// PushNotification.localNotificationSchedule(androidNotification);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = AlarmServiceDriverNode;
|
@ -419,8 +419,20 @@ class Synchronizer {
|
|||||||
}
|
}
|
||||||
content = await BaseItem.unserialize(content);
|
content = await BaseItem.unserialize(content);
|
||||||
let ItemClass = BaseItem.itemClass(content);
|
let ItemClass = BaseItem.itemClass(content);
|
||||||
|
content = ItemClass.filter(content);
|
||||||
|
|
||||||
|
let newContent = null;
|
||||||
|
|
||||||
|
if (action === 'createLocal') {
|
||||||
|
newContent = Object.assign({}, content);
|
||||||
|
} else if (action === 'updateLocal') {
|
||||||
|
newContent = BaseModel.diffObjects(local, content);
|
||||||
|
newContent.type_ = content.type_;
|
||||||
|
newContent.id = content.id;
|
||||||
|
} else {
|
||||||
|
throw new Error('Unknown action: ' + action);
|
||||||
|
}
|
||||||
|
|
||||||
let newContent = Object.assign({}, content);
|
|
||||||
let options = {
|
let options = {
|
||||||
autoTimestamp: false,
|
autoTimestamp: false,
|
||||||
nextQueries: BaseItem.updateSyncTimeQueries(syncTargetId, newContent, time.unixMs()),
|
nextQueries: BaseItem.updateSyncTimeQueries(syncTargetId, newContent, time.unixMs()),
|
||||||
|
@ -3,11 +3,11 @@ const moment = require('moment');
|
|||||||
let time = {
|
let time = {
|
||||||
|
|
||||||
unix() {
|
unix() {
|
||||||
return Math.floor((new Date()).getTime() / 1000);
|
return Math.floor(Date.now() / 1000);
|
||||||
},
|
},
|
||||||
|
|
||||||
unixMs() {
|
unixMs() {
|
||||||
return (new Date()).getTime();
|
return Date.now();
|
||||||
},
|
},
|
||||||
|
|
||||||
unixMsToObject(ms) {
|
unixMsToObject(ms) {
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
const React = require('react'); const Component = React.Component;
|
const React = require('react'); const Component = React.Component;
|
||||||
const { Keyboard, NativeModules, PushNotificationIOS } = require('react-native');
|
const { Keyboard, NativeModules } = require('react-native');
|
||||||
const { connect, Provider } = require('react-redux');
|
const { connect, Provider } = require('react-redux');
|
||||||
const { BackButtonService } = require('lib/services/back-button.js');
|
const { BackButtonService } = require('lib/services/back-button.js');
|
||||||
|
const AlarmService = require('lib/services/AlarmService.js');
|
||||||
|
const AlarmServiceDriver = require('lib/services/AlarmServiceDriver');
|
||||||
const { createStore, applyMiddleware } = require('redux');
|
const { createStore, applyMiddleware } = require('redux');
|
||||||
const { shimInit } = require('lib/shim-init-react.js');
|
const { shimInit } = require('lib/shim-init-react.js');
|
||||||
const { Log } = require('lib/log.js');
|
const { Log } = require('lib/log.js');
|
||||||
@ -37,7 +39,6 @@ const { _, setLocale, closestSupportedLocale, defaultLocale } = require('lib/loc
|
|||||||
const RNFetchBlob = require('react-native-fetch-blob').default;
|
const RNFetchBlob = require('react-native-fetch-blob').default;
|
||||||
const { PoorManIntervals } = require('lib/poor-man-intervals.js');
|
const { PoorManIntervals } = require('lib/poor-man-intervals.js');
|
||||||
const { reducer, defaultState } = require('lib/reducer.js');
|
const { reducer, defaultState } = require('lib/reducer.js');
|
||||||
const PushNotification = require('react-native-push-notification');
|
|
||||||
|
|
||||||
const SyncTargetRegistry = require('lib/SyncTargetRegistry.js');
|
const SyncTargetRegistry = require('lib/SyncTargetRegistry.js');
|
||||||
const SyncTargetOneDrive = require('lib/SyncTargetOneDrive.js');
|
const SyncTargetOneDrive = require('lib/SyncTargetOneDrive.js');
|
||||||
@ -290,6 +291,9 @@ async function initialize(dispatch, backButtonHandler) {
|
|||||||
BaseItem.loadClass('Tag', Tag);
|
BaseItem.loadClass('Tag', Tag);
|
||||||
BaseItem.loadClass('NoteTag', NoteTag);
|
BaseItem.loadClass('NoteTag', NoteTag);
|
||||||
|
|
||||||
|
AlarmService.setDriver(new AlarmServiceDriver());
|
||||||
|
AlarmService.setLogger(mainLogger);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (Setting.value('env') == 'prod') {
|
if (Setting.value('env') == 'prod') {
|
||||||
await db.open({ name: 'joplin.sqlite' })
|
await db.open({ name: 'joplin.sqlite' })
|
||||||
@ -366,13 +370,14 @@ async function initialize(dispatch, backButtonHandler) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
reg.logger().info('Scheduling iOS notification');
|
|
||||||
|
|
||||||
PushNotificationIOS.scheduleLocalNotification({
|
//reg.logger().info('Scheduling iOS notification');
|
||||||
alertTitle: "From Joplin",
|
|
||||||
alertBody : "Testing notification on iOS",
|
// PushNotificationIOS.scheduleLocalNotification({
|
||||||
fireDate: new Date(Date.now() + (10 * 1000)),
|
// alertTitle: "From Joplin",
|
||||||
});
|
// alertBody : "Testing notification on iOS",
|
||||||
|
// fireDate: new Date(Date.now() + (10 * 1000)),
|
||||||
|
// });
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user