mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
parent
17295734fd
commit
c516ab405b
@ -121,6 +121,9 @@ packages/app-cli/tests/models_Note.js.map
|
|||||||
packages/app-cli/tests/models_Setting.d.ts
|
packages/app-cli/tests/models_Setting.d.ts
|
||||||
packages/app-cli/tests/models_Setting.js
|
packages/app-cli/tests/models_Setting.js
|
||||||
packages/app-cli/tests/models_Setting.js.map
|
packages/app-cli/tests/models_Setting.js.map
|
||||||
|
packages/app-cli/tests/registry.d.ts
|
||||||
|
packages/app-cli/tests/registry.js
|
||||||
|
packages/app-cli/tests/registry.js.map
|
||||||
packages/app-cli/tests/services/plugins/RepositoryApi.d.ts
|
packages/app-cli/tests/services/plugins/RepositoryApi.d.ts
|
||||||
packages/app-cli/tests/services/plugins/RepositoryApi.js
|
packages/app-cli/tests/services/plugins/RepositoryApi.js
|
||||||
packages/app-cli/tests/services/plugins/RepositoryApi.js.map
|
packages/app-cli/tests/services/plugins/RepositoryApi.js.map
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -108,6 +108,9 @@ packages/app-cli/tests/models_Note.js.map
|
|||||||
packages/app-cli/tests/models_Setting.d.ts
|
packages/app-cli/tests/models_Setting.d.ts
|
||||||
packages/app-cli/tests/models_Setting.js
|
packages/app-cli/tests/models_Setting.js
|
||||||
packages/app-cli/tests/models_Setting.js.map
|
packages/app-cli/tests/models_Setting.js.map
|
||||||
|
packages/app-cli/tests/registry.d.ts
|
||||||
|
packages/app-cli/tests/registry.js
|
||||||
|
packages/app-cli/tests/registry.js.map
|
||||||
packages/app-cli/tests/services/plugins/RepositoryApi.d.ts
|
packages/app-cli/tests/services/plugins/RepositoryApi.d.ts
|
||||||
packages/app-cli/tests/services/plugins/RepositoryApi.js
|
packages/app-cli/tests/services/plugins/RepositoryApi.js
|
||||||
packages/app-cli/tests/services/plugins/RepositoryApi.js.map
|
packages/app-cli/tests/services/plugins/RepositoryApi.js.map
|
||||||
|
84
packages/app-cli/tests/registry.ts
Normal file
84
packages/app-cli/tests/registry.ts
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import Setting from '@joplin/lib/models/Setting';
|
||||||
|
import { reg } from '@joplin/lib/registry';
|
||||||
|
|
||||||
|
const sync = {
|
||||||
|
start: jest.fn().mockReturnValue({}),
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Registry', function() {
|
||||||
|
let originalSyncTarget: typeof reg.syncTarget;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
Setting.setConstant('env', 'prod');
|
||||||
|
originalSyncTarget = reg.syncTarget;
|
||||||
|
reg.syncTarget = () => ({
|
||||||
|
isAuthenticated: () => true,
|
||||||
|
synchronizer: () => sync,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
Setting.setConstant('env', 'dev');
|
||||||
|
reg.syncTarget = originalSyncTarget;
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.useFakeTimers();
|
||||||
|
Setting.setValue('sync.interval', 300);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
Setting.setValue('sync.interval', 0);
|
||||||
|
reg.setupRecurrentSync();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when on mobile data', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
Setting.setValue('sync.mobileWifiOnly', true);
|
||||||
|
reg.setIsOnMobileData(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not sync automatically', () => {
|
||||||
|
reg.setupRecurrentSync();
|
||||||
|
jest.runOnlyPendingTimers();
|
||||||
|
|
||||||
|
expect(sync.start).toHaveBeenCalledTimes(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sync if do wifi check is false', done => {
|
||||||
|
void reg.scheduleSync(1, null, false)
|
||||||
|
.then(() =>{
|
||||||
|
expect(sync.start).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.runOnlyPendingTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sync if "sync only over wifi" is disabled in settings', () => {
|
||||||
|
Setting.setValue('sync.mobileWifiOnly', false);
|
||||||
|
reg.setupRecurrentSync();
|
||||||
|
jest.runOnlyPendingTimers();
|
||||||
|
|
||||||
|
expect(sync.start).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when not on mobile data', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
Setting.setValue('sync.mobileWifiOnly', true);
|
||||||
|
reg.setIsOnMobileData(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sync automatically', () => {
|
||||||
|
reg.setupRecurrentSync();
|
||||||
|
jest.runOnlyPendingTimers();
|
||||||
|
|
||||||
|
expect(sync.start).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
@ -6,6 +6,7 @@
|
|||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||||
|
|
||||||
<!-- RN-NOTIFICATION -->
|
<!-- RN-NOTIFICATION -->
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
|
@ -345,6 +345,14 @@ class SideMenuContentComponent extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.props.syncOnlyOverWifi && this.props.isOnMobileData) {
|
||||||
|
items.push(
|
||||||
|
<Text key="net_info" style={this.styles().syncStatus}>
|
||||||
|
{ _('Mobile data - auto-sync disabled') }
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return <View style={{ flex: 0, flexDirection: 'column', paddingBottom: theme.marginBottom }}>{items}</View>;
|
return <View style={{ flex: 0, flexDirection: 'column', paddingBottom: theme.marginBottom }}>{items}</View>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -404,6 +412,8 @@ const SideMenuContent = connect(state => {
|
|||||||
collapsedFolderIds: state.collapsedFolderIds,
|
collapsedFolderIds: state.collapsedFolderIds,
|
||||||
decryptionWorker: state.decryptionWorker,
|
decryptionWorker: state.decryptionWorker,
|
||||||
resourceFetcher: state.resourceFetcher,
|
resourceFetcher: state.resourceFetcher,
|
||||||
|
isOnMobileData: state.isOnMobileData,
|
||||||
|
syncOnlyOverWifi: state.settings['sync.mobileWifiOnly'],
|
||||||
};
|
};
|
||||||
})(SideMenuContentComponent);
|
})(SideMenuContentComponent);
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
"@react-native-community/clipboard": "^1.5.0",
|
"@react-native-community/clipboard": "^1.5.0",
|
||||||
"@react-native-community/datetimepicker": "^3.0.3",
|
"@react-native-community/datetimepicker": "^3.0.3",
|
||||||
"@react-native-community/geolocation": "^2.0.2",
|
"@react-native-community/geolocation": "^2.0.2",
|
||||||
|
"@react-native-community/netinfo": "^6.0.0",
|
||||||
"@react-native-community/push-notification-ios": "^1.6.0",
|
"@react-native-community/push-notification-ios": "^1.6.0",
|
||||||
"@react-native-community/slider": "^3.0.3",
|
"@react-native-community/slider": "^3.0.3",
|
||||||
"buffer": "^5.0.8",
|
"buffer": "^5.0.8",
|
||||||
|
@ -29,6 +29,7 @@ import SyncTargetOneDrive from '@joplin/lib/SyncTargetOneDrive';
|
|||||||
|
|
||||||
const { AppState, Keyboard, NativeModules, BackHandler, Animated, View, StatusBar } = require('react-native');
|
const { AppState, Keyboard, NativeModules, BackHandler, Animated, View, StatusBar } = require('react-native');
|
||||||
|
|
||||||
|
import NetInfo from '@react-native-community/netinfo';
|
||||||
const DropdownAlert = require('react-native-dropdownalert').default;
|
const DropdownAlert = require('react-native-dropdownalert').default;
|
||||||
const AlarmServiceDriver = require('./services/AlarmServiceDriver').default;
|
const AlarmServiceDriver = require('./services/AlarmServiceDriver').default;
|
||||||
const SafeAreaView = require('./components/SafeAreaView');
|
const SafeAreaView = require('./components/SafeAreaView');
|
||||||
@ -118,7 +119,7 @@ const generalMiddleware = (store: any) => (next: any) => async (action: any) =>
|
|||||||
if (action.type == 'NAV_GO') Keyboard.dismiss();
|
if (action.type == 'NAV_GO') Keyboard.dismiss();
|
||||||
|
|
||||||
if (['NOTE_UPDATE_ONE', 'NOTE_DELETE', 'FOLDER_UPDATE_ONE', 'FOLDER_DELETE'].indexOf(action.type) >= 0) {
|
if (['NOTE_UPDATE_ONE', 'NOTE_DELETE', 'FOLDER_UPDATE_ONE', 'FOLDER_DELETE'].indexOf(action.type) >= 0) {
|
||||||
if (!await reg.syncTarget().syncStarted()) void reg.scheduleSync(5 * 1000, { syncSteps: ['update_remote', 'delete_remote'] });
|
if (!await reg.syncTarget().syncStarted()) void reg.scheduleSync(5 * 1000, { syncSteps: ['update_remote', 'delete_remote'] }, true);
|
||||||
SearchEngine.instance().scheduleSyncTables();
|
SearchEngine.instance().scheduleSyncTables();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,7 +152,7 @@ const generalMiddleware = (store: any) => (next: any) => async (action: any) =>
|
|||||||
|
|
||||||
// Schedule a sync operation so that items that need to be encrypted
|
// Schedule a sync operation so that items that need to be encrypted
|
||||||
// are sent to sync target.
|
// are sent to sync target.
|
||||||
void reg.scheduleSync();
|
void reg.scheduleSync(null, null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.type == 'NAV_GO' && action.routeName == 'Notes') {
|
if (action.type == 'NAV_GO' && action.routeName == 'Notes') {
|
||||||
@ -194,6 +195,7 @@ const appDefaultState = Object.assign({}, defaultState, {
|
|||||||
route: DEFAULT_ROUTE,
|
route: DEFAULT_ROUTE,
|
||||||
noteSelectionEnabled: false,
|
noteSelectionEnabled: false,
|
||||||
noteSideMenuOptions: null,
|
noteSideMenuOptions: null,
|
||||||
|
isOnMobileData: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const appReducer = (state = appDefaultState, action: any) => {
|
const appReducer = (state = appDefaultState, action: any) => {
|
||||||
@ -359,6 +361,12 @@ const appReducer = (state = appDefaultState, action: any) => {
|
|||||||
newState.noteSideMenuOptions = action.options;
|
newState.noteSideMenuOptions = action.options;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'MOBILE_DATA_WARNING_UPDATE':
|
||||||
|
|
||||||
|
newState = Object.assign({}, state);
|
||||||
|
newState.isOnMobileData = action.isOnMobileData;
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
error.message = `In reducer: ${error.message} Action: ${JSON.stringify(action)}`;
|
error.message = `In reducer: ${error.message} Action: ${JSON.stringify(action)}`;
|
||||||
@ -583,7 +591,9 @@ async function initialize(dispatch: Function) {
|
|||||||
|
|
||||||
// When the app starts we want the full sync to
|
// When the app starts we want the full sync to
|
||||||
// start almost immediately to get the latest data.
|
// start almost immediately to get the latest data.
|
||||||
void reg.scheduleSync(1000).then(() => {
|
// doWifiConnectionCheck set to true so initial sync
|
||||||
|
// doesn't happen on mobile data
|
||||||
|
void reg.scheduleSync(1000, null, true).then(() => {
|
||||||
// Wait for the first sync before updating the notifications, since synchronisation
|
// Wait for the first sync before updating the notifications, since synchronisation
|
||||||
// might change the notifications.
|
// might change the notifications.
|
||||||
void AlarmService.updateAllNotifications();
|
void AlarmService.updateAllNotifications();
|
||||||
@ -646,6 +656,22 @@ class AppComponent extends React.Component {
|
|||||||
state: 'initializing',
|
state: 'initializing',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
await initialize(this.props.dispatch);
|
await initialize(this.props.dispatch);
|
||||||
|
|
||||||
this.props.dispatch({
|
this.props.dispatch({
|
||||||
@ -679,6 +705,7 @@ class AppComponent extends React.Component {
|
|||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
AppState.removeEventListener('change', this.onAppStateChange_);
|
AppState.removeEventListener('change', this.onAppStateChange_);
|
||||||
|
if (this.unsubscribeNetInfoHandler_) this.unsubscribeNetInfoHandler_();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps: any) {
|
componentDidUpdate(prevProps: any) {
|
||||||
|
@ -949,6 +949,15 @@ class Setting extends BaseModel {
|
|||||||
},
|
},
|
||||||
storage: SettingStorage.File,
|
storage: SettingStorage.File,
|
||||||
},
|
},
|
||||||
|
'sync.mobileWifiOnly': {
|
||||||
|
value: false,
|
||||||
|
type: SettingItemType.Bool,
|
||||||
|
section: 'sync',
|
||||||
|
public: true,
|
||||||
|
label: () => _('Synchronise only over WiFi connection'),
|
||||||
|
storage: SettingStorage.File,
|
||||||
|
appTypes: ['mobile'],
|
||||||
|
},
|
||||||
noteVisiblePanes: { value: ['editor', 'viewer'], type: SettingItemType.Array, storage: SettingStorage.File, public: false, appTypes: ['desktop'] },
|
noteVisiblePanes: { value: ['editor', 'viewer'], type: SettingItemType.Array, storage: SettingStorage.File, public: false, appTypes: ['desktop'] },
|
||||||
tagHeaderIsExpanded: { value: true, type: SettingItemType.Bool, public: false, appTypes: ['desktop'] },
|
tagHeaderIsExpanded: { value: true, type: SettingItemType.Bool, public: false, appTypes: ['desktop'] },
|
||||||
folderHeaderIsExpanded: { value: true, type: SettingItemType.Bool, public: false, appTypes: ['desktop'] },
|
folderHeaderIsExpanded: { value: true, type: SettingItemType.Bool, public: false, appTypes: ['desktop'] },
|
||||||
|
@ -15,6 +15,7 @@ class Registry {
|
|||||||
private scheduleSyncId_: any;
|
private scheduleSyncId_: any;
|
||||||
private recurrentSyncId_: any;
|
private recurrentSyncId_: any;
|
||||||
private db_: any;
|
private db_: any;
|
||||||
|
private isOnMobileData_ = false;
|
||||||
|
|
||||||
logger() {
|
logger() {
|
||||||
if (!this.logger_) {
|
if (!this.logger_) {
|
||||||
@ -38,6 +39,12 @@ class Registry {
|
|||||||
this.showErrorMessageBoxHandler_(message);
|
this.showErrorMessageBoxHandler_(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If isOnMobileData is true, the doWifiConnectionCheck is not set
|
||||||
|
// and the sync.mobileWifiOnly setting is true it will cancel the sync.
|
||||||
|
setIsOnMobileData(isOnMobileData: boolean) {
|
||||||
|
this.isOnMobileData_ = isOnMobileData;
|
||||||
|
}
|
||||||
|
|
||||||
resetSyncTarget(syncTargetId: number = null) {
|
resetSyncTarget(syncTargetId: number = null) {
|
||||||
if (syncTargetId === null) syncTargetId = Setting.value('sync.target');
|
if (syncTargetId === null) syncTargetId = Setting.value('sync.target');
|
||||||
delete this.syncTargets_[syncTargetId];
|
delete this.syncTargets_[syncTargetId];
|
||||||
@ -74,7 +81,7 @@ class Registry {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
scheduleSync = async (delay: number = null, syncOptions: any = null) => {
|
scheduleSync = async (delay: number = null, syncOptions: any = null, doWifiConnectionCheck: boolean = false) => {
|
||||||
this.schedSyncCalls_.push(true);
|
this.schedSyncCalls_.push(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -104,6 +111,12 @@ class Registry {
|
|||||||
this.scheduleSyncId_ = null;
|
this.scheduleSyncId_ = null;
|
||||||
this.logger().info('Preparing scheduled sync');
|
this.logger().info('Preparing scheduled sync');
|
||||||
|
|
||||||
|
if (doWifiConnectionCheck && Setting.value('sync.mobileWifiOnly') && this.isOnMobileData_) {
|
||||||
|
this.logger().info('Sync cancelled because we\'re on mobile data');
|
||||||
|
promiseResolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const syncTargetId = Setting.value('sync.target');
|
const syncTargetId = Setting.value('sync.target');
|
||||||
|
|
||||||
if (!(await this.syncTarget(syncTargetId).isAuthenticated())) {
|
if (!(await this.syncTarget(syncTargetId).isAuthenticated())) {
|
||||||
@ -191,7 +204,7 @@ class Registry {
|
|||||||
|
|
||||||
this.recurrentSyncId_ = shim.setInterval(() => {
|
this.recurrentSyncId_ = shim.setInterval(() => {
|
||||||
this.logger().info('Running background sync on timer...');
|
this.logger().info('Running background sync on timer...');
|
||||||
void this.scheduleSync(0);
|
void this.scheduleSync(0, null, true);
|
||||||
}, 1000 * Setting.value('sync.interval'));
|
}, 1000 * Setting.value('sync.interval'));
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
Loading…
Reference in New Issue
Block a user