2020-12-30 13:51:43 +02:00
const React = require ( 'react' ) ;
import shim from '@joplin/lib/shim' ;
shim . setReact ( React ) ;
2021-05-19 23:26:42 +02:00
import setupQuickActions from './setupQuickActions' ;
2020-06-02 22:13:15 +02:00
import PluginAssetsLoader from './PluginAssetsLoader' ;
2020-12-30 12:54:00 +02:00
import AlarmService from '@joplin/lib/services/AlarmService' ;
import Alarm from '@joplin/lib/models/Alarm' ;
import time from '@joplin/lib/time' ;
import Logger , { TargetType } from '@joplin/lib/Logger' ;
import BaseModel from '@joplin/lib/BaseModel' ;
import BaseService from '@joplin/lib/services/BaseService' ;
import ResourceService from '@joplin/lib/services/ResourceService' ;
import KvStore from '@joplin/lib/services/KvStore' ;
import NoteScreen from './components/screens/Note' ;
import UpgradeSyncTargetScreen from './components/screens/UpgradeSyncTargetScreen' ;
2021-08-30 15:15:35 +02:00
import Setting , { Env } from '@joplin/lib/models/Setting' ;
2020-12-30 12:54:00 +02:00
import RNFetchBlob from 'rn-fetch-blob' ;
import PoorManIntervals from '@joplin/lib/PoorManIntervals' ;
import reducer from '@joplin/lib/reducer' ;
import ShareExtension from './utils/ShareExtension' ;
import handleShared from './utils/shareHandler' ;
import uuid from '@joplin/lib/uuid' ;
import { loadKeychainServiceAndSettings } from '@joplin/lib/services/SettingUtils' ;
import KeychainServiceDriverMobile from '@joplin/lib/services/keychain/KeychainServiceDriver.mobile' ;
import { setLocale , closestSupportedLocale , defaultLocale } from '@joplin/lib/locale' ;
import SyncTargetJoplinServer from '@joplin/lib/SyncTargetJoplinServer' ;
2021-06-03 17:12:07 +02:00
import SyncTargetJoplinCloud from '@joplin/lib/SyncTargetJoplinCloud' ;
2021-01-20 17:49:02 +02:00
import SyncTargetOneDrive from '@joplin/lib/SyncTargetOneDrive' ;
2021-06-17 19:19:32 +02:00
const VersionInfo = require ( 'react-native-version-info' ) . default ;
2021-04-25 10:50:52 +02:00
const { AppState , Keyboard , NativeModules , BackHandler , Animated , View , StatusBar , Linking , Platform } = require ( 'react-native' ) ;
2021-03-29 10:35:39 +02:00
import NetInfo from '@react-native-community/netinfo' ;
2020-12-30 12:54:00 +02:00
const DropdownAlert = require ( 'react-native-dropdownalert' ) . default ;
2021-01-27 19:42:58 +02:00
const AlarmServiceDriver = require ( './services/AlarmServiceDriver' ) . default ;
2020-11-05 18:58:23 +02:00
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' ) ;
2021-01-23 17:51:19 +02:00
import NavService from '@joplin/lib/services/NavService' ;
2021-05-13 18:57:37 +02:00
import { createStore , applyMiddleware } from 'redux' ;
2020-11-07 17:59:37 +02:00
const reduxSharedMiddleware = require ( '@joplin/lib/components/shared/reduxSharedMiddleware' ) ;
2020-11-05 18:58:23 +02:00
const { shimInit } = require ( './utils/shim-init-react.js' ) ;
const { AppNav } = require ( './components/app-nav.js' ) ;
2021-01-22 19:41:11 +02:00
import Note from '@joplin/lib/models/Note' ;
import Folder from '@joplin/lib/models/Folder' ;
2021-03-30 23:58:47 +02:00
import BaseSyncTarget from '@joplin/lib/BaseSyncTarget' ;
2020-11-07 17:59:37 +02:00
const { FoldersScreenUtils } = require ( '@joplin/lib/folders-screen-utils.js' ) ;
2021-01-22 19:41:11 +02:00
import Resource from '@joplin/lib/models/Resource' ;
import Tag from '@joplin/lib/models/Tag' ;
import NoteTag from '@joplin/lib/models/NoteTag' ;
import BaseItem from '@joplin/lib/models/BaseItem' ;
import MasterKey from '@joplin/lib/models/MasterKey' ;
import Revision from '@joplin/lib/models/Revision' ;
2021-01-23 17:51:19 +02:00
import RevisionService from '@joplin/lib/services/RevisionService' ;
2021-01-29 20:45:11 +02:00
import JoplinDatabase from '@joplin/lib/JoplinDatabase' ;
import Database from '@joplin/lib/database' ;
2020-11-05 18:58:23 +02:00
const { NotesScreen } = require ( './components/screens/notes.js' ) ;
const { TagsScreen } = require ( './components/screens/tags.js' ) ;
2021-06-19 18:32:36 +02:00
import ConfigScreen from './components/screens/ConfigScreen' ;
2020-11-05 18:58:23 +02:00
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' ) ;
2021-08-07 13:22:37 +02:00
import EncryptionConfigScreen from './components/screens/encryption-config' ;
2020-11-05 18:58:23 +02:00
const { DropboxLoginScreen } = require ( './components/screens/dropbox-login.js' ) ;
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' ) ;
2021-01-29 20:45:11 +02:00
import { reg } from '@joplin/lib/registry' ;
2020-11-07 17:59:37 +02:00
const { defaultState } = require ( '@joplin/lib/reducer' ) ;
const { FileApiDriverLocal } = require ( '@joplin/lib/file-api-driver-local.js' ) ;
2021-01-23 17:51:19 +02:00
import ResourceFetcher from '@joplin/lib/services/ResourceFetcher' ;
2021-01-22 19:41:11 +02:00
import SearchEngine from '@joplin/lib/services/searchengine/SearchEngine' ;
2020-11-07 17:59:37 +02:00
const WelcomeUtils = require ( '@joplin/lib/WelcomeUtils' ) ;
2020-11-05 18:58:23 +02:00
const { themeStyle } = require ( './components/global-style.js' ) ;
2021-08-16 16:20:14 +02:00
import SyncTargetRegistry from '@joplin/lib/SyncTargetRegistry' ;
2020-11-07 17:59:37 +02:00
const SyncTargetFilesystem = require ( '@joplin/lib/SyncTargetFilesystem.js' ) ;
const SyncTargetNextcloud = require ( '@joplin/lib/SyncTargetNextcloud.js' ) ;
const SyncTargetWebDAV = require ( '@joplin/lib/SyncTargetWebDAV.js' ) ;
const SyncTargetDropbox = require ( '@joplin/lib/SyncTargetDropbox.js' ) ;
const SyncTargetAmazonS3 = require ( '@joplin/lib/SyncTargetAmazonS3.js' ) ;
2020-02-08 13:59:19 +02:00
2021-08-16 19:05:22 +02:00
SyncTargetRegistry . addClass ( SyncTargetNone ) ;
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 ) ;
2020-12-30 12:54:00 +02:00
SyncTargetRegistry . addClass ( SyncTargetJoplinServer ) ;
2021-06-03 17:12:07 +02:00
SyncTargetRegistry . addClass ( SyncTargetJoplinCloud ) ;
2018-01-17 23:01:41 +02:00
2021-01-27 19:42:58 +02:00
import FsDriverRN from './utils/fs-driver-rn' ;
2021-01-23 17:51:19 +02:00
import DecryptionWorker from '@joplin/lib/services/DecryptionWorker' ;
2021-08-23 19:47:07 +02:00
import EncryptionService from '@joplin/lib/services/e2ee/EncryptionService' ;
2021-01-23 17:51:19 +02:00
import MigrationService from '@joplin/lib/services/MigrationService' ;
2021-04-07 19:41:54 +02:00
import { clearSharedFilesCache } from './utils/ShareUtils' ;
2021-04-25 10:50:52 +02:00
import setIgnoreTlsErrors from './utils/TlsUtils' ;
2021-05-13 18:57:37 +02:00
import ShareService from '@joplin/lib/services/share/ShareService' ;
2021-05-19 23:26:42 +02:00
import setupNotifications from './utils/setupNotifications' ;
2021-08-30 15:15:35 +02:00
import { loadMasterKeysFromSettings , migrateMasterPassword } from '@joplin/lib/services/e2ee/utils' ;
2021-10-03 18:38:13 +02:00
import SyncTargetNone from '@joplin/lib/SyncTargetNone' ;
2021-10-03 17:00:49 +02:00
import { setRSA } from '@joplin/lib/services/e2ee/ppk' ;
import RSA from './services/e2ee/RSA.react-native' ;
import { runIntegrationTests } from '@joplin/lib/services/e2ee/ppkTestUtils' ;
2017-12-30 21:57:34 +02:00
2020-12-30 12:54:00 +02:00
let storeDispatch = function ( _action : any ) { } ;
2018-01-02 21:17:14 +02:00
2020-12-30 12:54:00 +02:00
const logReducerAction = function ( action : any ) {
2018-04-16 15:15:29 +02:00
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-12-30 12:54:00 +02:00
const generalMiddleware = ( store : any ) = > ( next : any ) = > async ( action : any ) = > {
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 ) {
2021-03-29 10:35:39 +02:00
if ( ! await reg . syncTarget ( ) . syncStarted ( ) ) void reg . scheduleSync ( 5 * 1000 , { syncSteps : [ 'update_remote' , 'delete_remote' ] } , true ) ;
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' ) ) {
2021-08-12 17:54:10 +02:00
await loadMasterKeysFromSettings ( EncryptionService . instance ( ) ) ;
2021-01-22 19:41:11 +02:00
void DecryptionWorker . instance ( ) . scheduleStart ( ) ;
2018-01-02 21:17:14 +02:00
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.
2021-03-29 10:35:39 +02:00
void reg . scheduleSync ( null , null , true ) ;
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' ) {
2021-01-22 19:41:11 +02:00
void DecryptionWorker . instance ( ) . scheduleStart ( ) ;
2017-12-30 21:57:34 +02:00
}
2020-05-31 01:31:29 +02:00
if ( action . type === 'SYNC_CREATED_OR_UPDATED_RESOURCE' ) {
2021-01-22 19:41:11 +02:00
void 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-12-30 12:54:00 +02:00
const navHistory : any [ ] = [ ] ;
2017-11-06 20:05:12 +02:00
2020-12-30 12:54:00 +02:00
function historyCanGoBackTo ( route : any ) {
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 ,
2021-03-29 10:35:39 +02:00
isOnMobileData : false ,
2017-11-06 20:35:04 +02:00
} ) ;
2020-12-30 12:54:00 +02:00
const appReducer = ( state = appDefaultState , action : any ) = > {
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
2020-12-30 12:54:00 +02:00
// @ts-ignore
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 ;
2020-12-30 12:54:00 +02:00
if ( ! historyGoingBack && historyCanGoBackTo ( currentRoute ) ) {
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
2021-03-29 10:35:39 +02:00
case 'MOBILE_DATA_WARNING_UPDATE' :
newState = Object . assign ( { } , state ) ;
newState . isOnMobileData = action . isOnMobileData ;
break ;
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
2020-12-30 12:54:00 +02:00
function resourceFetcher_downloadComplete ( event : any ) {
2019-05-22 16:56:07 +02:00
if ( event . encrypted ) {
2021-01-22 19:41:11 +02:00
void DecryptionWorker . instance ( ) . scheduleStart ( ) ;
2019-05-22 16:56:07 +02:00
}
}
2020-06-09 21:56:48 +02:00
function decryptionWorker_resourceMetadataButNotBlobDecrypted() {
ResourceFetcher . instance ( ) . scheduleAutoAddResources ( ) ;
}
2020-12-30 12:54:00 +02:00
async function initialize ( dispatch : Function ) {
2017-07-10 20:09:58 +02:00
shimInit ( ) ;
2017-07-07 19:19:24 +02:00
2020-12-30 12:54:00 +02:00
// @ts-ignore
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 ( ) ;
2020-12-30 12:54:00 +02:00
mainLogger . addTarget ( TargetType . 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' ) {
2020-12-30 12:54:00 +02:00
mainLogger . addTarget ( TargetType . Console ) ;
2017-12-31 15:58:50 +02:00
mainLogger . setLevel ( Logger . LEVEL_DEBUG ) ;
}
2017-07-15 01:12:32 +02:00
2020-11-19 17:25:02 +02:00
Logger . initializeGlobalLogger ( mainLogger ) ;
2017-07-15 01:12:32 +02:00
reg . setLogger ( mainLogger ) ;
2020-12-30 12:54:00 +02:00
reg . setShowErrorMessageBoxHandler ( ( message : string ) = > { alert ( message ) ; } ) ;
2017-07-10 00:57:15 +02:00
2018-03-15 20:08:46 +02:00
BaseService . logger_ = mainLogger ;
2020-11-07 17:59:37 +02:00
// require('@joplin/lib/ntpDate').setLogger(reg.logger());
2018-03-15 20:08:46 +02:00
2018-03-09 22:59:12 +02:00
reg . logger ( ) . info ( '====================================' ) ;
2021-06-17 19:19:32 +02:00
reg . logger ( ) . info ( ` Starting application ${ Setting . value ( 'appId' ) } v ${ VersionInfo . appVersion } ( ${ Setting . value ( 'env' ) } ) ` ) ;
2017-07-10 00:57:15 +02:00
2017-07-15 01:12:32 +02:00
const dbLogger = new Logger ( ) ;
2020-12-30 12:54:00 +02:00
dbLogger . addTarget ( TargetType . Database , { database : logDatabase , source : 'm' } ) ;
2018-03-09 22:59:12 +02:00
if ( Setting . value ( 'env' ) == 'dev' ) {
2020-12-30 12:54:00 +02:00
dbLogger . addTarget ( TargetType . 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 ) ;
2021-01-29 20:45:11 +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 ) ;
2021-10-03 17:00:49 +02:00
setRSA ( RSA ) ;
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 {
2021-10-17 18:20:59 +02:00
await db . open ( { name : 'joplin-107.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 ) ;
2021-08-30 15:15:35 +02:00
await migrateMasterPassword ( ) ;
2021-10-18 13:37:25 +02:00
if ( ! Setting . value ( 'clientId' ) ) Setting . setValue ( 'clientId' , uuid . create ( ) ) ;
if ( Setting . value ( 'firstStart' ) ) {
let locale = NativeModules . I18nManager . localeIdentifier ;
if ( ! locale ) locale = defaultLocale ( ) ;
Setting . setValue ( 'locale' , closestSupportedLocale ( locale ) ) ;
Setting . skipDefaultMigrations ( ) ;
Setting . setValue ( 'firstStart' , 0 ) ;
} else {
Setting . applyDefaultMigrations ( ) ;
}
2021-08-30 15:15:35 +02:00
if ( Setting . value ( 'env' ) === Env . Dev ) {
// Setting.setValue('sync.10.path', 'https://api.joplincloud.com');
// Setting.setValue('sync.10.userContentPath', 'https://joplinusercontent.com');
Setting . setValue ( 'sync.10.path' , 'http://api.joplincloud.local:22300' ) ;
Setting . setValue ( 'sync.10.userContentPath' , 'http://joplinusercontent.local:22300' ) ;
2021-10-17 18:20:59 +02:00
2021-11-02 18:33:53 +02:00
// Setting.setValue('sync.target', 10);
// Setting.setValue('sync.10.username', 'user1@example.com');
// Setting.setValue('sync.10.password', 'hunter1hunter2hunter3');
2021-08-30 15:15:35 +02:00
}
2017-07-25 20:57:06 +02:00
2018-12-28 22:40:29 +02:00
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
2021-04-25 10:50:52 +02:00
if ( Platform . OS === 'android' ) {
const ignoreTlsErrors = Setting . value ( 'net.ignoreTlsErrors' ) ;
if ( ignoreTlsErrors ) {
await setIgnoreTlsErrors ( ignoreTlsErrors ) ;
}
}
2017-12-30 21:57:34 +02:00
// ----------------------------------------------------------------
// E2EE SETUP
// ----------------------------------------------------------------
EncryptionService . fsDriver_ = fsDriver ;
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 ( ) ;
2021-05-13 18:57:37 +02:00
BaseItem . shareService_ = ShareService . 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 ( ) ) ;
2021-08-12 17:54:10 +02:00
await loadMasterKeysFromSettings ( EncryptionService . instance ( ) ) ;
2020-06-09 21:56:48 +02:00
DecryptionWorker . instance ( ) . on ( 'resourceMetadataButNotBlobDecrypted' , decryptionWorker_resourceMetadataButNotBlobDecrypted ) ;
2017-12-30 21:57:34 +02:00
// ----------------------------------------------------------------
// / E2EE SETUP
// ----------------------------------------------------------------
2021-11-03 18:24:40 +02:00
await ShareService . instance ( ) . initialize ( store , EncryptionService . instance ( ) ) ;
2021-05-13 18:57:37 +02:00
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
} ) ;
2021-08-12 17:54:10 +02:00
// const masterKeys = await MasterKey.all();
2017-12-30 21:57:34 +02:00
2021-08-12 17:54:10 +02:00
// dispatch({
// type: 'MASTERKEY_UPDATE_ALL',
// items: masterKeys,
// });
2017-12-30 21:57:34 +02:00
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
}
2021-04-07 19:41:54 +02:00
await clearSharedFilesCache ( ) ;
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 ( ( ) = > {
2020-12-30 12:54:00 +02:00
void AlarmService . garbageCollect ( ) ;
2017-11-28 20:58:04 +02:00
} , 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 ) ;
2021-01-22 19:41:11 +02:00
void ResourceFetcher . instance ( ) . start ( ) ;
2018-10-09 23:01:50 +02:00
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.
2021-03-29 10:35:39 +02:00
// doWifiConnectionCheck set to true so initial sync
// doesn't happen on mobile data
void reg . scheduleSync ( 1000 , null , true ) . then ( ( ) = > {
2017-11-28 20:58:04 +02:00
// Wait for the first sync before updating the notifications, since synchronisation
// might change the notifications.
2020-12-30 12:54:00 +02:00
void AlarmService . updateAllNotifications ( ) ;
2017-12-30 21:57:34 +02:00
2021-01-22 19:41:11 +02:00
void 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 ) ;
2021-10-03 17:00:49 +02:00
// ----------------------------------------------------------------------------
// Keep this below to test react-native-rsa-native
// ----------------------------------------------------------------------------
// const testData = await createTestData();
// await checkTestData(testData);
// const testData = {
// "publicKey": "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAoMx9NBioka8DUjO3bKrWMn8uJ23LH1xySogQFR6yh6qbl6i5LKTw\nPgqvv55FUuQtYTMtUTVLggYQhdCBvwbBrD1OqO4xU6Ew7x5/TQKPV3MSgYaps3FF\nOdipC4FyA00jBe6Z1CIpL+ZaSnvjDbMUf5lW8bmfRuXfdBGAcdSBjqm9ttajOws+\n7BBSQ9nI5dnBnWRIVEUb7e9bulgANzM1LMUOE+gaef7T3uKzc+Cx3BhHgw1JdFbL\nZAndYtP52KI5N3oiFM4II26DxmDrO1tQokNM88l5xT0BXPhYiEl1CeBpo5VHZBY2\nRHr4MM/OyAXSUdulsDzbntpE+Y85zv7gpQIDAQAB\n-----END RSA PUBLIC KEY-----",
// "privateKey": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAoMx9NBioka8DUjO3bKrWMn8uJ23LH1xySogQFR6yh6qbl6i5\nLKTwPgqvv55FUuQtYTMtUTVLggYQhdCBvwbBrD1OqO4xU6Ew7x5/TQKPV3MSgYap\ns3FFOdipC4FyA00jBe6Z1CIpL+ZaSnvjDbMUf5lW8bmfRuXfdBGAcdSBjqm9ttaj\nOws+7BBSQ9nI5dnBnWRIVEUb7e9bulgANzM1LMUOE+gaef7T3uKzc+Cx3BhHgw1J\ndFbLZAndYtP52KI5N3oiFM4II26DxmDrO1tQokNM88l5xT0BXPhYiEl1CeBpo5VH\nZBY2RHr4MM/OyAXSUdulsDzbntpE+Y85zv7gpQIDAQABAoIBAEA0Zmm+ztAcyX6x\nF7RUImLXVV55AHntN9V6rrFAKJjzDl1oCUhCM4sSSUqBr7yBT31YKegbF6M7OK21\nq5jS4dIcSKQ7N4bk/dz8mGfvdby9Pc5qLqhvuex3DkiBzzxyOGHN+64wVbHCkJrd\nDLQTpUOtvoGWVHrCno6Bzn+lEnYbvdr0hqI5H4D0ubk6TYed1/4ZlJf0R/o/4jnl\nou0UG2hpJN4ur506cttkZJSTxLjzdO38JuQIAkCEglrMYVY61lBNPxC11Kr3ZN7o\ncm7gWZVyP26KoU27t/g+2FoiBGsWLqGYiuTaqT6dKZ2vHyJGjJIZZStv5ye2Ez8V\nKQwpjQECgYEA3xtwYu4n/G5UjEMumkXHNd/bDamelo1aQvvjkVvxKeASNBqV8cM0\n6Jb2FCuT9Y3mWbFTM0jpqXehpHUOCCnrPKGKnJ0ZS4/SRIrtw0iM6q17fTAqmuOt\nhX0pJ77Il8lVCtx4ItsW+LUGbm6CwotlYLVUuyluhKe0pGw2yafi2N0CgYEAuIFk\ng4p7x0i1LFAlIP0YQ07bJQ0E6FEWbCfMgrV3VjtbnT99EaqPOHhMasITCuoEFlh8\ncgyZ6oH7GEy4IRWrM+Mlm47S+NTrr6KgnTGf570ZAFuqnJac97oFB7BvlQsQot6F\n0L2JKM7dQKIMlvwA9DoXZdKX/9ykiqqIpawNxmkCgYEAuyJOwAw2ads4+3UWT7wb\nfarIF8ugA3OItAqHNFNEEvWpDx8FigVMCZMl0IFE14AwKCc+PBP6OXTolgLAxEQ0\n1WRB2V9D6kc1/Nvy1guydt0QaU7PTZ+O2hrDPF0f74Cl3jhSZBoUSIO+Yz46W2eE\nnvs5mMsFsirgr9E8myRAd9kCgYAGMCDE4KIiHugkolN8dcCYkU58QaGGgSG1YuhT\nAe8Mr1T1QynYq9W92RsHAZdN6GdWsIUL9iw7VzyqpfgO9AEX7mhWfUXKHqoA6/1j\nCEUKqqbqAikIs2x0SoLcrSgw4XwfWkM2qwSsn7N/9W9iqPUHO+OJALUkWawTEoEe\nvVSA8QKBgQCEYCPnxgeQSZkrv7x5soXzgF1YN5EZRa1mTUqPBubs564ZjIIY66mI\nCTaHl7U1cPAhx7mHkSzP/i5NjjLqPZZNOyawWDEEmOzxX69OIzKImb6mEQNyS3do\nI8jnpN5q9pw5TvuEIYSrGqQVnHeaEjSvcT48W9GuzjNVscGfw76fPg==\n-----END RSA PRIVATE KEY-----",
// "plaintext": "just testing",
// "ciphertext": "BfkKLdrmd2UX4sPf0bzhfqrg3rKwH5DS7dPAqdmoQuHlrvEBrYKqheekwpnWQgGggGcm/orlrsQRwlexLv7jfRbb0bMnElkySMu4w6wTxILB66RX9H3vXCz02SwHKFRcuGJxlzTPUC23ki6f/McYJ2n/2L8qYxBO8fncTKutIWV54jY19RS1wQ4IdVDBqzji8D0QsRxUhVlpRk4qxsVnyuoyg9AyDe91LOYKfRc6NdapFij996nKzjxFcKOdBqpis34fN3Cg7avcs2Dm5vi7zlRhyGqJJhORXTU3x6hVwOBkVAisgaB7xS3lHiYp6Fs5tP3hBd0kFwVVx8gALbHsgg=="
// };
// await checkTestData(testData);
// await printTestData();
// ----------------------------------------------------------------------------
// On desktop and CLI we run various tests to check that node-rsa is working
// as expected. On mobile however we cannot run test units directly on
// device, and that's what would be needed to automatically verify
// react-native-rsa-native. So instead we run the tests every time the
// mobile app is started in dev mode. If there's any regression the below
// call will throw an error, alerting us of the issue. Otherwise it will
// just print some messages in the console.
// ----------------------------------------------------------------------------
if ( Setting . value ( 'env' ) == 'dev' ) await runIntegrationTests ( ) ;
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
2021-09-06 17:57:07 +02:00
public constructor ( ) {
2017-07-18 00:43:29 +02:00
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
} ;
2021-04-24 10:22:11 +02:00
this . handleOpenURL_ = ( event : any ) = > {
if ( event . url == ShareExtension . shareURL ) {
void this . handleShareData ( ) ;
}
} ;
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
2021-09-06 17:57:07 +02:00
public async componentDidMount() {
2020-10-08 12:35:29 +02:00
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
} ) ;
2021-03-29 10:35:39 +02:00
try {
// This will be called right after adding the event listener
// so there's no need to check netinfo on startup
this . unsubscribeNetInfoHandler_ = NetInfo . addEventListener ( ( { type , details } ) = > {
const isMobile = details . isConnectionExpensive || type === 'cellular' ;
reg . setIsOnMobileData ( isMobile ) ;
this . props . dispatch ( {
type : 'MOBILE_DATA_WARNING_UPDATE' ,
isOnMobileData : isMobile ,
} ) ;
} ) ;
} catch ( error ) {
reg . logger ( ) . warn ( 'Something went wrong while checking network info' ) ;
reg . logger ( ) . info ( error ) ;
}
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
2021-04-24 10:22:11 +02:00
Linking . addEventListener ( 'url' , this . handleOpenURL_ ) ;
2020-10-08 12:35:29 +02:00
BackButtonService . initialize ( this . backButtonHandler_ ) ;
2017-12-01 21:44:34 +02:00
2020-12-30 12:54:00 +02:00
AlarmService . setInAppNotificationHandler ( async ( alarmId : string ) = > {
2020-10-08 12:35:29 +02:00
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
2021-04-24 10:22:11 +02:00
await this . handleShareData ( ) ;
2021-03-10 21:20:40 +02:00
2021-05-19 23:26:42 +02:00
setupQuickActions ( this . props . dispatch , this . props . selectedFolderId ) ;
await setupNotifications ( this . props . dispatch ) ;
2021-08-30 15:15:35 +02:00
// Setting.setValue('encryption.masterPassword', 'WRONG');
// setTimeout(() => NavService.go('EncryptionConfig'), 2000);
2018-02-07 21:51:58 +02:00
}
2021-09-06 17:57:07 +02:00
public componentWillUnmount() {
2018-03-09 22:59:12 +02:00
AppState . removeEventListener ( 'change' , this . onAppStateChange_ ) ;
2021-04-24 10:22:11 +02:00
Linking . removeEventListener ( 'url' , this . handleOpenURL_ ) ;
2021-03-29 10:35:39 +02:00
if ( this . unsubscribeNetInfoHandler_ ) this . unsubscribeNetInfoHandler_ ( ) ;
2017-07-10 23:00:41 +02:00
}
2021-09-06 17:57:07 +02:00
public componentDidUpdate ( prevProps : any ) {
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 ( ) ;
}
}
2021-09-06 17:57:07 +02:00
private 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
}
2021-09-06 17:57:07 +02:00
private async handleShareData() {
2021-04-24 10:22:11 +02:00
const sharedData = await ShareExtension . data ( ) ;
if ( sharedData ) {
reg . logger ( ) . info ( 'Received shared data' ) ;
if ( this . props . selectedFolderId ) {
await handleShared ( sharedData , this . props . selectedFolderId , this . props . dispatch ) ;
} else {
reg . logger ( ) . info ( 'Cannot handle share - default folder id is not set' ) ;
}
}
}
2021-09-06 17:57:07 +02:00
public UNSAFE_componentWillReceiveProps ( newProps : any ) {
2017-07-18 00:43:29 +02:00
if ( newProps . syncStarted != this . lastSyncStarted_ ) {
if ( ! newProps . syncStarted ) FoldersScreenUtils . refreshFolders ( ) ;
this . lastSyncStarted_ = newProps . syncStarted ;
}
}
2021-09-06 17:57:07 +02:00
private sideMenu_change ( isOpen : boolean ) {
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
} ) ;
}
2021-09-06 17:57:07 +02:00
public 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 } / > < / SafeAreaView > ;
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 / > < / SafeAreaView > ;
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 }
2020-12-30 12:54:00 +02:00
onChange = { ( isOpen : boolean ) = > this . sideMenu_change ( isOpen ) }
onSliding = { ( percent : number ) = > {
2020-06-10 23:08:59 +02:00
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 } / >
< / View >
2020-12-30 12:54:00 +02:00
< DropdownAlert ref = { ( ref : any ) = > this . dropdownAlert_ = ref } tapToCloseEnabled = { true } / >
2020-06-10 23:08:59 +02:00
< Animated.View pointerEvents = 'none' style = { { position : 'absolute' , backgroundColor : 'black' , opacity : this.state.sideMenuContentOpacity , width : '100%' , height : '120%' } } / >
< / SafeAreaView >
< / MenuContext >
< / SideMenu >
< / View >
2017-05-11 22:14:01 +02:00
) ;
}
2017-05-10 20:58:02 +02:00
}
2020-12-30 12:54:00 +02:00
const mapStateToProps = ( state : any ) = > {
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 ) ;
2020-12-30 12:54:00 +02:00
export default class Root extends React . Component {
2021-10-03 17:00:49 +02:00
public render() {
2017-05-09 22:46:54 +02:00
return (
< Provider store = { store } >
2018-03-09 22:59:12 +02:00
< App / >
2017-05-09 22:46:54 +02:00
< / Provider >
) ;
}
2017-05-09 21:59:14 +02:00
}