2020-06-02 22:13:15 +02:00
import setUpQuickActions from './setUpQuickActions' ;
import PluginAssetsLoader from './PluginAssetsLoader' ;
2019-07-30 09:35:42 +02:00
const React = require ( 'react' ) ;
2020-10-08 12:35:29 +02:00
const { AppState , Keyboard , NativeModules , BackHandler , Animated , View , StatusBar } = require ( 'react-native' ) ;
2020-11-05 18:58:23 +02:00
const shim = require ( '@joplinapp/lib/shim' ) . default ;
shim . setReact ( React ) ;
const SafeAreaView = require ( './components/SafeAreaView' ) ;
2018-03-09 22:59:12 +02:00
const { connect , Provider } = require ( 'react-redux' ) ;
2020-11-05 18:58:23 +02:00
const { BackButtonService } = require ( './services/back-button.js' ) ;
const NavService = require ( '@joplinapp/lib/services/NavService.js' ) ;
const AlarmService = require ( '@joplinapp/lib/services/AlarmService.js' ) . default ;
const AlarmServiceDriver = require ( './services/AlarmServiceDriver' ) . default ;
const Alarm = require ( '@joplinapp/lib/models/Alarm' ) . default ;
2018-03-09 22:59:12 +02:00
const { createStore , applyMiddleware } = require ( 'redux' ) ;
2020-11-05 18:58:23 +02:00
const reduxSharedMiddleware = require ( '@joplinapp/lib/components/shared/reduxSharedMiddleware' ) ;
const { shimInit } = require ( './utils/shim-init-react.js' ) ;
const time = require ( '@joplinapp/lib/time' ) . default ;
const { AppNav } = require ( './components/app-nav.js' ) ;
const Logger = require ( '@joplinapp/lib/Logger' ) . default ;
const Note = require ( '@joplinapp/lib/models/Note.js' ) ;
const Folder = require ( '@joplinapp/lib/models/Folder.js' ) ;
const BaseSyncTarget = require ( '@joplinapp/lib/BaseSyncTarget.js' ) ;
const { FoldersScreenUtils } = require ( '@joplinapp/lib/folders-screen-utils.js' ) ;
const Resource = require ( '@joplinapp/lib/models/Resource.js' ) ;
const Tag = require ( '@joplinapp/lib/models/Tag.js' ) ;
const NoteTag = require ( '@joplinapp/lib/models/NoteTag.js' ) ;
const BaseItem = require ( '@joplinapp/lib/models/BaseItem.js' ) ;
const MasterKey = require ( '@joplinapp/lib/models/MasterKey.js' ) ;
const Revision = require ( '@joplinapp/lib/models/Revision.js' ) ;
const BaseModel = require ( '@joplinapp/lib/BaseModel' ) . default ;
const BaseService = require ( '@joplinapp/lib/services/BaseService' ) . default ;
const ResourceService = require ( '@joplinapp/lib/services/ResourceService' ) ;
const RevisionService = require ( '@joplinapp/lib/services/RevisionService' ) ;
const KvStore = require ( '@joplinapp/lib/services/KvStore' ) ;
const { JoplinDatabase } = require ( '@joplinapp/lib/joplin-database.js' ) ;
const { Database } = require ( '@joplinapp/lib/database.js' ) ;
const { NotesScreen } = require ( './components/screens/notes.js' ) ;
const { TagsScreen } = require ( './components/screens/tags.js' ) ;
const NoteScreen = require ( './components/screens/Note' ) . default ;
const { ConfigScreen } = require ( './components/screens/config.js' ) ;
const { FolderScreen } = require ( './components/screens/folder.js' ) ;
const { LogScreen } = require ( './components/screens/log.js' ) ;
const { StatusScreen } = require ( './components/screens/status.js' ) ;
const { SearchScreen } = require ( './components/screens/search.js' ) ;
const { OneDriveLoginScreen } = require ( './components/screens/onedrive-login.js' ) ;
const { EncryptionConfigScreen } = require ( './components/screens/encryption-config.js' ) ;
const { DropboxLoginScreen } = require ( './components/screens/dropbox-login.js' ) ;
const UpgradeSyncTargetScreen = require ( './components/screens/UpgradeSyncTargetScreen' ) . default ;
const Setting = require ( '@joplinapp/lib/models/Setting' ) . default ;
2018-03-09 22:59:12 +02:00
const { MenuContext } = require ( 'react-native-popup-menu' ) ;
2020-11-05 18:58:23 +02:00
const { SideMenu } = require ( './components/side-menu.js' ) ;
const { SideMenuContent } = require ( './components/side-menu-content.js' ) ;
const { SideMenuContentNote } = require ( './components/side-menu-content-note.js' ) ;
const { DatabaseDriverReactNative } = require ( './utils/database-driver-react-native' ) ;
const { reg } = require ( '@joplinapp/lib/registry.js' ) ;
const { setLocale , closestSupportedLocale , defaultLocale } = require ( '@joplinapp/lib/locale' ) ;
2018-10-07 19:55:49 +02:00
const RNFetchBlob = require ( 'rn-fetch-blob' ) . default ;
2020-11-05 18:58:23 +02:00
const PoorManIntervals = require ( '@joplinapp/lib/PoorManIntervals' ) . default ;
const reducer = require ( '@joplinapp/lib/reducer' ) . default ;
const { defaultState } = require ( '@joplinapp/lib/reducer' ) ;
const { FileApiDriverLocal } = require ( '@joplinapp/lib/file-api-driver-local.js' ) ;
2018-03-09 22:59:12 +02:00
const DropdownAlert = require ( 'react-native-dropdownalert' ) . default ;
2020-11-05 18:58:23 +02:00
const ShareExtension = require ( './utils/ShareExtension.js' ) . default ;
const handleShared = require ( './utils/shareHandler' ) . default ;
const ResourceFetcher = require ( '@joplinapp/lib/services/ResourceFetcher' ) ;
const SearchEngine = require ( '@joplinapp/lib/services/searchengine/SearchEngine' ) ;
const WelcomeUtils = require ( '@joplinapp/lib/WelcomeUtils' ) ;
const { themeStyle } = require ( './components/global-style.js' ) ;
const uuid = require ( '@joplinapp/lib/uuid' ) . default ;
const { loadKeychainServiceAndSettings } = require ( '@joplinapp/lib/services/SettingUtils' ) ;
const KeychainServiceDriverMobile = require ( '@joplinapp/lib/services/keychain/KeychainServiceDriver.mobile' ) . default ;
const SyncTargetRegistry = require ( '@joplinapp/lib/SyncTargetRegistry.js' ) ;
const SyncTargetOneDrive = require ( '@joplinapp/lib/SyncTargetOneDrive.js' ) ;
const SyncTargetFilesystem = require ( '@joplinapp/lib/SyncTargetFilesystem.js' ) ;
const SyncTargetNextcloud = require ( '@joplinapp/lib/SyncTargetNextcloud.js' ) ;
const SyncTargetWebDAV = require ( '@joplinapp/lib/SyncTargetWebDAV.js' ) ;
const SyncTargetDropbox = require ( '@joplinapp/lib/SyncTargetDropbox.js' ) ;
const SyncTargetAmazonS3 = require ( '@joplinapp/lib/SyncTargetAmazonS3.js' ) ;
2020-02-08 13:59:19 +02:00
2017-11-24 21:21:30 +02:00
SyncTargetRegistry . addClass ( SyncTargetOneDrive ) ;
2018-01-25 21:01:14 +02:00
SyncTargetRegistry . addClass ( SyncTargetNextcloud ) ;
2018-02-02 01:40:05 +02:00
SyncTargetRegistry . addClass ( SyncTargetWebDAV ) ;
2018-03-27 01:55:44 +02:00
SyncTargetRegistry . addClass ( SyncTargetDropbox ) ;
2018-05-02 11:27:37 +02:00
SyncTargetRegistry . addClass ( SyncTargetFilesystem ) ;
2020-07-29 00:47:24 +02:00
SyncTargetRegistry . addClass ( SyncTargetAmazonS3 ) ;
2018-01-17 23:01:41 +02:00
2020-11-05 18:58:23 +02:00
const FsDriverRN = require ( './utils/fs-driver-rn.js' ) . FsDriverRN ;
const DecryptionWorker = require ( '@joplinapp/lib/services/DecryptionWorker' ) ;
const EncryptionService = require ( '@joplinapp/lib/services/EncryptionService' ) ;
const MigrationService = require ( '@joplinapp/lib/services/MigrationService' ) ;
2017-12-30 21:57:34 +02:00
2019-09-13 00:16:42 +02:00
let storeDispatch = function ( ) { } ;
2018-01-02 21:17:14 +02:00
2018-04-16 15:15:29 +02:00
const logReducerAction = function ( action ) {
if ( [ 'SIDE_MENU_OPEN_PERCENT' , 'SYNC_REPORT_UPDATE' ] . indexOf ( action . type ) >= 0 ) return ;
2020-03-14 01:46:14 +02:00
const msg = [ action . type ] ;
2018-04-16 15:15:29 +02:00
if ( action . routeName ) msg . push ( action . routeName ) ;
2019-05-28 23:05:11 +02:00
// reg.logger().debug('Reducer action', msg.join(', '));
2019-07-30 09:35:42 +02:00
} ;
2018-04-16 15:15:29 +02:00
2020-05-21 10:14:33 +02:00
const generalMiddleware = store => next => async ( action ) => {
2018-04-16 15:15:29 +02:00
logReducerAction ( action ) ;
2017-07-25 20:41:53 +02:00
PoorManIntervals . update ( ) ; // This function needs to be called regularly so put it here
const result = next ( action ) ;
2017-07-26 19:49:01 +02:00
const newState = store . getState ( ) ;
2017-07-25 20:41:53 +02:00
2019-02-24 20:02:50 +02:00
await reduxSharedMiddleware ( store , next , action ) ;
2018-05-09 13:39:17 +02:00
2019-07-30 09:35:42 +02:00
if ( action . type == 'NAV_GO' ) Keyboard . dismiss ( ) ;
2017-07-25 20:41:53 +02:00
2019-07-30 09:35:42 +02:00
if ( [ 'NOTE_UPDATE_ONE' , 'NOTE_DELETE' , 'FOLDER_UPDATE_ONE' , 'FOLDER_DELETE' ] . indexOf ( action . type ) >= 0 ) {
if ( ! await reg . syncTarget ( ) . syncStarted ( ) ) reg . scheduleSync ( 5 * 1000 , { syncSteps : [ 'update_remote' , 'delete_remote' ] } ) ;
2019-01-18 20:31:07 +02:00
SearchEngine . instance ( ) . scheduleSyncTables ( ) ;
2017-07-26 19:49:01 +02:00
}
2018-03-09 22:59:12 +02:00
if ( [ 'EVENT_NOTE_ALARM_FIELD_CHANGE' , 'NOTE_DELETE' ] . indexOf ( action . type ) >= 0 ) {
await AlarmService . updateNoteNotification ( action . id , action . type === 'NOTE_DELETE' ) ;
2017-11-28 20:58:04 +02:00
}
2018-03-09 22:59:12 +02:00
if ( action . type == 'SETTING_UPDATE_ONE' && action . key == 'sync.interval' || action . type == 'SETTING_UPDATE_ALL' ) {
2017-07-26 19:49:01 +02:00
reg . setupRecurrentSync ( ) ;
}
2018-03-09 22:59:12 +02:00
if ( ( action . type == 'SETTING_UPDATE_ONE' && ( action . key == 'dateFormat' || action . key == 'timeFormat' ) ) || ( action . type == 'SETTING_UPDATE_ALL' ) ) {
time . setDateFormat ( Setting . value ( 'dateFormat' ) ) ;
time . setTimeFormat ( Setting . value ( 'timeFormat' ) ) ;
2017-11-28 20:02:54 +02:00
}
2018-03-09 22:59:12 +02:00
if ( action . type == 'SETTING_UPDATE_ONE' && action . key == 'locale' || action . type == 'SETTING_UPDATE_ALL' ) {
setLocale ( Setting . value ( 'locale' ) ) ;
2018-01-02 21:17:14 +02:00
}
2018-03-09 22:59:12 +02:00
if ( ( action . type == 'SETTING_UPDATE_ONE' && ( action . key . indexOf ( 'encryption.' ) === 0 ) ) || ( action . type == 'SETTING_UPDATE_ALL' ) ) {
2018-01-02 21:17:14 +02:00
await EncryptionService . instance ( ) . loadMasterKeysFromSettings ( ) ;
DecryptionWorker . instance ( ) . scheduleStart ( ) ;
const loadedMasterKeyIds = EncryptionService . instance ( ) . loadedMasterKeyIds ( ) ;
storeDispatch ( {
2018-03-09 22:59:12 +02:00
type : 'MASTERKEY_REMOVE_NOT_LOADED' ,
2018-01-02 21:17:14 +02:00
ids : loadedMasterKeyIds ,
} ) ;
2018-01-08 22:25:38 +02:00
// Schedule a sync operation so that items that need to be encrypted
// are sent to sync target.
reg . scheduleSync ( ) ;
2018-01-02 21:17:14 +02:00
}
2017-11-03 20:51:13 +02:00
2018-03-09 22:59:12 +02:00
if ( action . type == 'NAV_GO' && action . routeName == 'Notes' ) {
Setting . setValue ( 'activeFolderId' , newState . selectedFolderId ) ;
2017-07-25 20:41:53 +02:00
}
2018-03-09 22:59:12 +02:00
if ( action . type === 'SYNC_GOT_ENCRYPTED_ITEM' ) {
2017-12-30 21:57:34 +02:00
DecryptionWorker . instance ( ) . scheduleStart ( ) ;
}
2020-05-31 01:31:29 +02:00
if ( action . type === 'SYNC_CREATED_OR_UPDATED_RESOURCE' ) {
2019-05-22 16:56:07 +02:00
ResourceFetcher . instance ( ) . autoAddResources ( ) ;
2018-10-09 23:01:50 +02:00
}
2019-07-30 09:35:42 +02:00
return result ;
} ;
2017-07-25 20:41:53 +02:00
2020-03-14 01:46:14 +02:00
const navHistory = [ ] ;
2017-11-06 20:05:12 +02:00
2019-09-13 00:16:42 +02:00
function historyCanGoBackTo ( route ) {
2018-03-18 01:00:01 +02:00
if ( route . routeName === 'Note' ) return false ;
2018-03-16 22:17:52 +02:00
if ( route . routeName === 'Folder' ) return false ;
2017-11-06 20:05:12 +02:00
2018-09-23 21:45:34 +02:00
// There's no point going back to these screens in general and, at least in OneDrive case,
2019-07-30 09:35:42 +02:00
// it can be buggy to do so, due to incorrectly relying on global state (reg.syncTarget...)
2018-09-23 21:45:34 +02:00
if ( route . routeName === 'OneDriveLogin' ) return false ;
if ( route . routeName === 'DropboxLogin' ) return false ;
2017-11-06 20:05:12 +02:00
return true ;
}
2019-06-29 01:24:00 +02:00
const DEFAULT _ROUTE = {
type : 'NAV_GO' ,
routeName : 'Notes' ,
smartFilterId : 'c3176726992c11e9ac940492261af972' ,
} ;
2017-11-06 20:35:04 +02:00
const appDefaultState = Object . assign ( { } , defaultState , {
sideMenuOpenPercent : 0 ,
2019-06-29 01:24:00 +02:00
route : DEFAULT _ROUTE ,
2017-11-23 20:47:51 +02:00
noteSelectionEnabled : false ,
2019-07-11 18:43:55 +02:00
noteSideMenuOptions : null ,
2017-11-06 20:35:04 +02:00
} ) ;
const appReducer = ( state = appDefaultState , action ) => {
2017-11-06 20:05:12 +02:00
let newState = state ;
let historyGoingBack = false ;
try {
switch ( action . type ) {
2018-03-09 22:59:12 +02:00
2019-07-30 09:35:42 +02:00
case 'NAV_BACK' :
2018-03-09 22:59:12 +02:00
2019-07-30 09:35:42 +02:00
{
if ( ! navHistory . length ) break ;
2017-11-06 20:05:12 +02:00
2019-07-30 09:35:42 +02:00
let newAction = null ;
while ( navHistory . length ) {
newAction = navHistory . pop ( ) ;
if ( newAction . routeName != state . route . routeName ) break ;
}
2017-11-06 20:05:12 +02:00
2019-07-30 09:35:42 +02:00
action = newAction ? newAction : navHistory . pop ( ) ;
2017-11-06 20:05:12 +02:00
2019-07-30 09:35:42 +02:00
historyGoingBack = true ;
}
2017-11-06 20:05:12 +02:00
2019-07-30 09:35:42 +02:00
// Fall throught
2018-03-09 22:59:12 +02:00
2019-07-30 09:35:42 +02:00
case 'NAV_GO' :
2017-11-06 20:05:12 +02:00
2019-07-30 09:35:42 +02:00
{
2017-11-06 20:05:12 +02:00
const currentRoute = state . route ;
2018-03-16 22:17:52 +02:00
if ( ! historyGoingBack && historyCanGoBackTo ( currentRoute , action ) ) {
2020-02-19 02:09:19 +02:00
// If the route *name* is the same (even if the other parameters are different), we
// overwrite the last route in the history with the current one. If the route name
// is different, we push a new history entry.
2017-11-06 20:05:12 +02:00
if ( currentRoute . routeName == action . routeName ) {
2019-07-30 09:35:42 +02:00
// nothing
2017-11-06 20:05:12 +02:00
} else {
navHistory . push ( currentRoute ) ;
}
}
// HACK: whenever a new screen is loaded, all the previous screens of that type
// are overwritten with the new screen parameters. This is because the way notes
// are currently loaded is not optimal (doesn't retain history properly) so
// this is a simple fix without doing a big refactoring to change the way notes
// are loaded. Might be good enough since going back to different folders
// is probably not a common workflow.
for ( let i = 0 ; i < navHistory . length ; i ++ ) {
2020-03-14 01:46:14 +02:00
const n = navHistory [ i ] ;
2017-11-06 20:05:12 +02:00
if ( n . routeName == action . routeName ) {
navHistory [ i ] = Object . assign ( { } , action ) ;
}
}
newState = Object . assign ( { } , state ) ;
2019-09-09 19:16:00 +02:00
newState . selectedNoteHash = '' ;
2018-03-09 22:59:12 +02:00
if ( 'noteId' in action ) {
2017-11-22 20:35:31 +02:00
newState . selectedNoteIds = action . noteId ? [ action . noteId ] : [ ] ;
2017-11-06 20:05:12 +02:00
}
2018-03-09 22:59:12 +02:00
if ( 'folderId' in action ) {
2017-11-06 20:05:12 +02:00
newState . selectedFolderId = action . folderId ;
2018-03-09 22:59:12 +02:00
newState . notesParentType = 'Folder' ;
2017-11-06 20:05:12 +02:00
}
2018-03-09 22:59:12 +02:00
if ( 'tagId' in action ) {
2017-11-06 20:05:12 +02:00
newState . selectedTagId = action . tagId ;
2018-03-09 22:59:12 +02:00
newState . notesParentType = 'Tag' ;
2017-11-06 20:05:12 +02:00
}
2019-06-28 01:51:02 +02:00
if ( 'smartFilterId' in action ) {
newState . smartFilterId = action . smartFilterId ;
newState . notesParentType = 'SmartFilter' ;
}
2018-03-09 22:59:12 +02:00
if ( 'itemType' in action ) {
2017-11-06 20:05:12 +02:00
newState . selectedItemType = action . itemType ;
}
2019-09-09 19:16:00 +02:00
if ( 'noteHash' in action ) {
newState . selectedNoteHash = action . noteHash ;
}
2018-07-20 11:04:25 +02:00
if ( 'sharedData' in action ) {
newState . sharedData = action . sharedData ;
} else {
newState . sharedData = null ;
}
2017-11-06 20:05:12 +02:00
newState . route = action ;
newState . historyCanGoBack = ! ! navHistory . length ;
2019-07-30 09:35:42 +02:00
}
break ;
2017-11-06 20:05:12 +02:00
2019-07-30 09:35:42 +02:00
case 'SIDE_MENU_TOGGLE' :
2018-03-09 22:59:12 +02:00
2019-07-30 09:35:42 +02:00
newState = Object . assign ( { } , state ) ;
newState . showSideMenu = ! newState . showSideMenu ;
break ;
2017-11-06 20:05:12 +02:00
2019-07-30 09:35:42 +02:00
case 'SIDE_MENU_OPEN' :
2018-03-09 22:59:12 +02:00
2019-07-30 09:35:42 +02:00
newState = Object . assign ( { } , state ) ;
newState . showSideMenu = true ;
break ;
2017-11-06 20:05:12 +02:00
2019-07-30 09:35:42 +02:00
case 'SIDE_MENU_CLOSE' :
2018-03-09 22:59:12 +02:00
2019-07-30 09:35:42 +02:00
newState = Object . assign ( { } , state ) ;
newState . showSideMenu = false ;
break ;
2017-11-06 20:05:12 +02:00
2019-07-30 09:35:42 +02:00
case 'SIDE_MENU_OPEN_PERCENT' :
2018-03-09 22:59:12 +02:00
2019-07-30 09:35:42 +02:00
newState = Object . assign ( { } , state ) ;
newState . sideMenuOpenPercent = action . value ;
break ;
2017-11-06 20:05:12 +02:00
2019-07-30 09:35:42 +02:00
case 'NOTE_SELECTION_TOGGLE' :
2018-03-09 22:59:12 +02:00
2019-07-30 09:35:42 +02:00
{
2017-11-23 20:47:51 +02:00
newState = Object . assign ( { } , state ) ;
const noteId = action . id ;
const newSelectedNoteIds = state . selectedNoteIds . slice ( ) ;
const existingIndex = state . selectedNoteIds . indexOf ( noteId ) ;
if ( existingIndex >= 0 ) {
newSelectedNoteIds . splice ( existingIndex , 1 ) ;
} else {
newSelectedNoteIds . push ( noteId ) ;
}
newState . selectedNoteIds = newSelectedNoteIds ;
newState . noteSelectionEnabled = ! ! newSelectedNoteIds . length ;
2019-07-30 09:35:42 +02:00
}
break ;
2017-11-23 20:47:51 +02:00
2019-07-30 09:35:42 +02:00
case 'NOTE_SELECTION_START' :
2018-03-09 22:59:12 +02:00
2019-07-30 09:35:42 +02:00
if ( ! state . noteSelectionEnabled ) {
newState = Object . assign ( { } , state ) ;
newState . noteSelectionEnabled = true ;
newState . selectedNoteIds = [ action . id ] ;
}
break ;
2017-11-23 20:47:51 +02:00
2019-07-30 09:35:42 +02:00
case 'NOTE_SELECTION_END' :
2018-03-09 22:59:12 +02:00
2019-07-30 09:35:42 +02:00
newState = Object . assign ( { } , state ) ;
newState . noteSelectionEnabled = false ;
newState . selectedNoteIds = [ ] ;
break ;
2018-03-09 22:59:12 +02:00
2019-07-30 09:35:42 +02:00
case 'NOTE_SIDE_MENU_OPTIONS_SET' :
2019-07-11 18:43:55 +02:00
2019-07-30 09:35:42 +02:00
newState = Object . assign ( { } , state ) ;
newState . noteSideMenuOptions = action . options ;
break ;
2018-03-09 22:59:12 +02:00
2017-11-06 20:05:12 +02:00
}
} catch ( error ) {
2019-09-19 23:51:18 +02:00
error . message = ` In reducer: ${ error . message } Action: ${ JSON . stringify ( action ) } ` ;
2017-11-06 20:05:12 +02:00
throw error ;
}
return reducer ( newState , action ) ;
2019-07-30 09:35:42 +02:00
} ;
2017-11-06 20:05:12 +02:00
2020-03-14 01:46:14 +02:00
const store = createStore ( appReducer , applyMiddleware ( generalMiddleware ) ) ;
2018-01-02 21:17:14 +02:00
storeDispatch = store . dispatch ;
2017-05-09 22:46:54 +02:00
2019-05-22 16:56:07 +02:00
function resourceFetcher _downloadComplete ( event ) {
if ( event . encrypted ) {
DecryptionWorker . instance ( ) . scheduleStart ( ) ;
}
}
2020-06-09 21:56:48 +02:00
function decryptionWorker _resourceMetadataButNotBlobDecrypted ( ) {
ResourceFetcher . instance ( ) . scheduleAutoAddResources ( ) ;
}
2020-10-08 12:35:29 +02:00
async function initialize ( dispatch ) {
2017-07-10 20:09:58 +02:00
shimInit ( ) ;
2017-07-07 19:19:24 +02:00
2018-03-09 22:59:12 +02:00
Setting . setConstant ( 'env' , _ _DEV _ _ ? 'dev' : 'prod' ) ;
Setting . setConstant ( 'appId' , 'net.cozic.joplin-mobile' ) ;
Setting . setConstant ( 'appType' , 'mobile' ) ;
Setting . setConstant ( 'resourceDir' , RNFetchBlob . fs . dirs . DocumentDir ) ;
2017-07-10 00:57:15 +02:00
const logDatabase = new Database ( new DatabaseDriverReactNative ( ) ) ;
2018-03-09 22:59:12 +02:00
await logDatabase . open ( { name : 'log.sqlite' } ) ;
2017-07-10 00:57:15 +02:00
await logDatabase . exec ( Logger . databaseCreateTableSql ( ) ) ;
2017-07-15 01:12:32 +02:00
const mainLogger = new Logger ( ) ;
2018-03-09 22:59:12 +02:00
mainLogger . addTarget ( 'database' , { database : logDatabase , source : 'm' } ) ;
2017-12-31 15:58:50 +02:00
mainLogger . setLevel ( Logger . LEVEL _INFO ) ;
2019-07-30 09:35:42 +02:00
2018-03-09 22:59:12 +02:00
if ( Setting . value ( 'env' ) == 'dev' ) {
mainLogger . addTarget ( 'console' ) ;
2017-12-31 15:58:50 +02:00
mainLogger . setLevel ( Logger . LEVEL _DEBUG ) ;
}
2017-07-15 01:12:32 +02:00
reg . setLogger ( mainLogger ) ;
2019-07-30 09:35:42 +02:00
reg . setShowErrorMessageBoxHandler ( ( message ) => { alert ( message ) ; } ) ;
2017-07-10 00:57:15 +02:00
2018-03-15 20:08:46 +02:00
BaseService . logger _ = mainLogger ;
2020-11-05 18:58:23 +02:00
// require('@joplinapp/lib/ntpDate').setLogger(reg.logger());
2018-03-15 20:08:46 +02:00
2018-03-09 22:59:12 +02:00
reg . logger ( ) . info ( '====================================' ) ;
2019-09-19 23:51:18 +02:00
reg . logger ( ) . info ( ` Starting application ${ Setting . value ( 'appId' ) } ( ${ Setting . value ( 'env' ) } ) ` ) ;
2017-07-10 00:57:15 +02:00
2017-07-15 01:12:32 +02:00
const dbLogger = new Logger ( ) ;
2019-07-30 09:35:42 +02:00
dbLogger . addTarget ( 'database' , { database : logDatabase , source : 'm' } ) ;
2018-03-09 22:59:12 +02:00
if ( Setting . value ( 'env' ) == 'dev' ) {
dbLogger . addTarget ( 'console' ) ;
2017-07-18 00:43:29 +02:00
dbLogger . setLevel ( Logger . LEVEL _INFO ) ; // Set to LEVEL_DEBUG for full SQL queries
2017-07-15 19:08:54 +02:00
} else {
dbLogger . setLevel ( Logger . LEVEL _INFO ) ;
}
2017-07-15 01:12:32 +02:00
2020-03-14 01:46:14 +02:00
const db = new JoplinDatabase ( new DatabaseDriverReactNative ( ) ) ;
2017-07-15 01:12:32 +02:00
db . setLogger ( dbLogger ) ;
2017-07-10 00:57:15 +02:00
reg . setDb ( db ) ;
2017-07-18 00:43:29 +02:00
reg . dispatch = dispatch ;
2017-07-10 00:57:15 +02:00
BaseModel . dispatch = dispatch ;
2017-07-15 17:54:19 +02:00
FoldersScreenUtils . dispatch = dispatch ;
2017-11-24 20:06:30 +02:00
BaseSyncTarget . dispatch = dispatch ;
2018-02-22 00:08:34 +02:00
NavService . dispatch = dispatch ;
2020-03-16 04:30:54 +02:00
BaseModel . setDb ( db ) ;
2017-07-10 00:57:15 +02:00
2019-06-08 00:11:08 +02:00
KvStore . instance ( ) . setDb ( reg . db ( ) ) ;
2018-03-09 22:59:12 +02:00
BaseItem . loadClass ( 'Note' , Note ) ;
BaseItem . loadClass ( 'Folder' , Folder ) ;
BaseItem . loadClass ( 'Resource' , Resource ) ;
BaseItem . loadClass ( 'Tag' , Tag ) ;
BaseItem . loadClass ( 'NoteTag' , NoteTag ) ;
BaseItem . loadClass ( 'MasterKey' , MasterKey ) ;
2019-05-06 22:35:29 +02:00
BaseItem . loadClass ( 'Revision' , Revision ) ;
2017-07-10 00:57:15 +02:00
2017-12-30 21:57:34 +02:00
const fsDriver = new FsDriverRN ( ) ;
Resource . fsDriver _ = fsDriver ;
2018-01-17 23:01:41 +02:00
FileApiDriverLocal . fsDriver _ = fsDriver ;
2017-12-30 21:57:34 +02:00
2020-10-16 17:26:19 +02:00
AlarmService . setDriver ( new AlarmServiceDriver ( mainLogger ) ) ;
2017-11-28 00:50:46 +02:00
AlarmService . setLogger ( mainLogger ) ;
2017-07-10 00:57:15 +02:00
try {
2018-03-09 22:59:12 +02:00
if ( Setting . value ( 'env' ) == 'prod' ) {
2019-07-30 09:35:42 +02:00
await db . open ( { name : 'joplin.sqlite' } ) ;
2017-07-10 00:57:15 +02:00
} else {
2020-09-04 18:28:18 +02:00
await db . open ( { name : 'joplin-76.sqlite' } ) ;
2019-05-22 16:56:07 +02:00
2019-05-26 20:39:07 +02:00
// await db.clearForTesting();
2017-07-10 00:57:15 +02:00
}
2017-05-16 23:46:21 +02:00
2018-03-09 22:59:12 +02:00
reg . logger ( ) . info ( 'Database is ready.' ) ;
reg . logger ( ) . info ( 'Loading settings...' ) ;
2020-06-05 01:08:09 +02:00
await loadKeychainServiceAndSettings ( KeychainServiceDriverMobile ) ;
2017-07-25 20:57:06 +02:00
2019-10-10 23:23:11 +02:00
if ( ! Setting . value ( 'clientId' ) ) Setting . setValue ( 'clientId' , uuid . create ( ) ) ;
2018-03-09 22:59:12 +02:00
if ( Setting . value ( 'firstStart' ) ) {
2019-07-30 09:35:42 +02:00
let locale = NativeModules . I18nManager . localeIdentifier ;
2017-07-25 20:57:06 +02:00
if ( ! locale ) locale = defaultLocale ( ) ;
2018-03-09 22:59:12 +02:00
Setting . setValue ( 'locale' , closestSupportedLocale ( locale ) ) ;
2018-12-28 22:40:29 +02:00
Setting . setValue ( 'firstStart' , 0 ) ;
}
if ( Setting . value ( 'db.ftsEnabled' ) === - 1 ) {
const ftsEnabled = await db . ftsEnabled ( ) ;
Setting . setValue ( 'db.ftsEnabled' , ftsEnabled ? 1 : 0 ) ;
reg . logger ( ) . info ( 'db.ftsEnabled = ' , Setting . value ( 'db.ftsEnabled' ) ) ;
2017-07-25 20:57:06 +02:00
}
2019-05-22 16:56:07 +02:00
if ( Setting . value ( 'env' ) === 'dev' ) {
Setting . setValue ( 'welcome.enabled' , false ) ;
2019-07-30 09:35:42 +02:00
}
2019-05-22 16:56:07 +02:00
2019-12-29 19:58:40 +02:00
PluginAssetsLoader . instance ( ) . setLogger ( mainLogger ) ;
await PluginAssetsLoader . instance ( ) . importAssets ( ) ;
2019-07-30 09:35:42 +02:00
// eslint-disable-next-line require-atomic-updates
2019-05-06 22:35:29 +02:00
BaseItem . revisionService _ = RevisionService . instance ( ) ;
2019-07-30 09:35:42 +02:00
// Note: for now we hard-code the folder sort order as we need to
2019-03-10 22:30:31 +02:00
// create a UI to allow customisation (started in branch mobile_add_sidebar_buttons)
Setting . setValue ( 'folders.sortOrder.field' , 'title' ) ;
Setting . setValue ( 'folders.sortOrder.reverse' , false ) ;
2019-09-19 23:51:18 +02:00
reg . logger ( ) . info ( ` Sync target: ${ Setting . value ( 'sync.target' ) } ` ) ;
2017-11-24 21:47:24 +02:00
2018-03-09 22:59:12 +02:00
setLocale ( Setting . value ( 'locale' ) ) ;
2017-07-19 23:26:30 +02:00
2017-12-30 21:57:34 +02:00
// ----------------------------------------------------------------
// E2EE SETUP
// ----------------------------------------------------------------
EncryptionService . fsDriver _ = fsDriver ;
EncryptionService . instance ( ) . setLogger ( mainLogger ) ;
2019-07-30 09:35:42 +02:00
// eslint-disable-next-line require-atomic-updates
2017-12-30 21:57:34 +02:00
BaseItem . encryptionService _ = EncryptionService . instance ( ) ;
2018-01-02 21:17:14 +02:00
DecryptionWorker . instance ( ) . dispatch = dispatch ;
2017-12-30 21:57:34 +02:00
DecryptionWorker . instance ( ) . setLogger ( mainLogger ) ;
2019-06-08 00:11:08 +02:00
DecryptionWorker . instance ( ) . setKvStore ( KvStore . instance ( ) ) ;
2017-12-30 21:57:34 +02:00
DecryptionWorker . instance ( ) . setEncryptionService ( EncryptionService . instance ( ) ) ;
await EncryptionService . instance ( ) . loadMasterKeysFromSettings ( ) ;
2020-06-09 21:56:48 +02:00
DecryptionWorker . instance ( ) . on ( 'resourceMetadataButNotBlobDecrypted' , decryptionWorker _resourceMetadataButNotBlobDecrypted ) ;
2017-12-30 21:57:34 +02:00
// ----------------------------------------------------------------
// / E2EE SETUP
// ----------------------------------------------------------------
2018-03-09 22:59:12 +02:00
reg . logger ( ) . info ( 'Loading folders...' ) ;
2017-07-06 23:30:45 +02:00
2017-07-15 17:54:19 +02:00
await FoldersScreenUtils . refreshFolders ( ) ;
2017-07-09 00:57:09 +02:00
2019-02-24 20:02:50 +02:00
const tags = await Tag . allWithNotes ( ) ;
2017-07-25 20:36:52 +02:00
dispatch ( {
2018-03-09 22:59:12 +02:00
type : 'TAG_UPDATE_ALL' ,
2017-12-14 19:58:10 +02:00
items : tags ,
2017-07-25 20:36:52 +02:00
} ) ;
2017-12-30 21:57:34 +02:00
const masterKeys = await MasterKey . all ( ) ;
dispatch ( {
2018-03-09 22:59:12 +02:00
type : 'MASTERKEY_UPDATE_ALL' ,
2017-12-30 21:57:34 +02:00
items : masterKeys ,
} ) ;
2020-03-14 01:46:14 +02:00
const folderId = Setting . value ( 'activeFolderId' ) ;
2017-07-15 01:12:32 +02:00
let folder = await Folder . load ( folderId ) ;
2017-07-25 20:09:01 +02:00
if ( ! folder ) folder = await Folder . defaultFolder ( ) ;
2017-07-23 20:26:50 +02:00
2018-05-09 13:39:17 +02:00
dispatch ( {
type : 'FOLDER_SET_COLLAPSED_ALL' ,
ids : Setting . value ( 'collapsedFolderIds' ) ,
} ) ;
2017-07-25 20:09:01 +02:00
if ( ! folder ) {
2019-06-29 01:24:00 +02:00
dispatch ( DEFAULT _ROUTE ) ;
2017-07-24 23:52:30 +02:00
} else {
2017-07-25 20:09:01 +02:00
dispatch ( {
2018-03-09 22:59:12 +02:00
type : 'NAV_GO' ,
routeName : 'Notes' ,
2017-07-25 20:09:01 +02:00
folderId : folder . id ,
} ) ;
2017-07-24 23:52:30 +02:00
}
2020-02-19 01:52:36 +02:00
setUpQuickActions ( dispatch , folderId ) ;
2017-07-10 00:57:15 +02:00
} catch ( error ) {
2019-09-19 23:51:18 +02:00
alert ( ` Initialization error: ${ error . message } ` ) ;
2018-03-09 22:59:12 +02:00
reg . logger ( ) . error ( 'Initialization error:' , error ) ;
2017-07-10 00:57:15 +02:00
}
2017-07-26 19:49:01 +02:00
reg . setupRecurrentSync ( ) ;
2017-07-22 19:35:39 +02:00
2017-11-28 20:58:04 +02:00
PoorManIntervals . setTimeout ( ( ) => {
AlarmService . garbageCollect ( ) ;
} , 1000 * 60 * 60 ) ;
2017-11-24 20:06:45 +02:00
2018-03-16 16:32:47 +02:00
ResourceService . runInBackground ( ) ;
2018-03-15 20:08:46 +02:00
2019-07-30 09:35:42 +02:00
ResourceFetcher . instance ( ) . setFileApi ( ( ) => { return reg . syncTarget ( ) . fileApi ( ) ; } ) ;
2018-10-09 23:01:50 +02:00
ResourceFetcher . instance ( ) . setLogger ( reg . logger ( ) ) ;
2019-05-22 16:56:07 +02:00
ResourceFetcher . instance ( ) . dispatch = dispatch ;
ResourceFetcher . instance ( ) . on ( 'downloadComplete' , resourceFetcher _downloadComplete ) ;
2018-10-09 23:01:50 +02:00
ResourceFetcher . instance ( ) . start ( ) ;
2018-12-16 19:32:42 +02:00
SearchEngine . instance ( ) . setDb ( reg . db ( ) ) ;
SearchEngine . instance ( ) . setLogger ( reg . logger ( ) ) ;
2019-01-18 20:31:07 +02:00
SearchEngine . instance ( ) . scheduleSyncTables ( ) ;
2018-12-16 19:32:42 +02:00
2019-05-11 18:55:40 +02:00
await MigrationService . instance ( ) . run ( ) ;
2020-07-29 00:47:24 +02:00
// When the app starts we want the full sync to
// start almost immediately to get the latest data.
reg . scheduleSync ( 1000 ) . then ( ( ) => {
2017-11-28 20:58:04 +02:00
// Wait for the first sync before updating the notifications, since synchronisation
// might change the notifications.
AlarmService . updateAllNotifications ( ) ;
2017-12-30 21:57:34 +02:00
DecryptionWorker . instance ( ) . scheduleStart ( ) ;
2017-11-28 20:58:04 +02:00
} ) ;
2017-11-24 20:06:45 +02:00
2019-02-09 21:04:34 +02:00
await WelcomeUtils . install ( dispatch ) ;
2019-02-05 20:46:32 +02:00
2019-05-06 22:35:29 +02:00
// Collect revisions more frequently on mobile because it doesn't auto-save
// and it cannot collect anything when the app is not active.
RevisionService . instance ( ) . runInBackground ( 1000 * 30 ) ;
2018-03-09 22:59:12 +02:00
reg . logger ( ) . info ( 'Application initialized' ) ;
2017-07-10 00:57:15 +02:00
}
class AppComponent extends React . Component {
2018-03-09 22:59:12 +02:00
2017-07-18 00:43:29 +02:00
constructor ( ) {
super ( ) ;
2019-06-26 20:05:37 +02:00
this . state = {
sideMenuContentOpacity : new Animated . Value ( 0 ) ,
} ;
2017-07-18 00:43:29 +02:00
this . lastSyncStarted _ = defaultState . syncStarted ;
2017-12-01 21:44:34 +02:00
this . backButtonHandler _ = ( ) => {
return this . backButtonHandler ( ) ;
2019-07-30 09:35:42 +02:00
} ;
2018-02-07 21:51:58 +02:00
this . onAppStateChange _ = ( ) => {
PoorManIntervals . update ( ) ;
2019-07-30 09:35:42 +02:00
} ;
2017-07-18 00:43:29 +02:00
}
2020-10-08 12:49:39 +02:00
// 2020-10-08: It seems the initialisation code is quite fragile in general and should be kept simple.
// For example, adding a loading screen as was done in this commit: https://github.com/laurent22/joplin/commit/569355a3182bc12e50a54249882e3d68a72c2b28.
// had for effect that sharing with the app would create multiple instances of the app, thus breaking
// database access and so on. It's unclear why it happens and how to fix it but reverting that commit
// fixed the issue for now.
//
// Changing app launch mode doesn't help.
//
// It's possible that it's a bug in React Native, or perhaps the framework expects that the whole app can be
// mounted/unmounted or multiple ones can be running at the same time, but the app was not designed in this
// way.
//
// More reports and info about the multiple instance bug:
//
// https://github.com/laurent22/joplin/issues/3800
// https://github.com/laurent22/joplin/issues/3804
// https://github.com/laurent22/joplin/issues/3807
// https://discourse.joplinapp.org/t/webdav-config-encryption-config-randomly-lost-on-android/11364
// https://discourse.joplinapp.org/t/android-keeps-on-resetting-my-sync-and-theme/11443
2020-10-08 12:35:29 +02:00
async componentDidMount ( ) {
if ( this . props . appState == 'starting' ) {
2017-08-01 19:41:58 +02:00
this . props . dispatch ( {
2018-03-09 22:59:12 +02:00
type : 'APP_STATE_SET' ,
state : 'initializing' ,
2017-08-01 19:41:58 +02:00
} ) ;
2020-10-08 12:35:29 +02:00
await initialize ( this . props . dispatch ) ;
this . props . dispatch ( {
type : 'APP_STATE_SET' ,
state : 'ready' ,
2017-08-01 19:41:58 +02:00
} ) ;
2020-10-08 12:35:29 +02:00
}
2017-11-28 22:17:34 +02:00
2020-10-08 12:35:29 +02:00
BackButtonService . initialize ( this . backButtonHandler _ ) ;
2017-12-01 21:44:34 +02:00
2020-10-08 12:35:29 +02:00
AlarmService . setInAppNotificationHandler ( async ( alarmId ) => {
const alarm = await Alarm . load ( alarmId ) ;
const notification = await Alarm . makeNotification ( alarm ) ;
this . dropdownAlert _ . alertWithType ( 'info' , notification . title , notification . body ? notification . body : '' ) ;
} ) ;
2018-02-07 21:51:58 +02:00
2020-10-08 12:35:29 +02:00
AppState . addEventListener ( 'change' , this . onAppStateChange _ ) ;
2020-06-04 19:40:44 +02:00
2020-10-08 12:35:29 +02:00
const sharedData = await ShareExtension . data ( ) ;
if ( sharedData ) {
reg . logger ( ) . info ( 'Received shared data' ) ;
if ( this . props . selectedFolderId ) {
handleShared ( sharedData , this . props . selectedFolderId , this . props . dispatch ) ;
} else {
reg . logger . info ( 'Cannot handle share - default folder id is not set' ) ;
2020-06-04 19:40:44 +02:00
}
2020-10-08 12:35:29 +02:00
}
2018-02-07 21:51:58 +02:00
}
componentWillUnmount ( ) {
2018-03-09 22:59:12 +02:00
AppState . removeEventListener ( 'change' , this . onAppStateChange _ ) ;
2017-07-10 23:00:41 +02:00
}
2020-10-08 12:35:29 +02:00
componentDidUpdate ( prevProps ) {
2019-06-26 20:05:37 +02:00
if ( this . props . showSideMenu !== prevProps . showSideMenu ) {
Animated . timing ( this . state . sideMenuContentOpacity , {
toValue : this . props . showSideMenu ? 0.5 : 0 ,
duration : 600 ,
} ) . start ( ) ;
}
}
2017-08-24 20:10:03 +02:00
async backButtonHandler ( ) {
2017-11-23 20:47:51 +02:00
if ( this . props . noteSelectionEnabled ) {
2018-03-09 22:59:12 +02:00
this . props . dispatch ( { type : 'NOTE_SELECTION_END' } ) ;
2017-11-23 20:47:51 +02:00
return true ;
}
2017-07-10 23:00:41 +02:00
if ( this . props . showSideMenu ) {
2018-03-09 22:59:12 +02:00
this . props . dispatch ( { type : 'SIDE_MENU_CLOSE' } ) ;
2018-12-07 01:45:18 +02:00
return true ;
2017-07-10 23:00:41 +02:00
}
if ( this . props . historyCanGoBack ) {
2018-03-09 22:59:12 +02:00
this . props . dispatch ( { type : 'NAV_BACK' } ) ;
2017-07-10 23:00:41 +02:00
return true ;
}
2017-12-01 21:44:34 +02:00
BackHandler . exitApp ( ) ;
2017-07-10 23:00:41 +02:00
return false ;
2017-05-11 22:14:01 +02:00
}
2018-04-30 18:38:19 +02:00
UNSAFE _componentWillReceiveProps ( newProps ) {
2017-07-18 00:43:29 +02:00
if ( newProps . syncStarted != this . lastSyncStarted _ ) {
if ( ! newProps . syncStarted ) FoldersScreenUtils . refreshFolders ( ) ;
this . lastSyncStarted _ = newProps . syncStarted ;
}
}
2017-06-06 22:58:19 +02:00
sideMenu _change ( isOpen ) {
2017-05-24 21:27:13 +02:00
// Make sure showSideMenu property of state is updated
// when the menu is open/closed.
this . props . dispatch ( {
2018-03-09 22:59:12 +02:00
type : isOpen ? 'SIDE_MENU_OPEN' : 'SIDE_MENU_CLOSE' ,
2017-05-24 21:27:13 +02:00
} ) ;
}
2017-05-11 22:14:01 +02:00
render ( ) {
2020-10-08 12:35:29 +02:00
if ( this . props . appState != 'ready' ) return null ;
2020-09-15 15:01:07 +02:00
const theme = themeStyle ( this . props . themeId ) ;
2017-08-01 19:41:58 +02:00
2019-07-11 18:43:55 +02:00
let sideMenuContent = null ;
let menuPosition = 'left' ;
if ( this . props . routeName === 'Note' ) {
2020-02-05 00:09:34 +02:00
sideMenuContent = < SafeAreaView style = { { flex : 1 , backgroundColor : theme . backgroundColor } } > < SideMenuContentNote options = { this . props . noteSideMenuOptions } / > < / S a f e A r e a V i e w > ;
2019-07-11 18:43:55 +02:00
menuPosition = 'right' ;
} else {
2020-02-05 00:09:34 +02:00
sideMenuContent = < SafeAreaView style = { { flex : 1 , backgroundColor : theme . backgroundColor } } > < SideMenuContent / > < / S a f e A r e a V i e w > ;
2019-07-11 18:43:55 +02:00
}
2017-05-24 21:09:46 +02:00
2017-07-14 20:49:14 +02:00
const appNavInit = {
Notes : { screen : NotesScreen } ,
Note : { screen : NoteScreen } ,
2019-03-14 00:42:16 +02:00
Tags : { screen : TagsScreen } ,
2017-07-14 20:49:14 +02:00
Folder : { screen : FolderScreen } ,
OneDriveLogin : { screen : OneDriveLoginScreen } ,
2018-03-27 01:55:44 +02:00
DropboxLogin : { screen : DropboxLoginScreen } ,
2017-12-30 21:57:34 +02:00
EncryptionConfig : { screen : EncryptionConfigScreen } ,
2020-08-02 13:28:50 +02:00
UpgradeSyncTarget : { screen : UpgradeSyncTargetScreen } ,
2017-07-14 20:49:14 +02:00
Log : { screen : LogScreen } ,
Status : { screen : StatusScreen } ,
2017-07-23 00:52:24 +02:00
Search : { screen : SearchScreen } ,
2017-07-23 20:26:50 +02:00
Config : { screen : ConfigScreen } ,
2017-07-14 20:49:14 +02:00
} ;
2020-06-10 23:08:59 +02:00
const statusBarStyle = theme . appearance === 'light' ? 'dark-content' : 'light-content' ;
2017-05-11 22:14:01 +02:00
return (
2020-06-10 23:08:59 +02:00
< View style = { { flex : 1 , backgroundColor : theme . backgroundColor } } >
< SideMenu
menu = { sideMenuContent }
edgeHitWidth = { 5 }
menuPosition = { menuPosition }
onChange = { ( isOpen ) => this . sideMenu _change ( isOpen ) }
onSliding = { ( percent ) => {
this . props . dispatch ( {
type : 'SIDE_MENU_OPEN_PERCENT' ,
value : percent ,
} ) ;
} }
>
< StatusBar barStyle = { statusBarStyle } / >
< MenuContext style = { { flex : 1 , backgroundColor : theme . backgroundColor } } >
< SafeAreaView style = { { flex : 1 } } >
< View style = { { flex : 1 , backgroundColor : theme . backgroundColor } } >
< AppNav screens = { appNavInit } / >
< / V i e w >
< DropdownAlert ref = { ref => this . dropdownAlert _ = ref } tapToCloseEnabled = { true } / >
< Animated . View pointerEvents = 'none' style = { { position : 'absolute' , backgroundColor : 'black' , opacity : this . state . sideMenuContentOpacity , width : '100%' , height : '120%' } } / >
< / S a f e A r e a V i e w >
< / M e n u C o n t e x t >
< / S i d e M e n u >
< / V i e w >
2017-05-11 22:14:01 +02:00
) ;
}
2017-05-10 20:58:02 +02:00
}
2018-03-09 22:59:12 +02:00
const mapStateToProps = ( state ) => {
2017-05-10 20:58:02 +02:00
return {
2017-11-04 18:40:34 +02:00
historyCanGoBack : state . historyCanGoBack ,
showSideMenu : state . showSideMenu ,
syncStarted : state . syncStarted ,
appState : state . appState ,
2017-11-23 20:47:51 +02:00
noteSelectionEnabled : state . noteSelectionEnabled ,
2018-09-04 12:07:33 +02:00
selectedFolderId : state . selectedFolderId ,
2019-07-11 18:43:55 +02:00
routeName : state . route . routeName ,
2020-09-15 15:01:07 +02:00
themeId : state . settings . theme ,
2019-07-11 18:43:55 +02:00
noteSideMenuOptions : state . noteSideMenuOptions ,
2017-11-04 18:40:34 +02:00
} ;
2017-05-10 20:58:02 +02:00
} ;
const App = connect ( mapStateToProps ) ( AppComponent ) ;
2017-05-09 22:46:54 +02:00
class Root extends React . Component {
render ( ) {
return (
< Provider store = { store } >
2018-03-09 22:59:12 +02:00
< App / >
2017-05-09 22:46:54 +02:00
< / P r o v i d e r >
) ;
}
2017-05-09 21:59:14 +02:00
}
2018-03-09 19:49:35 +02:00
module . exports = { Root } ;