mirror of
https://github.com/laurent22/joplin.git
synced 2025-02-01 19:15:01 +02:00
Merge branch 'dev' of github.com:laurent22/joplin into dev
This commit is contained in:
commit
05f65c326a
@ -145,7 +145,7 @@ export default function useKeymap(CodeMirror: any) {
|
||||
};
|
||||
if (shim.isMac()) {
|
||||
CodeMirror.keyMap.default = {
|
||||
// MacOS
|
||||
// macOS
|
||||
'Shift-Cmd-Z': 'redo',
|
||||
'Cmd-Y': 'redo',
|
||||
'Cmd-End': 'goDocEnd',
|
||||
@ -153,10 +153,12 @@ export default function useKeymap(CodeMirror: any) {
|
||||
'Cmd-Home': 'goDocStart',
|
||||
'Cmd-Up': 'goDocStart',
|
||||
'Ctrl-D': 'delCharAfter',
|
||||
'Cmd-Left': 'goGroupLeft',
|
||||
'Cmd-Right': 'goGroupRight',
|
||||
'Alt-Left': 'goGroupLeft',
|
||||
'Alt-Right': 'goGroupRight',
|
||||
'Ctrl-A': 'goLineStart',
|
||||
'Ctrl-E': 'goLineEnd',
|
||||
'Cmd-Left': 'goLineLeftSmart',
|
||||
'Cmd-Right': 'goLineRightSmart',
|
||||
'Alt-Backspace': 'delGroupBefore',
|
||||
'Alt-Delete': 'delGroupAfter',
|
||||
|
||||
|
@ -153,6 +153,7 @@ class ResourceScreenComponent extends React.Component<Props, State> {
|
||||
order: [{
|
||||
by: getSortingOrderColumn(sorting.order),
|
||||
dir: sorting.type,
|
||||
caseInsensitive: true,
|
||||
}],
|
||||
limit: MAX_RESOURCES,
|
||||
fields: ['title', 'id', 'size', 'file_extension'],
|
||||
|
@ -19,16 +19,19 @@
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
|
||||
|
||||
<!--
|
||||
Android 10 introduced new "scoped storage" mechanism.
|
||||
android:requestLegacyExternalStorage: Android 10 introduced new "scoped storage" mechanism.
|
||||
Apps targeting Android 10 (sdk 29) can no longer freely access arbitrary paths on the shared storage.
|
||||
The attribute "requestLegacyExternalStorage" below allows to opt out of this restriction.
|
||||
This attribute allows to opt out of this restriction.
|
||||
|
||||
android:allowBackup: used to enable Android Backup which some users need:
|
||||
https://github.com/laurent22/joplin/issues/4020
|
||||
-->
|
||||
<application
|
||||
android:name=".MainApplication"
|
||||
android:label="@string/app_name"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:allowBackup="false"
|
||||
android:allowBackup="true"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:theme="@style/AppTheme">
|
||||
|
@ -1,6 +1,7 @@
|
||||
const moment = require('moment');
|
||||
const { dirname, basename } = require('./path-utils');
|
||||
const shim = require('./shim').default;
|
||||
const Buffer = require('buffer').Buffer;
|
||||
|
||||
class FileApiDriverOneDrive {
|
||||
constructor(api) {
|
||||
@ -133,17 +134,19 @@ class FileApiDriverOneDrive {
|
||||
if (!options) options = {};
|
||||
|
||||
let response = null;
|
||||
// We need to check the file size as files > 4 MBs are uploaded in a different way than files < 4 MB (see https://docs.microsoft.com/de-de/onedrive/developer/rest-api/concepts/upload?view=odsp-graph-online)
|
||||
let byteSize = null;
|
||||
|
||||
if (options.source == 'file') {
|
||||
// We need to check the file size as files > 4 MBs are uploaded in a different way than files < 4 MB (see https://docs.microsoft.com/de-de/onedrive/developer/rest-api/concepts/upload?view=odsp-graph-online)
|
||||
const fileSize = (await shim.fsDriver().stat(options.path)).size;
|
||||
path = fileSize < 4 * 1024 * 1024 ? `${this.makePath_(path)}:/content` : `${this.makePath_(path)}:/createUploadSession`;
|
||||
response = await this.api_.exec('PUT', path, null, null, options);
|
||||
byteSize = (await shim.fsDriver().stat(options.path)).size;
|
||||
} else {
|
||||
options.headers = { 'Content-Type': 'text/plain' };
|
||||
response = await this.api_.exec('PUT', `${this.makePath_(path)}:/content`, null, content, options);
|
||||
byteSize = Buffer.byteLength(content);
|
||||
}
|
||||
|
||||
path = byteSize < 4 * 1024 * 1024 ? `${this.makePath_(path)}:/content` : `${this.makePath_(path)}:/createUploadSession`;
|
||||
response = await this.api_.exec('PUT', path, null, content, options);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ const time = require('./time').default;
|
||||
const Logger = require('./Logger').default;
|
||||
const { _ } = require('./locale');
|
||||
const urlUtils = require('./urlUtils.js');
|
||||
const Buffer = require('buffer').Buffer;
|
||||
|
||||
class OneDriveApi {
|
||||
// `isPublic` is to tell OneDrive whether the application is a "public" one (Mobile and desktop
|
||||
@ -136,20 +137,25 @@ class OneDriveApi {
|
||||
}
|
||||
}
|
||||
|
||||
async uploadChunk(url, handle, options) {
|
||||
async uploadChunk(url, handle, buffer, options) {
|
||||
options = Object.assign({}, options);
|
||||
if (!options.method) { options.method = 'POST'; }
|
||||
if (!options.headers) { options.headers = {}; }
|
||||
|
||||
if (!options.contentLength) throw new Error(' uploadChunk: contentLength is missing');
|
||||
if (!options.contentLength) throw new Error('uploadChunk: contentLength is missing');
|
||||
if (!options.headers) throw new Error('uploadChunk: header is missing');
|
||||
|
||||
if (buffer) {
|
||||
options.body = buffer.slice(options.startByte, options.startByte + options.contentLength);
|
||||
} else {
|
||||
const chunk = await shim.fsDriver().readFileChunk(handle, options.contentLength);
|
||||
const buffer = Buffer.from(chunk, 'base64');
|
||||
options.body = buffer;
|
||||
}
|
||||
|
||||
const chunk = await shim.fsDriver().readFileChunk(handle, options.contentLength);
|
||||
const Buffer = require('buffer').Buffer;
|
||||
const buffer = Buffer.from(chunk, 'base64');
|
||||
delete options.contentLength;
|
||||
options.body = buffer;
|
||||
delete options.startByte;
|
||||
|
||||
const response = await shim.fetch(url, options);
|
||||
const response = await shim.fetch(url,options);
|
||||
return response;
|
||||
}
|
||||
|
||||
@ -165,12 +171,20 @@ class OneDriveApi {
|
||||
return response;
|
||||
} else {
|
||||
const uploadUrl = (await response.json()).uploadUrl;
|
||||
// uploading file in 7.5 MiB-Fragments (except the last one) because this is the mean of 5 and 10 Mib which are the recommended lower and upper limits.
|
||||
// https://docs.microsoft.com/de-de/onedrive/developer/rest-api/api/driveitem_createuploadsession?view=odsp-graph-online#best-practices
|
||||
const chunkSize = 7.5 * 1024 * 1024;
|
||||
const fileSize = (await shim.fsDriver().stat(options.path)).size;
|
||||
const numberOfChunks = Math.ceil(fileSize / chunkSize);
|
||||
const handle = await shim.fsDriver().open(options.path, 'r');
|
||||
|
||||
let byteSize = null;
|
||||
let handle = null;
|
||||
let buffer = null;
|
||||
if (options.body) {
|
||||
byteSize = Buffer.byteLength(options.body);
|
||||
buffer = Buffer.from(options.body);
|
||||
} else {
|
||||
byteSize = (await shim.fsDriver().stat(options.path)).size;
|
||||
handle = await shim.fsDriver().open(options.path, 'r');
|
||||
}
|
||||
|
||||
const numberOfChunks = Math.ceil(byteSize / chunkSize);
|
||||
|
||||
try {
|
||||
for (let i = 0; i < numberOfChunks; i++) {
|
||||
@ -179,32 +193,34 @@ class OneDriveApi {
|
||||
let contentLength = null;
|
||||
if (i === numberOfChunks - 1) {
|
||||
// Last fragment. It is not ensured that the last fragment is a multiple of 327,680 bytes as recommanded in the api doc. The reasons is that the docs are out of day for this purpose: https://github.com/OneDrive/onedrive-api-docs/issues/1200#issuecomment-597281253
|
||||
endByte = fileSize - 1;
|
||||
contentLength = fileSize - ((numberOfChunks - 1) * chunkSize);
|
||||
endByte = byteSize - 1;
|
||||
contentLength = byteSize - ((numberOfChunks - 1) * chunkSize);
|
||||
} else {
|
||||
endByte = (i + 1) * chunkSize - 1;
|
||||
contentLength = chunkSize;
|
||||
}
|
||||
this.logger().debug(`${options.path}: Uploading File Fragment ${(startByte / 1048576).toFixed(2)} - ${(endByte / 1048576).toFixed(2)} from ${(fileSize / 1048576).toFixed(2)} Mbit ...`);
|
||||
this.logger().debug(`Uploading File Fragment ${(startByte / 1048576).toFixed(2)} - ${(endByte / 1048576).toFixed(2)} from ${(byteSize / 1048576).toFixed(2)} Mbit ...`);
|
||||
const headers = {
|
||||
'Content-Length': contentLength,
|
||||
'Content-Range': `bytes ${startByte}-${endByte}/${fileSize}`,
|
||||
'Content-Range': `bytes ${startByte}-${endByte}/${byteSize}`,
|
||||
'Content-Type': 'application/octet-stream; charset=utf-8',
|
||||
};
|
||||
|
||||
const response = await this.uploadChunk(uploadUrl, handle, { contentLength: contentLength, method: 'PUT', headers: headers });
|
||||
|
||||
const response = await this.uploadChunk(uploadUrl, handle, buffer, { startByte: startByte, contentLength: contentLength, method: 'PUT', headers: headers });
|
||||
if (!response.ok) {
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
return { ok: true };
|
||||
} catch (error) {
|
||||
this.logger().error('Got unhandled error:', error ? error.code : '', error ? error.message : '', error);
|
||||
const type = (handle) ? 'Resource' : 'Note Content';
|
||||
this.logger().error(`Couldn't upload ${type} > 4 Mb. Got unhandled error:`, error ? error.code : '', error ? error.message : '', error);
|
||||
throw error;
|
||||
} finally {
|
||||
await shim.fsDriver().close(handle);
|
||||
if (handle) await shim.fsDriver().close(handle);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -250,8 +266,10 @@ class OneDriveApi {
|
||||
|
||||
let response = null;
|
||||
try {
|
||||
if (options.source == 'file' && (method == 'POST' || method == 'PUT')) {
|
||||
response = path.includes('/createUploadSession') ? await this.uploadBigFile(url, options) : await shim.uploadBlob(url, options);
|
||||
if (path.includes('/createUploadSession')) {
|
||||
response = await this.uploadBigFile(url, options);
|
||||
} else if (options.source == 'file' && (method == 'POST' || method == 'PUT')) {
|
||||
response = await shim.uploadBlob(url, options);
|
||||
} else if (options.target == 'string') {
|
||||
response = await shim.fetch(url, options);
|
||||
} else {
|
||||
|
Loading…
x
Reference in New Issue
Block a user