You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-08-30 20:39:46 +02:00
Compare commits
6 Commits
android-v2
...
resource_b
Author | SHA1 | Date | |
---|---|---|---|
|
7d9997da61 | ||
|
7bafb1a546 | ||
|
d789c10ba2 | ||
|
b98f421752 | ||
|
f0aeb630bc | ||
|
14a1373e6d |
@@ -242,7 +242,6 @@ packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/Editor.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/CodeMirror.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/Editor.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/useEditorCommands.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/PlainEditor/PlainEditor.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/joplinCommandToTinyMceCommands.js
|
||||
@@ -500,8 +499,7 @@ packages/app-mobile/utils/autodetectTheme.js
|
||||
packages/app-mobile/utils/checkPermissions.js
|
||||
packages/app-mobile/utils/createRootStyle.js
|
||||
packages/app-mobile/utils/debounce.js
|
||||
packages/app-mobile/utils/fs-driver/fs-driver-rn.js
|
||||
packages/app-mobile/utils/fs-driver/runOnDeviceTests.js
|
||||
packages/app-mobile/utils/fs-driver-rn.js
|
||||
packages/app-mobile/utils/setupNotifications.js
|
||||
packages/app-mobile/utils/shareHandler.js
|
||||
packages/app-mobile/utils/types.js
|
||||
@@ -535,7 +533,6 @@ packages/editor/CodeMirror/testUtil/createTestEditor.js
|
||||
packages/editor/CodeMirror/testUtil/forceFullParse.js
|
||||
packages/editor/CodeMirror/testUtil/loadLanguages.js
|
||||
packages/editor/CodeMirror/theme.js
|
||||
packages/editor/CodeMirror/util/isInSyntaxNode.js
|
||||
packages/editor/SelectionFormatting.js
|
||||
packages/editor/events.js
|
||||
packages/editor/types.js
|
||||
@@ -627,7 +624,6 @@ packages/lib/import-enex.js
|
||||
packages/lib/initLib.js
|
||||
packages/lib/locale.test.js
|
||||
packages/lib/locale.js
|
||||
packages/lib/makeDiscourseDebugUrl.js
|
||||
packages/lib/markdownUtils.test.js
|
||||
packages/lib/markdownUtils.js
|
||||
packages/lib/markdownUtils2.test.js
|
||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@@ -224,7 +224,6 @@ packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/Editor.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/CodeMirror.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/Editor.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/useEditorCommands.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/PlainEditor/PlainEditor.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/joplinCommandToTinyMceCommands.js
|
||||
@@ -482,8 +481,7 @@ packages/app-mobile/utils/autodetectTheme.js
|
||||
packages/app-mobile/utils/checkPermissions.js
|
||||
packages/app-mobile/utils/createRootStyle.js
|
||||
packages/app-mobile/utils/debounce.js
|
||||
packages/app-mobile/utils/fs-driver/fs-driver-rn.js
|
||||
packages/app-mobile/utils/fs-driver/runOnDeviceTests.js
|
||||
packages/app-mobile/utils/fs-driver-rn.js
|
||||
packages/app-mobile/utils/setupNotifications.js
|
||||
packages/app-mobile/utils/shareHandler.js
|
||||
packages/app-mobile/utils/types.js
|
||||
@@ -517,7 +515,6 @@ packages/editor/CodeMirror/testUtil/createTestEditor.js
|
||||
packages/editor/CodeMirror/testUtil/forceFullParse.js
|
||||
packages/editor/CodeMirror/testUtil/loadLanguages.js
|
||||
packages/editor/CodeMirror/theme.js
|
||||
packages/editor/CodeMirror/util/isInSyntaxNode.js
|
||||
packages/editor/SelectionFormatting.js
|
||||
packages/editor/events.js
|
||||
packages/editor/types.js
|
||||
@@ -609,7 +606,6 @@ packages/lib/import-enex.js
|
||||
packages/lib/initLib.js
|
||||
packages/lib/locale.test.js
|
||||
packages/lib/locale.js
|
||||
packages/lib/makeDiscourseDebugUrl.js
|
||||
packages/lib/markdownUtils.test.js
|
||||
packages/lib/markdownUtils.js
|
||||
packages/lib/markdownUtils2.test.js
|
||||
|
@@ -1,25 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Joplin]]></title><description><![CDATA[Joplin, the open source note-taking application]]></description><link>https://joplinapp.org</link><generator>RSS for Node</generator><lastBuildDate>Mon, 23 Oct 2023 00:00:00 GMT</lastBuildDate><atom:link href="https://joplinapp.org/rss.xml" rel="self" type="application/rss+xml"/><pubDate>Mon, 23 Oct 2023 00:00:00 GMT</pubDate><item><title><![CDATA[Working in the shadows with white-hat hackers]]></title><description><![CDATA[<p>The majority of Joplin's development is carried out in the public domain. This includes the discussion of issues on GitHub, as well as the submission of pull requests and related discussions. The transparency of these processes allows for collaborative problem-solving and shared insights.</p>
|
||||
<p>However, there is one aspect that operates behind closed doors, and for good reason: addressing cybersecurity vulnerabilities. It is imperative that these issues remain undisclosed until they have been resolved. Once a solution is implemented, it is usually accompanied by discreet commits and a message in the changelog to signify the progress made.</p>
|
||||
<p>Typically, the process begins with an email from a security researcher. They provide valuable insights, such as a specially crafted note that triggers a bug, or an API call, along with an explanation of how the application's security can be circumvented. We examine the vulnerability, create a fix, and create automated test units to prevent any accidental reintroduction of the vulnerability in future code updates. An example of such a commit is: <a href="https://github.com/laurent22/joplin/commit/9e90d9016daf79b5414646a93fd369aedb035071">9e90d9016daf79b5414646a93fd369aedb035071</a></p>
|
||||
<p>We then share our fix with the researcher for validation. Additionally, we often apply the fix to previous versions of Joplin, depending on the severity of the vulnerability.</p>
|
||||
<p>The contribution of security researchers in this regard is immeasurable. They employ their ingenuity to identify inventive methods of bypassing existing security measures and often discover subtle flaws in the code that might otherwise go unnoticed.</p>
|
||||
<p>We would like to express our sincere gratitude to the security researchers who have assisted us throughout the years in identifying and rectifying security vulnerabilities!</p>
|
||||
<ul>
|
||||
<li><a href="https://github.com/a1ise">@Alise</a></li>
|
||||
<li>@hexodotsh</li>
|
||||
<li><a href="https://github.com/ly1g3">@ly1g3</a></li>
|
||||
<li><a href="https://twitter.com/maple3142">@maple3142</a></li>
|
||||
<li>Ademar Nowasky Junior</li>
|
||||
<li><a href="mailto:ben@mayhem.sg">Benjamin Harris</a></li>
|
||||
<li><a href="https://github.com/JavierOlmedo">Javier Olmedo</a></li>
|
||||
<li><a href="https://twitter.com/newfolderj">Jubair Rehman Yousafzai</a></li>
|
||||
<li>lin@UCCU Hacker</li>
|
||||
<li><a href="https://github.com/personalizedrefrigerator">personalizedrefrigerator</a></li>
|
||||
<li><a href="https://twitter.com/fhlipZero">Phil Holbrook</a></li>
|
||||
<li><a href="https://ryotak.net/">RyotaK</a></li>
|
||||
<li><a href="https://twitter.com/YNizry">Yaniv Nizry</a></li>
|
||||
</ul>
|
||||
]]></description><link>https://joplinapp.org/news/20231023-white-hat-hackers/</link><guid isPermaLink="false">20231023-white-hat-hackers</guid><pubDate>Mon, 23 Oct 2023 00:00:00 GMT</pubDate><twitter-text>Working in the shadows with white-hat hackers</twitter-text></item><item><title><![CDATA[What's new in Joplin 2.12]]></title><description><![CDATA[<h1>Desktop<a name="desktop" href="#desktop" class="heading-anchor">🔗</a></h1>
|
||||
<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Joplin]]></title><description><![CDATA[Joplin, the open source note-taking application]]></description><link>https://joplinapp.org</link><generator>RSS for Node</generator><lastBuildDate>Wed, 06 Sep 2023 12:00:00 GMT</lastBuildDate><atom:link href="https://joplinapp.org/rss.xml" rel="self" type="application/rss+xml"/><pubDate>Wed, 06 Sep 2023 12:00:00 GMT</pubDate><item><title><![CDATA[What's new in Joplin 2.12]]></title><description><![CDATA[<h1>Desktop<a name="desktop" href="#desktop" class="heading-anchor">🔗</a></h1>
|
||||
<h2>Support for Apple Silicon<a name="support-for-apple-silicon" href="#support-for-apple-silicon" class="heading-anchor">🔗</a></h2>
|
||||
<p>A new release is now available for Apple Silicon, which provides improve performances on this architecture.</p>
|
||||
<h2>Rich Text editor<a name="rich-text-editor" href="#rich-text-editor" class="heading-anchor">🔗</a></h2>
|
||||
@@ -340,4 +319,17 @@
|
||||
<p><a href="https://joplinapp.org/changelog_android/">https://joplinapp.org/changelog_android/</a></p>
|
||||
<p><a href="https://joplinapp.org/changelog_ios/">https://joplinapp.org/changelog_ios/</a></p>
|
||||
<p><a href="https://joplinapp.org/changelog_cli/">https://joplinapp.org/changelog_cli/</a></p>
|
||||
]]></description><link>https://joplinapp.org/news/20211217-120324/</link><guid isPermaLink="false">20211217-120324</guid><pubDate>Fri, 17 Dec 2021 12:03:24 GMT</pubDate><twitter-text></twitter-text></item></channel></rss>
|
||||
]]></description><link>https://joplinapp.org/news/20211217-120324/</link><guid isPermaLink="false">20211217-120324</guid><pubDate>Fri, 17 Dec 2021 12:03:24 GMT</pubDate><twitter-text></twitter-text></item><item><title><![CDATA[Potential breaking change in next Joplin Server update (2.5.10)]]></title><description><![CDATA[<p>Just a head up that the next Joplin Server update could potentially include a breaking change, depending on your data.</p>
|
||||
<p>One of the database migration is going to add an "owner_id" column to the "items" table (where all notes, notebooks, etc. are stored), and automatically populate it. Normally that shouldn't take too long but you might want to make sure you won't need the server right away when you process this.</p>
|
||||
<p>The second database migration will add a unique constraint on items.name and items.owner_id and that's where the breaking change might be. Normally this data is already unique because that's enforced by the application but in some rare cases, due a race condition, there could be duplicate data in there. If that happens the migration will fail and the server will not start.</p>
|
||||
<p>If that happens, you'll need to decide what to do with the data, as it's not possible to automatically decide. You can find all duplicates using this query:</p>
|
||||
<p><code><strong>select</strong> count(<em>), name, owner_id<br>
|
||||
<strong>from</strong> items <strong>group</strong> <strong>by</strong> name, owner_id<br>
|
||||
<strong>having</strong> count(</em>) > 1;</code></p>
|
||||
<p>Once you have the list of IDs you have a few options:</p>
|
||||
<ul>
|
||||
<li>Find the corresponding item in Joplin (it can unfortunately be anything - a note, resource, folder, etc.), then delete it and sync.</li>
|
||||
<li>Or, just delete the data directly in the database. You'll want to delete the corresponding item_id from the user_items table too.</li>
|
||||
</ul>
|
||||
<p>But really in most cases you should be fine. Especially if you don't have that many notes it's unlikely you have duplicates.</p>
|
||||
]]></description><link>https://joplinapp.org/news/20211102-150403/</link><guid isPermaLink="false">20211102-150403</guid><pubDate>Tue, 02 Nov 2021 15:04:03 GMT</pubDate><twitter-text></twitter-text></item></channel></rss>
|
@@ -4,7 +4,6 @@ import shim from '@joplin/lib/shim';
|
||||
import { isCallbackUrl } from '@joplin/lib/callbackUrlUtils';
|
||||
|
||||
import { BrowserWindow, Tray, screen } from 'electron';
|
||||
import bridge from './bridge';
|
||||
const url = require('url');
|
||||
const path = require('path');
|
||||
const { dirname } = require('@joplin/lib/path-utils');
|
||||
@@ -143,15 +142,6 @@ export default class ElectronAppWrapper {
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// will-frame-navigate is fired by clicking on a link within the BrowserWindow.
|
||||
this.win_.webContents.on('will-frame-navigate', event => {
|
||||
// If the link changes the URL of the browser window,
|
||||
if (event.isMainFrame) {
|
||||
event.preventDefault();
|
||||
void bridge().openExternal(event.url);
|
||||
}
|
||||
});
|
||||
|
||||
this.win_.on('close', (event: any) => {
|
||||
// If it's on macOS, the app is completely closed only if the user chooses to close the app (willQuitApp_ will be true)
|
||||
// otherwise the window is simply hidden, and will be re-open once the app is "activated" (which happens when the
|
||||
|
@@ -184,16 +184,11 @@ export class Bridge {
|
||||
return dialog.showMessageBoxSync(window, options);
|
||||
}
|
||||
|
||||
public showErrorMessageBox(message: string, options: any = null) {
|
||||
options = {
|
||||
buttons: [_('OK')],
|
||||
...options,
|
||||
};
|
||||
|
||||
public showErrorMessageBox(message: string) {
|
||||
return this.showMessageBox_(this.window(), {
|
||||
type: 'error',
|
||||
message: message,
|
||||
buttons: options.buttons,
|
||||
buttons: [_('OK')],
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -740,9 +740,7 @@ class MainScreenComponent extends React.Component<Props, State> {
|
||||
editor: () => {
|
||||
let bodyEditor = this.props.settingEditorCodeView ? 'CodeMirror' : 'TinyMCE';
|
||||
|
||||
if (this.props.isSafeMode) {
|
||||
bodyEditor = 'PlainText';
|
||||
} else if (this.props.settingEditorCodeView && this.props.enableBetaMarkdownEditor) {
|
||||
if (this.props.settingEditorCodeView && this.props.enableBetaMarkdownEditor) {
|
||||
bodyEditor = 'CodeMirror6';
|
||||
}
|
||||
return <NoteEditor key={key} bodyEditor={bodyEditor} />;
|
||||
|
@@ -9,7 +9,6 @@ import { PluginStates, utils as pluginUtils } from '@joplin/lib/services/plugins
|
||||
import shim from '@joplin/lib/shim';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import versionInfo from '@joplin/lib/versionInfo';
|
||||
import makeDiscourseDebugUrl from '@joplin/lib/makeDiscourseDebugUrl';
|
||||
import { ImportModule } from '@joplin/lib/services/interop/Module';
|
||||
import InteropServiceHelper from '../InteropServiceHelper';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
@@ -317,30 +316,14 @@ function useMenu(props: Props) {
|
||||
bridge().showErrorMessageBox(error.message);
|
||||
}
|
||||
|
||||
void CommandService.instance().execute('hideModalMessage');
|
||||
|
||||
if (errors.length) {
|
||||
const response = bridge().showErrorMessageBox('There was some errors importing the notes - check the console for more details.\n\nPlease consider sending a bug report to the forum!', {
|
||||
buttons: [_('Close'), _('Send bug report')],
|
||||
});
|
||||
|
||||
bridge().showErrorMessageBox('There was some errors importing the notes. Please check the console for more details.');
|
||||
props.dispatch({ type: 'NOTE_DEVTOOLS_SET', value: true });
|
||||
|
||||
if (response === 1) {
|
||||
const url = makeDiscourseDebugUrl(
|
||||
`Error importing notes from format: ${module.format}`,
|
||||
`- Input format: ${module.format}\n- Output format: ${module.outputFormat}`,
|
||||
errors,
|
||||
packageInfo,
|
||||
PluginService.instance(),
|
||||
props.pluginSettings,
|
||||
);
|
||||
|
||||
void bridge().openExternal(url);
|
||||
}
|
||||
}
|
||||
|
||||
void CommandService.instance().execute('hideModalMessage');
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.selectedFolderId, props.pluginSettings]);
|
||||
}, [props.selectedFolderId]);
|
||||
|
||||
const onMenuItemClickRef = useRef(null);
|
||||
onMenuItemClickRef.current = onMenuItemClick;
|
||||
@@ -834,7 +817,6 @@ function useMenu(props: Props) {
|
||||
menuItemDic.setTags,
|
||||
menuItemDic.showShareNoteDialog,
|
||||
separator(),
|
||||
menuItemDic.showNoteProperties,
|
||||
menuItemDic.showNoteContentProperties,
|
||||
],
|
||||
},
|
||||
|
@@ -1,56 +0,0 @@
|
||||
|
||||
// Used in safe mode
|
||||
|
||||
import * as React from 'react';
|
||||
import { ForwardedRef } from 'react';
|
||||
import { useEffect, useCallback, useRef, forwardRef, useImperativeHandle } from 'react';
|
||||
import { NoteBodyEditorProps, NoteBodyEditorRef } from '../../utils/types';
|
||||
|
||||
const PlainEditor = (props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditorRef>) => {
|
||||
const editorRef = useRef<HTMLTextAreaElement>();
|
||||
|
||||
useImperativeHandle(ref, () => {
|
||||
return {
|
||||
content: () => editorRef.current?.value ?? '',
|
||||
resetScroll: () => {
|
||||
editorRef.current.scrollTop = 0;
|
||||
},
|
||||
scrollTo: () => {
|
||||
// Not supported
|
||||
},
|
||||
|
||||
supportsCommand: _name => {
|
||||
return false;
|
||||
},
|
||||
execCommand: async _command => {
|
||||
// Not supported
|
||||
},
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!editorRef.current) return;
|
||||
|
||||
if (editorRef.current.value !== props.content) {
|
||||
editorRef.current.value = props.content;
|
||||
}
|
||||
}, [props.content]);
|
||||
|
||||
const onChange = useCallback((event: any) => {
|
||||
props.onChange({ changeId: null, content: event.target.value });
|
||||
}, [props.onChange]);
|
||||
|
||||
return (
|
||||
<div style={props.style}>
|
||||
<textarea
|
||||
ref={editorRef}
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
defaultValue={props.content}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default forwardRef(PlainEditor);
|
||||
|
@@ -0,0 +1,50 @@
|
||||
// Kept only for reference
|
||||
|
||||
import * as React from 'react';
|
||||
import { useEffect, useCallback, useRef, forwardRef, useImperativeHandle } from 'react';
|
||||
|
||||
export interface OnChangeEvent {
|
||||
changeId: number,
|
||||
content: any,
|
||||
}
|
||||
|
||||
interface PlainEditorProps {
|
||||
style: any,
|
||||
onChange(event: OnChangeEvent): void,
|
||||
onWillChange(event:any): void,
|
||||
markupToHtml: Function,
|
||||
disabled: boolean,
|
||||
}
|
||||
|
||||
const PlainEditor = (props:PlainEditorProps, ref:any) => {
|
||||
const editorRef = useRef<any>();
|
||||
|
||||
useImperativeHandle(ref, () => {
|
||||
return {
|
||||
content: () => '',
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!editorRef.current) return;
|
||||
editorRef.current.value = props.defaultEditorState.value;
|
||||
}, [props.defaultEditorState]);
|
||||
|
||||
const onChange = useCallback((event:any) => {
|
||||
props.onChange({ changeId: null, content: event.target.value });
|
||||
}, [props.onWillChange, props.onChange]);
|
||||
|
||||
return (
|
||||
<div style={props.style}>
|
||||
<textarea
|
||||
ref={editorRef}
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
defaultValue={props.defaultEditorState.value}
|
||||
onChange={onChange}
|
||||
/>;
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default forwardRef(PlainEditor);
|
||||
|
@@ -14,7 +14,7 @@ import useFormNote, { OnLoadEvent } from './utils/useFormNote';
|
||||
import useEffectiveNoteId from './utils/useEffectiveNoteId';
|
||||
import useFolder from './utils/useFolder';
|
||||
import styles_ from './styles';
|
||||
import { NoteEditorProps, FormNote, ScrollOptions, ScrollOptionTypes, OnChangeEvent, NoteBodyEditorProps, AllAssetsOptions, NoteBodyEditorRef } from './utils/types';
|
||||
import { NoteEditorProps, FormNote, ScrollOptions, ScrollOptionTypes, OnChangeEvent, NoteBodyEditorProps, AllAssetsOptions } from './utils/types';
|
||||
import ResourceEditWatcher from '@joplin/lib/services/ResourceEditWatcher/index';
|
||||
import CommandService from '@joplin/lib/services/CommandService';
|
||||
import ToolbarButton from '../ToolbarButton/ToolbarButton';
|
||||
@@ -45,7 +45,6 @@ import { ModelType } from '@joplin/lib/BaseModel';
|
||||
import BaseItem from '@joplin/lib/models/BaseItem';
|
||||
import { ErrorCode } from '@joplin/lib/errors';
|
||||
import ItemChange from '@joplin/lib/models/ItemChange';
|
||||
import PlainEditor from './NoteBody/PlainEditor/PlainEditor';
|
||||
import CodeMirror6 from './NoteBody/CodeMirror/v6/CodeMirror';
|
||||
import CodeMirror5 from './NoteBody/CodeMirror/v5/CodeMirror';
|
||||
|
||||
@@ -61,7 +60,7 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
const [scrollWhenReady, setScrollWhenReady] = useState<ScrollOptions>(null);
|
||||
const [isReadOnly, setIsReadOnly] = useState<boolean>(false);
|
||||
|
||||
const editorRef = useRef<NoteBodyEditorRef>();
|
||||
const editorRef = useRef<any>();
|
||||
const titleInputRef = useRef<any>();
|
||||
const isMountedRef = useRef(true);
|
||||
const noteSearchBarRef = useRef(null);
|
||||
@@ -391,7 +390,8 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
noteId: formNoteRef.current.id,
|
||||
percent: event.percent,
|
||||
});
|
||||
}, [props.dispatch]);
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.dispatch, formNote]);
|
||||
|
||||
function renderNoNotes(rootStyle: any) {
|
||||
const emptyDivStyle = {
|
||||
@@ -462,8 +462,6 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
|
||||
if (props.bodyEditor === 'TinyMCE') {
|
||||
editor = <TinyMCE {...editorProps}/>;
|
||||
} else if (props.bodyEditor === 'PlainText') {
|
||||
editor = <PlainEditor {...editorProps}/>;
|
||||
} else if (props.bodyEditor === 'CodeMirror') {
|
||||
editor = <CodeMirror5 {...editorProps}/>;
|
||||
} else if (props.bodyEditor === 'CodeMirror6') {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { RefObject, useEffect } from 'react';
|
||||
import { FormNote, NoteBodyEditorRef, ScrollOptionTypes } from './types';
|
||||
import { useEffect } from 'react';
|
||||
import { FormNote, ScrollOptionTypes } from './types';
|
||||
import editorCommandDeclarations, { enabledCondition } from '../editorCommandDeclarations';
|
||||
import CommandService, { CommandDeclaration, CommandRuntime, CommandContext } from '@joplin/lib/services/CommandService';
|
||||
import time from '@joplin/lib/time';
|
||||
@@ -12,8 +12,6 @@ const commandsWithDependencies = [
|
||||
require('../commands/pasteAsText'),
|
||||
];
|
||||
|
||||
type SetFormNoteCallback = (callback: (prev: FormNote)=> FormNote)=> void;
|
||||
|
||||
interface HookDependencies {
|
||||
formNote: FormNote;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
||||
@@ -21,18 +19,16 @@ interface HookDependencies {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
||||
dispatch: Function;
|
||||
noteSearchBarRef: any;
|
||||
editorRef: RefObject<NoteBodyEditorRef>;
|
||||
editorRef: any;
|
||||
titleInputRef: any;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
||||
saveNoteAndWait: Function;
|
||||
setFormNote: SetFormNoteCallback;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
||||
setFormNote: Function;
|
||||
}
|
||||
|
||||
function editorCommandRuntime(
|
||||
declaration: CommandDeclaration,
|
||||
editorRef: RefObject<NoteBodyEditorRef>,
|
||||
setFormNote: SetFormNoteCallback,
|
||||
): CommandRuntime {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
||||
function editorCommandRuntime(declaration: CommandDeclaration, editorRef: any, setFormNote: Function): CommandRuntime {
|
||||
return {
|
||||
execute: async (_context: CommandContext, ...args: any[]) => {
|
||||
if (!editorRef.current) {
|
||||
|
@@ -67,6 +67,5 @@ export default function() {
|
||||
'switchProfile2',
|
||||
'switchProfile3',
|
||||
'pasteAsText',
|
||||
'showNoteProperties',
|
||||
];
|
||||
}
|
||||
|
@@ -76,49 +76,4 @@ test.describe('main', () => {
|
||||
|
||||
await expect(mainScreen.noteListContainer.locator('[title^="Toggle sort order"]')).not.toBeVisible();
|
||||
});
|
||||
|
||||
test('clicking on an external link should try to launch a browser', async ({ electronApp, mainWindow }) => {
|
||||
const mainScreen = new MainScreen(mainWindow);
|
||||
await mainScreen.waitFor();
|
||||
|
||||
// Mock openExternal
|
||||
const nextExternalUrlPromise = electronApp.evaluate(({ shell }) => {
|
||||
return new Promise<string>(resolve => {
|
||||
const openExternal = async (url: string) => {
|
||||
resolve(url);
|
||||
};
|
||||
shell.openExternal = openExternal;
|
||||
});
|
||||
});
|
||||
|
||||
// Create a test link
|
||||
const testLinkTitle = 'This is a test link!';
|
||||
const linkHref = 'https://joplinapp.org/';
|
||||
|
||||
await mainWindow.evaluate(({ testLinkTitle, linkHref }) => {
|
||||
const testLink = document.createElement('a');
|
||||
testLink.textContent = testLinkTitle;
|
||||
testLink.onclick = () => {
|
||||
// We need to navigate by setting location.href -- clicking on a link
|
||||
// directly within the main window (i.e. not in a PDF viewer) doesn't
|
||||
// navigate.
|
||||
location.href = linkHref;
|
||||
};
|
||||
testLink.href = '#';
|
||||
|
||||
// Display on top of everything
|
||||
testLink.style.zIndex = '99999';
|
||||
testLink.style.position = 'fixed';
|
||||
testLink.style.top = '0';
|
||||
testLink.style.left = '0';
|
||||
|
||||
document.body.appendChild(testLink);
|
||||
}, { testLinkTitle, linkHref });
|
||||
|
||||
const testLink = mainWindow.getByText(testLinkTitle);
|
||||
await expect(testLink).toBeVisible();
|
||||
await testLink.click({ noWaitAfter: true });
|
||||
|
||||
expect(await nextExternalUrlPromise).toBe(linkHref);
|
||||
});
|
||||
});
|
||||
|
@@ -31,6 +31,12 @@ const React = require('react');
|
||||
const nodeSqlite = require('sqlite3');
|
||||
const initLib = require('@joplin/lib/initLib').default;
|
||||
|
||||
// Security: If we attempt to navigate away from the root HTML page, it's likely because
|
||||
// of an improperly sanitized link. Prevent this by closing the window before we can
|
||||
// navigate away.
|
||||
window.onbeforeunload = () => {
|
||||
window.close();
|
||||
};
|
||||
|
||||
if (bridge().env() === 'dev') {
|
||||
const newConsole = function(oldConsole) {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/app-desktop",
|
||||
"version": "2.13.3",
|
||||
"version": "2.13.2",
|
||||
"description": "Joplin for Desktop",
|
||||
"main": "main.js",
|
||||
"private": true,
|
||||
@@ -121,7 +121,7 @@
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@types/jest": "29.5.4",
|
||||
"@types/node": "18.17.19",
|
||||
"@types/react": "18.2.31",
|
||||
"@types/react": "18.2.23",
|
||||
"@types/react-redux": "7.1.27",
|
||||
"@types/styled-components": "5.1.28",
|
||||
"electron": "25.9.0",
|
||||
@@ -131,7 +131,7 @@
|
||||
"jest": "29.6.4",
|
||||
"jest-environment-jsdom": "29.6.4",
|
||||
"js-sha512": "0.8.0",
|
||||
"nan": "2.18.0",
|
||||
"nan": "2.17.0",
|
||||
"react-test-renderer": "18.2.0",
|
||||
"typescript": "5.1.6"
|
||||
},
|
||||
@@ -175,7 +175,7 @@
|
||||
"react-datetime": "3.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-redux": "8.1.3",
|
||||
"react-select": "5.7.7",
|
||||
"react-select": "5.7.5",
|
||||
"react-toggle-button": "2.2.0",
|
||||
"react-tooltip": "4.5.1",
|
||||
"redux": "4.2.1",
|
||||
|
@@ -12,9 +12,8 @@ export default function stateToWhenClauseContext(state: AppState, options: WhenC
|
||||
...libStateToWhenClauseContext(state, options),
|
||||
|
||||
// UI elements
|
||||
markdownEditorVisible: !!state.settings['editor.codeView'] && !state.settings['isSafeMode'],
|
||||
richTextEditorVisible: !state.settings['editor.codeView'] && !state.settings['isSafeMode'],
|
||||
|
||||
markdownEditorVisible: !!state.settings['editor.codeView'],
|
||||
richTextEditorVisible: !state.settings['editor.codeView'],
|
||||
markdownEditorPaneVisible: state.settings['editor.codeView'] && state.noteVisiblePanes.includes('editor'),
|
||||
markdownViewerPaneVisible: state.settings['editor.codeView'] && state.noteVisiblePanes.includes('viewer'),
|
||||
modalDialogVisible: !!Object.keys(state.visibleDialogs).length,
|
||||
|
@@ -110,8 +110,8 @@ android {
|
||||
applicationId "net.cozic.joplin"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 2097723
|
||||
versionName "2.13.3"
|
||||
versionCode 2097722
|
||||
versionName "2.13.2"
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
||||
}
|
||||
|
@@ -517,13 +517,13 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Joplin/Joplin.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 98;
|
||||
CURRENT_PROJECT_VERSION = 97;
|
||||
DEVELOPMENT_TEAM = A9BXAFS6CT;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Joplin/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 12.13.1;
|
||||
MARKETING_VERSION = 12.13.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -546,12 +546,12 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Joplin/Joplin.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 98;
|
||||
CURRENT_PROJECT_VERSION = 97;
|
||||
DEVELOPMENT_TEAM = A9BXAFS6CT;
|
||||
INFOPLIST_FILE = Joplin/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 12.13.1;
|
||||
MARKETING_VERSION = 12.13.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -698,14 +698,14 @@
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 98;
|
||||
CURRENT_PROJECT_VERSION = 97;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = A9BXAFS6CT;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||
MARKETING_VERSION = 12.13.1;
|
||||
MARKETING_VERSION = 12.13.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.cozic.joplin.ShareExtension;
|
||||
@@ -729,14 +729,14 @@
|
||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 98;
|
||||
CURRENT_PROJECT_VERSION = 97;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = A9BXAFS6CT;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||
MARKETING_VERSION = 12.13.1;
|
||||
MARKETING_VERSION = 12.13.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.cozic.joplin.ShareExtension;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@@ -49,7 +49,7 @@
|
||||
"react-native-device-info": "10.9.0",
|
||||
"react-native-dialogbox": "0.6.10",
|
||||
"react-native-document-picker": "9.0.1",
|
||||
"react-native-dropdownalert": "5.1.0",
|
||||
"react-native-dropdownalert": "4.5.1",
|
||||
"react-native-exit-app": "2.0.0",
|
||||
"react-native-file-viewer": "2.1.5",
|
||||
"react-native-fingerprint-scanner": "6.0.0",
|
||||
@@ -95,7 +95,7 @@
|
||||
"@tsconfig/react-native": "2.0.2",
|
||||
"@types/fs-extra": "11.0.2",
|
||||
"@types/jest": "29.5.4",
|
||||
"@types/react": "18.2.31",
|
||||
"@types/react": "18.2.23",
|
||||
"@types/react-native": "0.70.6",
|
||||
"@types/react-redux": "7.1.27",
|
||||
"@types/tar-stream": "2.2.3",
|
||||
|
@@ -1,5 +1,5 @@
|
||||
module.exports = {
|
||||
hash:"7d3976ee03fc0f6880dd54c78d1a325b", files: {
|
||||
hash:"3b957b10f317a91aaea146b3445709dd", files: {
|
||||
'highlight.js/atom-one-dark-reasonable.css': { data: require('./highlight.js/atom-one-dark-reasonable.css.base64.js'), mime: 'text/css', encoding: 'base64' },
|
||||
'highlight.js/atom-one-light.css': { data: require('./highlight.js/atom-one-light.css.base64.js'), mime: 'text/css', encoding: 'base64' },
|
||||
'katex/fonts/KaTeX_AMS-Regular.woff2': { data: require('./katex/fonts/KaTeX_AMS-Regular.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
|
||||
|
File diff suppressed because one or more lines are too long
@@ -97,7 +97,7 @@ SyncTargetRegistry.addClass(SyncTargetAmazonS3);
|
||||
SyncTargetRegistry.addClass(SyncTargetJoplinServer);
|
||||
SyncTargetRegistry.addClass(SyncTargetJoplinCloud);
|
||||
|
||||
import FsDriverRN from './utils/fs-driver/fs-driver-rn';
|
||||
import FsDriverRN from './utils/fs-driver-rn';
|
||||
import DecryptionWorker from '@joplin/lib/services/DecryptionWorker';
|
||||
import EncryptionService from '@joplin/lib/services/e2ee/EncryptionService';
|
||||
import MigrationService from '@joplin/lib/services/MigrationService';
|
||||
@@ -109,7 +109,7 @@ import { loadMasterKeysFromSettings, migrateMasterPassword } from '@joplin/lib/s
|
||||
import SyncTargetNone from '@joplin/lib/SyncTargetNone';
|
||||
import { setRSA } from '@joplin/lib/services/e2ee/ppk';
|
||||
import RSA from './services/e2ee/RSA.react-native';
|
||||
import { runIntegrationTests as runRsaIntegrationTests } from '@joplin/lib/services/e2ee/ppkTestUtils';
|
||||
import { runIntegrationTests } from '@joplin/lib/services/e2ee/ppkTestUtils';
|
||||
import { Theme, ThemeAppearance } from '@joplin/lib/themes/type';
|
||||
import { AppState } from './utils/types';
|
||||
import ProfileSwitcher from './components/ProfileSwitcher/ProfileSwitcher';
|
||||
@@ -121,7 +121,6 @@ import userFetcher, { initializeUserFetcher } from '@joplin/lib/utils/userFetche
|
||||
import { ReactNode } from 'react';
|
||||
import { parseShareCache } from '@joplin/lib/services/share/reducer';
|
||||
import autodetectTheme, { onSystemColorSchemeChange } from './utils/autodetectTheme';
|
||||
import runOnDeviceFsDriverTests from './utils/fs-driver/runOnDeviceTests';
|
||||
|
||||
type SideMenuPosition = 'left' | 'right';
|
||||
|
||||
@@ -750,10 +749,7 @@ async function initialize(dispatch: Function) {
|
||||
// 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 runRsaIntegrationTests();
|
||||
await runOnDeviceFsDriverTests();
|
||||
}
|
||||
if (Setting.value('env') === 'dev') await runIntegrationTests();
|
||||
|
||||
reg.logger().info('Application initialized');
|
||||
}
|
||||
@@ -763,7 +759,6 @@ class AppComponent extends React.Component {
|
||||
private urlOpenListener_: EmitterSubscription|null = null;
|
||||
private appStateChangeListener_: NativeEventSubscription|null = null;
|
||||
private themeChangeListener_: NativeEventSubscription|null = null;
|
||||
private dropdownAlert_ = (_data: any) => new Promise<any>(res => res);
|
||||
|
||||
public constructor() {
|
||||
super();
|
||||
@@ -896,11 +891,7 @@ class AppComponent extends React.Component {
|
||||
AlarmService.setInAppNotificationHandler(async (alarmId: string) => {
|
||||
const alarm = await Alarm.load(alarmId);
|
||||
const notification = await Alarm.makeNotification(alarm);
|
||||
void this.dropdownAlert_({
|
||||
type: 'info',
|
||||
title: notification.title,
|
||||
message: notification.body ? notification.body : '',
|
||||
});
|
||||
this.dropdownAlert_.alertWithType('info', notification.title, notification.body ? notification.body : '');
|
||||
});
|
||||
|
||||
this.appStateChangeListener_ = RNAppState.addEventListener('change', this.onAppStateChange_);
|
||||
@@ -1091,7 +1082,8 @@ class AppComponent extends React.Component {
|
||||
<View style={{ flex: 1, backgroundColor: theme.backgroundColor }}>
|
||||
{ shouldShowMainContent && <AppNav screens={appNavInit} dispatch={this.props.dispatch} /> }
|
||||
</View>
|
||||
<DropdownAlert alert={(func: any) => (this.dropdownAlert_ = func)} /> { !shouldShowMainContent && <BiometricPopup
|
||||
<DropdownAlert ref={(ref: any) => this.dropdownAlert_ = ref} tapToCloseEnabled={true} />
|
||||
{ !shouldShowMainContent && <BiometricPopup
|
||||
dispatch={this.props.dispatch}
|
||||
themeId={this.props.themeId}
|
||||
sensorInfo={this.state.sensorInfo}
|
||||
|
@@ -3,7 +3,7 @@ const RNFetchBlob = require('rn-fetch-blob').default;
|
||||
import * as RNFS from 'react-native-fs';
|
||||
const DocumentPicker = require('react-native-document-picker').default;
|
||||
import { openDocument } from '@joplin/react-native-saf-x';
|
||||
import RNSAF, { DocumentFileDetail, openDocumentTree } from '@joplin/react-native-saf-x';
|
||||
import RNSAF, { Encoding, DocumentFileDetail, openDocumentTree } from '@joplin/react-native-saf-x';
|
||||
import { Platform } from 'react-native';
|
||||
import * as tar from 'tar-stream';
|
||||
import { resolve } from 'path';
|
||||
@@ -18,63 +18,24 @@ function isScopedUri(path: string) {
|
||||
return path.includes(ANDROID_URI_PREFIX);
|
||||
}
|
||||
|
||||
// Encodings supported by rn-fetch-blob, RNSAF, and
|
||||
// RNFS.
|
||||
// See also
|
||||
// - https://github.com/itinance/react-native-fs#readfilefilepath-string-encoding-string-promisestring
|
||||
// - https://github.com/joltup/rn-fetch-blob/blob/cf9e8843599de92031df2660d5a1da18491fa3c0/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java#L1049
|
||||
export enum SupportedEncoding {
|
||||
Utf8 = 'utf8',
|
||||
Ascii = 'ascii',
|
||||
Base64 = 'base64',
|
||||
}
|
||||
const supportedEncodings = Object.values<string>(SupportedEncoding);
|
||||
|
||||
// Converts some encodings specifiers that work with NodeJS into encodings
|
||||
// that work with RNSAF, RNFetchBlob.fs, and RNFS.
|
||||
//
|
||||
// Throws if an encoding can't be normalized.
|
||||
const normalizeEncoding = (encoding: string): SupportedEncoding => {
|
||||
encoding = encoding.toLowerCase();
|
||||
|
||||
// rn-fetch-blob and RNSAF require the exact string "utf8", but NodeJS (and thus
|
||||
// fs-driver-node) support variants on this like "UtF-8" and "utf-8". Convert them:
|
||||
if (encoding === 'utf-8') {
|
||||
encoding = 'utf8';
|
||||
}
|
||||
|
||||
if (!supportedEncodings.includes(encoding)) {
|
||||
throw new Error(`Unsupported encoding: ${encoding}.`);
|
||||
}
|
||||
|
||||
return encoding as SupportedEncoding;
|
||||
};
|
||||
|
||||
export default class FsDriverRN extends FsDriverBase {
|
||||
public appendFileSync() {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
// Requires that the file already exists.
|
||||
// TODO: Update for compatibility with fs-driver-node's appendFile (which does not
|
||||
// require that the file exists).
|
||||
public appendFile(path: string, content: any, rawEncoding = 'base64') {
|
||||
const encoding = normalizeEncoding(rawEncoding);
|
||||
|
||||
// Encoding can be either "utf8" or "base64"
|
||||
public appendFile(path: string, content: any, encoding = 'base64') {
|
||||
if (isScopedUri(path)) {
|
||||
return RNSAF.writeFile(path, content, { encoding, append: true });
|
||||
return RNSAF.writeFile(path, content, { encoding: encoding as Encoding, append: true });
|
||||
}
|
||||
return RNFS.appendFile(path, content, encoding);
|
||||
}
|
||||
|
||||
// Encoding can be either "utf8", "utf-8", or "base64"
|
||||
public writeFile(path: string, content: any, rawEncoding = 'base64') {
|
||||
const encoding = normalizeEncoding(rawEncoding);
|
||||
|
||||
// Encoding can be either "utf8" or "base64"
|
||||
public writeFile(path: string, content: any, encoding = 'base64') {
|
||||
if (isScopedUri(path)) {
|
||||
return RNSAF.writeFile(path, content, { encoding: encoding });
|
||||
return RNSAF.writeFile(path, content, { encoding: encoding as Encoding });
|
||||
}
|
||||
|
||||
// We need to use rn-fetch-blob here due to this bug:
|
||||
// https://github.com/itinance/react-native-fs/issues/700
|
||||
return RNFetchBlob.fs.writeFile(path, content, encoding);
|
||||
@@ -234,11 +195,10 @@ export default class FsDriverRN extends FsDriverBase {
|
||||
return null;
|
||||
}
|
||||
|
||||
public readFile(path: string, rawEncoding = 'utf8') {
|
||||
const encoding = normalizeEncoding(rawEncoding);
|
||||
|
||||
public readFile(path: string, encoding = 'utf8') {
|
||||
if (encoding === 'Buffer') throw new Error('Raw buffer output not supported for FsDriverRN.readFile');
|
||||
if (isScopedUri(path)) {
|
||||
return RNSAF.readFile(path, { encoding: encoding });
|
||||
return RNSAF.readFile(path, { encoding: encoding as Encoding });
|
||||
}
|
||||
return RNFS.readFile(path, encoding);
|
||||
}
|
||||
@@ -284,9 +244,7 @@ export default class FsDriverRN extends FsDriverBase {
|
||||
}
|
||||
}
|
||||
|
||||
public async readFileChunk(handle: any, length: number, rawEncoding = 'base64') {
|
||||
const encoding = normalizeEncoding(rawEncoding);
|
||||
|
||||
public async readFileChunk(handle: any, length: number, encoding = 'base64') {
|
||||
if (handle.offset + length > handle.stat.size) {
|
||||
length = handle.stat.size - handle.offset;
|
||||
}
|
@@ -1,249 +0,0 @@
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import shim from '@joplin/lib/shim';
|
||||
import uuid from '@joplin/lib/uuid';
|
||||
import { join } from 'path';
|
||||
import FsDriverBase from '@joplin/lib/fs-driver-base';
|
||||
import Logger from '@joplin/utils/Logger';
|
||||
import { Buffer } from 'buffer';
|
||||
|
||||
const logger = Logger.create('fs-driver-tests');
|
||||
|
||||
const expectToBe = async <T> (actual: T, expected: T) => {
|
||||
if (actual !== expected) {
|
||||
throw new Error(`Integration test failure: ${actual} was expected to be ${expected}`);
|
||||
}
|
||||
};
|
||||
|
||||
const testExpect = async () => {
|
||||
// Verify that expect is working
|
||||
await expectToBe(1, 1);
|
||||
await expectToBe(true, true);
|
||||
|
||||
let failed = false;
|
||||
try {
|
||||
await expectToBe('a', 'test');
|
||||
failed = true;
|
||||
} catch (_error) {
|
||||
failed = false;
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
throw new Error('expectToBe should throw when given non-equal inputs');
|
||||
}
|
||||
};
|
||||
|
||||
const testAppendFile = async (tempDir: string) => {
|
||||
logger.info('Testing fsDriver.appendFile...');
|
||||
|
||||
const targetFile = join(tempDir, uuid.createNano());
|
||||
|
||||
const fsDriver: FsDriverBase = shim.fsDriver();
|
||||
|
||||
// For fs-driver-rn's appendFile to work, we first need to create the file.
|
||||
// TODO: This is different from the requirements of fs-driver-node.
|
||||
await fsDriver.writeFile(targetFile, '');
|
||||
|
||||
const firstChunk = 'A 𝓊𝓃𝒾𝒸𝓸𝒹𝓮 test\n...';
|
||||
await fsDriver.appendFile(targetFile, firstChunk, 'utf-8');
|
||||
await expectToBe(await fsDriver.readFile(targetFile), firstChunk);
|
||||
|
||||
const secondChunk = '▪️ More unicode ▪️';
|
||||
await fsDriver.appendFile(targetFile, secondChunk, 'utf8');
|
||||
await expectToBe(await fsDriver.readFile(targetFile), firstChunk + secondChunk);
|
||||
|
||||
const thirdChunk = 'ASCII';
|
||||
await fsDriver.appendFile(targetFile, thirdChunk, 'ascii');
|
||||
await expectToBe(await fsDriver.readFile(targetFile), firstChunk + secondChunk + thirdChunk);
|
||||
|
||||
const lastChunk = 'Test...';
|
||||
await fsDriver.appendFile(
|
||||
targetFile, Buffer.from(lastChunk, 'utf8').toString('base64'), 'base64',
|
||||
);
|
||||
await expectToBe(
|
||||
await fsDriver.readFile(targetFile), firstChunk + secondChunk + thirdChunk + lastChunk,
|
||||
);
|
||||
|
||||
// Should throw if given an invalid encoding
|
||||
let didThrow = false;
|
||||
try {
|
||||
await fsDriver.appendFile(targetFile, 'test', 'bad-encoding');
|
||||
} catch (_error) {
|
||||
didThrow = true;
|
||||
}
|
||||
await expectToBe(didThrow, true);
|
||||
};
|
||||
|
||||
const testReadWriteFileUtf8 = async (tempDir: string) => {
|
||||
logger.info('Testing fsDriver.writeFile and fsDriver.readFile with utf-8...');
|
||||
|
||||
const filePath = join(tempDir, uuid.createNano());
|
||||
|
||||
const testStrings = [
|
||||
// ASCII
|
||||
'test',
|
||||
|
||||
// Special characters
|
||||
'𝐴 𝒕𝐞𝑺𝒕',
|
||||
|
||||
// Emojis
|
||||
'✅ Test. 🕳️',
|
||||
];
|
||||
|
||||
const testEncodings = ['utf-8', 'utf8', 'UtF-8'];
|
||||
|
||||
// Use the same file for all tests to test overwriting
|
||||
for (const encoding of testEncodings) {
|
||||
for (const testString of testStrings) {
|
||||
const fsDriver: FsDriverBase = shim.fsDriver();
|
||||
await fsDriver.writeFile(filePath, testString, encoding);
|
||||
|
||||
const fileData = await fsDriver.readFile(filePath, encoding);
|
||||
await expectToBe(fileData, testString);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const testReadFileChunkUtf8 = async (tempDir: string) => {
|
||||
logger.info('Testing fsDriver.readFileChunk...');
|
||||
|
||||
const filePath = join(tempDir, `${uuid.createNano()}.txt`);
|
||||
|
||||
const fsDriver: FsDriverBase = shim.fsDriver();
|
||||
|
||||
// 🕳️ is 7 bytes when utf-8 encoded
|
||||
// à,á,â, and ã are each 2 bytes
|
||||
const expectedFileContent = '01234567\nàáâã\n🕳️🕳️🕳️\ntēst...';
|
||||
await fsDriver.writeFile(filePath, expectedFileContent, 'utf8');
|
||||
|
||||
const testEncodings = ['utf-8', 'utf8', 'UtF-8'];
|
||||
|
||||
for (const encoding of testEncodings) {
|
||||
const handle = await fsDriver.open(filePath, 'r');
|
||||
|
||||
await expectToBe(
|
||||
await fsDriver.readFileChunk(handle, 8, encoding), '01234567',
|
||||
);
|
||||
|
||||
await expectToBe(
|
||||
await fsDriver.readFileChunk(handle, 1, encoding), '\n',
|
||||
);
|
||||
|
||||
await expectToBe(
|
||||
await fsDriver.readFileChunk(handle, 8, encoding), 'àáâã',
|
||||
);
|
||||
|
||||
await expectToBe(
|
||||
await fsDriver.readFileChunk(handle, 8, encoding), '\n🕳️',
|
||||
);
|
||||
|
||||
await expectToBe(
|
||||
await fsDriver.readFileChunk(handle, 15, encoding), '🕳️🕳️\n',
|
||||
);
|
||||
|
||||
// A 0 length should return null and not advance
|
||||
await expectToBe(
|
||||
await fsDriver.readFileChunk(handle, 0, encoding), null,
|
||||
);
|
||||
|
||||
// Reading a different encoding (then switching back to the original)
|
||||
// should be supported
|
||||
await expectToBe(
|
||||
await fsDriver.readFileChunk(handle, 3, 'base64'),
|
||||
Buffer.from('tē', 'utf-8').toString('base64'),
|
||||
);
|
||||
|
||||
await expectToBe(
|
||||
await fsDriver.readFileChunk(handle, 100, encoding), 'st...',
|
||||
);
|
||||
|
||||
// Should not be able to read past the end
|
||||
await expectToBe(
|
||||
await fsDriver.readFileChunk(handle, 10, encoding), null,
|
||||
);
|
||||
|
||||
await expectToBe(
|
||||
await fsDriver.readFileChunk(handle, 1, encoding), null,
|
||||
);
|
||||
|
||||
await fsDriver.close(filePath);
|
||||
}
|
||||
};
|
||||
|
||||
const testTarCreate = async (tempDir: string) => {
|
||||
logger.info('Testing fsDriver.tarCreate...');
|
||||
|
||||
const directoryToPack = join(tempDir, uuid.createNano());
|
||||
|
||||
const fsDriver: FsDriverBase = shim.fsDriver();
|
||||
|
||||
// Add test files to the directory
|
||||
const fileContents: Record<string, string> = {};
|
||||
|
||||
// small utf-8 encoded files
|
||||
for (let i = 0; i < 10; i ++) {
|
||||
const testFilePath = join(directoryToPack, uuid.createNano());
|
||||
|
||||
const fileContent = `✅ Testing... ä ✅ File #${i}`;
|
||||
await fsDriver.writeFile(testFilePath, fileContent, 'utf-8');
|
||||
|
||||
fileContents[testFilePath] = fileContent;
|
||||
}
|
||||
|
||||
// larger utf-8 encoded files
|
||||
for (let i = 0; i < 3; i ++) {
|
||||
const testFilePath = join(directoryToPack, uuid.createNano());
|
||||
|
||||
let fileContent = `✅ Testing... ä ✅ File #${i}`;
|
||||
|
||||
for (let j = 0; j < 8; j ++) {
|
||||
fileContent += fileContent;
|
||||
}
|
||||
|
||||
await fsDriver.writeFile(testFilePath, fileContent, 'utf-8');
|
||||
|
||||
fileContents[testFilePath] = fileContent;
|
||||
}
|
||||
|
||||
// Pack the files
|
||||
const pathsToTar = Object.keys(fileContents);
|
||||
const tarOutputPath = join(tempDir, 'test-tar.tar');
|
||||
await fsDriver.tarCreate({
|
||||
cwd: tempDir,
|
||||
file: tarOutputPath,
|
||||
}, pathsToTar);
|
||||
|
||||
// Read the tar file as utf-8 and search for the written file contents
|
||||
// (which should work).
|
||||
const rawTarData: string = await fsDriver.readFile(tarOutputPath, 'utf8');
|
||||
|
||||
for (const fileContent of Object.values(fileContents)) {
|
||||
await expectToBe(rawTarData.includes(fileContent), true);
|
||||
}
|
||||
};
|
||||
|
||||
// In the past, some fs-driver functionality has worked correctly on some devices and not others.
|
||||
// As such, we need to be able to run some tests on-device.
|
||||
const runOnDeviceTests = async () => {
|
||||
const tempDir = join(Setting.value('tempDir'), uuid.createNano());
|
||||
|
||||
if (await shim.fsDriver().exists(tempDir)) {
|
||||
await shim.fsDriver().remove(tempDir);
|
||||
}
|
||||
|
||||
try {
|
||||
await testExpect();
|
||||
await testAppendFile(tempDir);
|
||||
await testReadWriteFileUtf8(tempDir);
|
||||
await testReadFileChunkUtf8(tempDir);
|
||||
await testTarCreate(tempDir);
|
||||
} catch (error) {
|
||||
const errorMessage = `On-device testing failed with an exception: ${error}.`;
|
||||
|
||||
logger.error(errorMessage, error);
|
||||
alert(errorMessage);
|
||||
} finally {
|
||||
await shim.fsDriver().remove(tempDir);
|
||||
}
|
||||
};
|
||||
|
||||
export default runOnDeviceTests;
|
@@ -3,7 +3,7 @@ const { GeolocationReact } = require('./geolocation-react.js');
|
||||
const PoorManIntervals = require('@joplin/lib/PoorManIntervals').default;
|
||||
const RNFetchBlob = require('rn-fetch-blob').default;
|
||||
const { generateSecureRandom } = require('react-native-securerandom');
|
||||
const FsDriverRN = require('./fs-driver/fs-driver-rn').default;
|
||||
const FsDriverRN = require('./fs-driver-rn').default;
|
||||
const { Buffer } = require('buffer');
|
||||
const { Linking, Platform } = require('react-native');
|
||||
const mimeUtils = require('@joplin/lib/mime-utils.js').mime;
|
||||
|
@@ -20,7 +20,6 @@ import { SearchState, EditorProps, EditorSettings } from '../types';
|
||||
import { EditorEventType, SelectionRangeChangeEvent } from '../events';
|
||||
import {
|
||||
decreaseIndent, increaseIndent,
|
||||
insertOrIncreaseIndent,
|
||||
toggleBolded, toggleCode,
|
||||
toggleItalicized, toggleMath,
|
||||
} from './markdown/markdownCommands';
|
||||
@@ -255,7 +254,7 @@ const createEditor = (
|
||||
notifyLinkEditRequest();
|
||||
return true;
|
||||
}),
|
||||
keyCommand('Tab', insertOrIncreaseIndent, true),
|
||||
keyCommand('Tab', increaseIndent, true),
|
||||
keyCommand('Shift-Tab', decreaseIndent, true),
|
||||
|
||||
...standardKeymap, ...historyKeymap, ...searchKeymap,
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import { EditorSelection } from '@codemirror/state';
|
||||
import {
|
||||
insertOrIncreaseIndent,
|
||||
toggleBolded, toggleCode, toggleHeaderLevel, toggleItalicized, toggleMath, updateLink,
|
||||
} from './markdownCommands';
|
||||
import createTestEditor from '../testUtil/createTestEditor';
|
||||
@@ -239,41 +238,5 @@ describe('markdownCommands', () => {
|
||||
expect(sel.from).toBe('> Testing...> \n> \n'.length);
|
||||
expect(sel.to).toBe(editor.state.doc.length);
|
||||
});
|
||||
|
||||
it('insertOrIncreaseIndent should indent when text is selected', async () => {
|
||||
const initialText = '> Testing...\n> Test.';
|
||||
const editor = await createTestEditor(
|
||||
initialText,
|
||||
EditorSelection.range(0, initialText.length),
|
||||
['Blockquote'],
|
||||
);
|
||||
|
||||
insertOrIncreaseIndent(editor);
|
||||
|
||||
expect(editor.state.doc.toString()).toBe('> \tTesting...\n> \tTest.');
|
||||
});
|
||||
|
||||
it('insertOrIncreaseIndent should insert tabs when selection is empty, in a paragraph', async () => {
|
||||
const initialText = 'This is a test\nof indentation.';
|
||||
const editor = await createTestEditor(
|
||||
initialText,
|
||||
EditorSelection.cursor(initialText.length),
|
||||
[],
|
||||
);
|
||||
|
||||
insertOrIncreaseIndent(editor);
|
||||
|
||||
const finalText = editor.state.doc.toString();
|
||||
|
||||
// Should add tab character at the cursor
|
||||
expect(finalText).toBe('This is a test\nof indentation.\t');
|
||||
|
||||
// Should move the selection after the tab
|
||||
expect(editor.state.selection.ranges).toHaveLength(1);
|
||||
expect(editor.state.selection.main).toMatchObject({
|
||||
from: finalText.length,
|
||||
to: finalText.length,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@@ -63,35 +63,6 @@ describe('markdownCommands.toggleList', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should not toggle a the full list when the cursor is on a blank line', async () => {
|
||||
const checklistStartText = [
|
||||
'# Test',
|
||||
'',
|
||||
'- [ ] This',
|
||||
'- [ ] is',
|
||||
'',
|
||||
].join('\n');
|
||||
|
||||
const checklistEndText = [
|
||||
'- [ ] a',
|
||||
'- [ ] test',
|
||||
].join('\n');
|
||||
|
||||
const editor = await createTestEditor(
|
||||
`${checklistStartText}\n${checklistEndText}`,
|
||||
|
||||
// Place the cursor on the blank line between the checklist
|
||||
// regions
|
||||
EditorSelection.cursor(unorderedListText.length + 1),
|
||||
['BulletList', 'ATXHeading1'],
|
||||
);
|
||||
|
||||
// Should create a checkbox on the blank line
|
||||
toggleList(ListType.CheckList)(editor);
|
||||
expect(editor.state.doc.toString()).toBe(
|
||||
`${checklistStartText}- [ ] \n${checklistEndText}`,
|
||||
);
|
||||
});
|
||||
|
||||
// it('should correctly replace an unordered list with a checklist', async () => {
|
||||
// const editor = await createEditor(
|
||||
|
@@ -12,7 +12,6 @@ import {
|
||||
toggleInlineFormatGlobally, toggleRegionFormatGlobally, toggleSelectedLinesStartWith,
|
||||
isIndentationEquivalent, stripBlockquote, tabsToSpaces,
|
||||
} from './markdownReformatter';
|
||||
import intersectsSyntaxNode from '../util/isInSyntaxNode';
|
||||
|
||||
const startingSpaceRegex = /^(\s*)/;
|
||||
|
||||
@@ -184,11 +183,8 @@ export const toggleList = (listType: ListType): Command => {
|
||||
const origFirstLineIndentation = firstLineIndentation;
|
||||
const origContainerType = containerType;
|
||||
|
||||
// Grow `sel` to the smallest containing list, unless the
|
||||
// cursor is on an empty line, in which case, the user
|
||||
// probably wants to add a list item (and not select the entire
|
||||
// list).
|
||||
if (sel.empty && fromLine.text.trim() !== '') {
|
||||
// Grow [sel] to the smallest containing list
|
||||
if (sel.empty) {
|
||||
sel = growSelectionToNode(state, sel, [orderedListTag, unorderedListTag]);
|
||||
computeSelectionProps();
|
||||
}
|
||||
@@ -424,35 +420,6 @@ export const increaseIndent: Command = (view: EditorView): boolean => {
|
||||
return true;
|
||||
};
|
||||
|
||||
// Like `increaseIndent`, but may insert tabs, rather than
|
||||
// indenting, in some instances.
|
||||
export const insertOrIncreaseIndent: Command = (view: EditorView): boolean => {
|
||||
const selection = view.state.selection;
|
||||
const mainSelection = selection.main;
|
||||
if (selection.ranges.length !== 1 || !mainSelection.empty) {
|
||||
return increaseIndent(view);
|
||||
}
|
||||
|
||||
|
||||
if (intersectsSyntaxNode(view.state, mainSelection, 'ListItem')) {
|
||||
return increaseIndent(view);
|
||||
}
|
||||
|
||||
const indentUnit = indentString(view.state, getIndentUnit(view.state));
|
||||
view.dispatch(view.state.changeByRange(selection => {
|
||||
return {
|
||||
// Move the selection to after the inserted text
|
||||
range: EditorSelection.cursor(selection.from + indentUnit.length),
|
||||
changes: {
|
||||
from: selection.from,
|
||||
insert: indentUnit,
|
||||
},
|
||||
};
|
||||
}));
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
export const decreaseIndent: Command = (view: EditorView): boolean => {
|
||||
const matchEmpty = true;
|
||||
const changes = toggleSelectedLinesStartWith(
|
||||
|
@@ -1,32 +0,0 @@
|
||||
import { syntaxTree } from '@codemirror/language';
|
||||
import { EditorState } from '@codemirror/state';
|
||||
|
||||
interface Range {
|
||||
from: number;
|
||||
to: number;
|
||||
}
|
||||
|
||||
const intersectsSyntaxNode = (state: EditorState, range: Range, nodeName: string) => {
|
||||
let foundNode = false;
|
||||
|
||||
syntaxTree(state).iterate({
|
||||
from: range.from,
|
||||
to: range.to,
|
||||
enter: node => {
|
||||
if (node.name === nodeName) {
|
||||
foundNode = true;
|
||||
|
||||
// Skip children
|
||||
return false;
|
||||
}
|
||||
|
||||
// Search children if we haven't found a matching node yet.
|
||||
return !foundNode;
|
||||
},
|
||||
});
|
||||
|
||||
return foundNode;
|
||||
};
|
||||
|
||||
export default intersectsSyntaxNode;
|
||||
|
@@ -18,7 +18,7 @@
|
||||
"@joplin/lib": "~2.13",
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@types/jest": "29.5.4",
|
||||
"@types/react": "18.2.31",
|
||||
"@types/react": "18.2.23",
|
||||
"@types/react-redux": "7.1.27",
|
||||
"@types/styled-components": "5.1.28",
|
||||
"jest": "29.6.3",
|
||||
|
@@ -25,10 +25,6 @@ export default class FsDriverBase {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
public async appendFile(_path: string, _content: string, _encoding = 'base64'): Promise<any> {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
public async copy(_source: string, _dest: string) {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
@@ -1,25 +0,0 @@
|
||||
import { PluginSettings } from './services/plugins/PluginService';
|
||||
import type PluginService from './services/plugins/PluginService';
|
||||
import versionInfo from './versionInfo';
|
||||
|
||||
const renderErrorBlock = (errors: any[]): string => {
|
||||
if (!errors.length) return '';
|
||||
return `\`\`\`\n${errors.map(e => typeof e === 'string' ? e.trim() : e.message.trim())}\n\`\`\``;
|
||||
};
|
||||
|
||||
export default (title: string, body: string, errors: any[], packageInfo: any, pluginService: PluginService, pluginSettings: PluginSettings) => {
|
||||
const v = versionInfo(packageInfo, pluginService.enabledPlugins(pluginSettings));
|
||||
|
||||
const errorBlock = renderErrorBlock(errors);
|
||||
|
||||
const query: Record<string, string> = {
|
||||
title,
|
||||
body: `# About\n\n${v.body.trim()}\n\n# Body\n\n${body}${errorBlock ? `\n\n# Errors\n\n${errorBlock}` : ''}`,
|
||||
category: 'support',
|
||||
};
|
||||
|
||||
const queryString = Object.keys(query).map(k => `${k}=${encodeURIComponent(query[k])}`).join('&');
|
||||
|
||||
const url = `https://discourse.joplinapp.org/new-topic?${queryString}`;
|
||||
return url;
|
||||
};
|
@@ -303,7 +303,6 @@ class Setting extends BaseModel {
|
||||
};
|
||||
|
||||
public static autoSaveEnabled = true;
|
||||
public static allowFileStorage = true;
|
||||
|
||||
private static metadata_: SettingItems = null;
|
||||
private static keychainService_: any = null;
|
||||
@@ -2056,7 +2055,7 @@ class Setting extends BaseModel {
|
||||
}
|
||||
|
||||
private static canUseFileStorage(): boolean {
|
||||
return this.allowFileStorage && !shim.mobilePlatform();
|
||||
return !shim.mobilePlatform();
|
||||
}
|
||||
|
||||
private static keyStorage(key: string): SettingStorage {
|
||||
|
@@ -21,7 +21,7 @@
|
||||
"@types/js-yaml": "4.0.6",
|
||||
"@types/node": "18.17.19",
|
||||
"@types/node-rsa": "1.1.2",
|
||||
"@types/react": "18.2.31",
|
||||
"@types/react": "18.2.23",
|
||||
"@types/uuid": "9.0.4",
|
||||
"clean-html": "1.5.0",
|
||||
"jest": "29.6.4",
|
||||
|
@@ -142,12 +142,12 @@ const features = (): Record<FeatureId, PlanFeature> => {
|
||||
basic: true,
|
||||
pro: true,
|
||||
teams: true,
|
||||
basicInfo: _('%d GB storage space', 2),
|
||||
proInfo: _('%d GB storage space', 30),
|
||||
teamsInfo: _('%d GB storage space', 50),
|
||||
basicInfoShort: _('%d GB', 2),
|
||||
proInfoShort: _('%d GB', 30),
|
||||
teamsInfoShort: _('%d GB', 50),
|
||||
basicInfo: _('%d GB storage space', 1),
|
||||
proInfo: _('%d GB storage space', 10),
|
||||
teamsInfo: _('%d GB storage space', 10),
|
||||
basicInfoShort: _('%d GB', 1),
|
||||
proInfoShort: _('%d GB', 10),
|
||||
teamsInfoShort: _('%d GB', 10),
|
||||
},
|
||||
publishNote: {
|
||||
title: _('Publish notes to the internet'),
|
||||
|
@@ -25,7 +25,7 @@ const userFetcher = async () => {
|
||||
const fileApi = await syncTarget.fileApi();
|
||||
const api = fileApi.driver().api();
|
||||
|
||||
if (!api.userId) {
|
||||
if (api.userId) {
|
||||
// That can happen if we don't have a session yet or if it has been
|
||||
// cleared
|
||||
logger.info('Skipping fetching user because user ID is not available');
|
||||
|
@@ -21,8 +21,8 @@
|
||||
"devDependencies": {
|
||||
"@types/jest": "29.5.4",
|
||||
"@types/pdfjs-dist": "2.10.378",
|
||||
"@types/react": "18.2.31",
|
||||
"@types/react-dom": "18.2.14",
|
||||
"@types/react": "18.2.23",
|
||||
"@types/react-dom": "18.2.8",
|
||||
"@types/styled-components": "5.1.28",
|
||||
"babel-jest": "29.6.4",
|
||||
"css-loader": "6.8.1",
|
||||
|
File diff suppressed because one or more lines are too long
@@ -35,7 +35,7 @@
|
||||
"highlight.js": "11.8.0",
|
||||
"html-entities": "1.4.0",
|
||||
"json-stringify-safe": "5.0.1",
|
||||
"katex": "0.16.9",
|
||||
"katex": "0.16.8",
|
||||
"markdown-it": "13.0.2",
|
||||
"markdown-it-abbr": "1.0.4",
|
||||
"markdown-it-anchor": "5.3.0",
|
||||
|
@@ -56,7 +56,7 @@
|
||||
"html-entities": "1.4.0",
|
||||
"jest": "29.6.4",
|
||||
"rss": "1.2.2",
|
||||
"sass": "1.67.0",
|
||||
"sass": "1.66.1",
|
||||
"sqlite3": "5.1.6",
|
||||
"typescript": "5.1.6"
|
||||
},
|
||||
|
@@ -1 +1 @@
|
||||
{"processedReleases":{"v2.13.1":true,"v2.13.2":true,"v2.13.3":true}}
|
||||
{"processedReleases":{"v2.13.1":true,"v2.13.2":true}}
|
@@ -309,7 +309,6 @@ Deletes the folder with ID :id
|
||||
| share_id | text | |
|
||||
| master_key_id | text | |
|
||||
| user_data | text | |
|
||||
| blob_updated_time | int | |
|
||||
|
||||
## GET /resources
|
||||
|
||||
|
@@ -1,18 +1,5 @@
|
||||
# Joplin changelog
|
||||
|
||||
## [v2.13.3](https://github.com/laurent22/joplin/releases/tag/v2.13.3) (Pre-release) - 2023-10-24T09:25:33Z
|
||||
|
||||
- Improved: Support for plural translations ([#9033](https://github.com/laurent22/joplin/issues/9033))
|
||||
- Improved: Update Electron to 25.9.0 ([90832da](https://github.com/laurent22/joplin/commit/90832da))
|
||||
- Improved: Updated packages dayjs (v1.11.10), follow-redirects (v1.15.3), glob (v10.3.6), katex (v0.16.9), markdown-it (v13.0.2), react, react-redux (v8.1.3), react-select (v5.7.7), sharp (v0.32.6), tar (v6.2.0)
|
||||
- Improved: Use plain text editor in safe mode ([#8750](https://github.com/laurent22/joplin/issues/8750)) ([#8749](https://github.com/laurent22/joplin/issues/8749) by Henry Heino)
|
||||
- Fixed: Added Note Properties to Note menu bar items ([#9119](https://github.com/laurent22/joplin/issues/9119)) ([#9108](https://github.com/laurent22/joplin/issues/9108) by [@CptMeetKat](https://github.com/CptMeetKat))
|
||||
- Fixed: Beta editor: Allow tab key to insert tabs at cursor rather than indent in some cases ([#9107](https://github.com/laurent22/joplin/issues/9107)) ([#9104](https://github.com/laurent22/joplin/issues/9104) by Henry Heino)
|
||||
- Fixed: Fix external links in PDFs break Joplin ([#9094](https://github.com/laurent22/joplin/issues/9094)) ([#9070](https://github.com/laurent22/joplin/issues/9070) by Henry Heino)
|
||||
- Fixed: Fix markdown editor context menu not displaying on some devices ([#9030](https://github.com/laurent22/joplin/issues/9030)) ([#8881](https://github.com/laurent22/joplin/issues/8881) by Henry Heino)
|
||||
- Fixed: Fixed issues related to sharing notes on read-only notebooks ([1c7d22e](https://github.com/laurent22/joplin/commit/1c7d22e))
|
||||
- Fixed: Plugins: Fix building plugins on Windows ([3ac2fe9](https://github.com/laurent22/joplin/commit/3ac2fe9))
|
||||
|
||||
## [v2.12.19](https://github.com/laurent22/joplin/releases/tag/v2.12.19) - 2023-10-21T09:39:18Z
|
||||
|
||||
- Security: Update Electron to 25.9.0 ([#9049](https://github.com/laurent22/joplin/issues/9049) by Henry Heino)
|
||||
|
@@ -1,15 +1,5 @@
|
||||
# Joplin Android app changelog
|
||||
|
||||
## [android-v2.13.3](https://github.com/laurent22/joplin/releases/tag/android-v2.13.3) (Pre-release) - 2023-10-24T15:55:07Z
|
||||
|
||||
- Improved: Allow modifying a resource metadata only when synchronising (#9114)
|
||||
- Improved: Support for plural translations (#9033)
|
||||
- Improved: Updated packages @react-native-community/datetimepicker (v7.5.0), @react-native-community/geolocation (v3.1.0), @testing-library/react-native (v12.3.0), dayjs (v1.11.10), follow-redirects (v1.15.3), glob (v10.3.6), katex (v0.16.9), markdown-it (v13.0.2), react, react-native-device-info (v10.9.0), react-native-dropdownalert (v5), react-native-image-picker (v5.7.0), react-native-paper (v5.10.6), react-native-share (v9.4.1), react-native-webview (v13.6.0), react-native-zip-archive (v6.1.0), react-redux (v8.1.3), sass (v1.67.0), sharp (v0.32.6), tar (v6.2.0)
|
||||
- Fixed: Fix sidebar folder icon (cd55a9a)
|
||||
- Fixed: Fix writing UTF-8 data to a file replaces non-ASCII characters with ?s (#9076) (#9069 by Henry Heino)
|
||||
- Fixed: Fixed issues related to sharing notes on read-only notebooks (1c7d22e)
|
||||
- Fixed: Improve list toggle logic (#9103) (#9066 by Henry Heino)
|
||||
|
||||
## [android-v2.13.2](https://github.com/laurent22/joplin/releases/tag/android-v2.13.2) (Pre-release) - 2023-10-07T16:42:16Z
|
||||
|
||||
- New: Add share button to log screen (#8364 by Henry Heino)
|
||||
|
@@ -1,33 +1,5 @@
|
||||
# Joplin iOS app changelog
|
||||
|
||||
## [ios-v12.13.1](https://github.com/laurent22/joplin/releases/tag/ios-v12.13.1) - 2023-10-24T14:50:37Z
|
||||
|
||||
- New: Add share button to log screen (#8364 by Henry Heino)
|
||||
- New: Add support for drawing pictures (#7588 by Henry Heino)
|
||||
- Improved: Allow modifying a resource metadata only when synchronising (#9114)
|
||||
- Improved: Apply correct size to images imported from ENEX files (#8684)
|
||||
- Improved: Bump mermaid version to 10.4.0 to support new chart types (#8890) (#8728 by [@oj-lappi](https://github.com/oj-lappi))
|
||||
- Improved: Enable ignoreTlsErrors and custom certificates for S3 sync (#8980 by Jens Böttge)
|
||||
- Improved: Fix random crash due to sidebar animation (#8792) (#8791 by Henry Heino)
|
||||
- Improved: Improved handling of invalid sync info (#6978)
|
||||
- Improved: Remember whether "All notes", a notebook or a tag was opened when re-opening the app (#8021)
|
||||
- Improved: Support for plural translations (#9033)
|
||||
- Improved: Updated packages @bam.tech/react-native-image-resizer (v3.0.7), @react-native-community/datetimepicker (v7.5.0), @react-native-community/geolocation (v3.1.0), @react-native-community/slider (v4.4.3), @testing-library/jest-native (v5.4.3), @testing-library/react-native (v12.3.0), compare-versions (v6.1.0), dayjs (v1.11.10), deprecated-react-native-prop-types (v4.2.1), follow-redirects (v1.15.3), glob (v10.3.6), katex (v0.16.9), markdown-it (v13.0.2), markdown-it-multimd-table (v4.2.3), nodemon (v3.0.1), react, react-native-device-info (v10.9.0), react-native-dropdownalert (v5), react-native-exit-app (v2), react-native-gesture-handler (v2.12.1), react-native-image-picker (v5.7.0), react-native-modal-datetime-picker (v17.1.0), react-native-paper (v5.10.6), react-native-safe-area-context (v4.7.2), react-native-share (v9.4.1), react-native-url-polyfill (v2), react-native-vector-icons (v10), react-native-webview (v13.6.0), react-native-zip-archive (v6.1.0), react-redux (v8.1.3), sass (v1.67.0), sharp (v0.32.6), sprintf-js (v1.1.3), tar (v6.2.0), url (v0.11.3), uuid (v9.0.1)
|
||||
- Fixed: Fix complex queries that contain quotes or filters (#8050)
|
||||
- Fixed: Fix icon after react-native-vector-icon upgrade (0e0c1d8)
|
||||
- Fixed: Fix not all dropdown items focusable with VoiceOver (#8714) (#8707 by Henry Heino)
|
||||
- Fixed: Fix search engine ranking algorithm (f504cf1)
|
||||
- Fixed: Fix sidebar folder icon (cd55a9a)
|
||||
- Fixed: Fix sync issue with Stackstorage (#2153)
|
||||
- Fixed: Fix unordered list button creates checklists (#8957) (#8956 by Henry Heino)
|
||||
- Fixed: Fix writing UTF-8 data to a file replaces non-ASCII characters with ?s (#9076) (#9069 by Henry Heino)
|
||||
- Fixed: Fixed code block not default line wrap in pdf view (#8626) (#8517 by [@wljince007](https://github.com/wljince007))
|
||||
- Fixed: Fixed issues related to sharing notes on read-only notebooks (1c7d22e)
|
||||
- Fixed: Hide the keyboard when showing the attach dialog (#8911) (#8774 by Henry Heino)
|
||||
- Fixed: Improve list toggle logic (#9103) (#9066 by Henry Heino)
|
||||
- Fixed: Prevent accessibility tools from focusing the notes list when it's invisible (#8799) (#8798 by Henry Heino)
|
||||
- Fixed: Prevent application from being stuck when importing an invalid ENEX file (#8699)
|
||||
|
||||
## [ios-v12.12.3](https://github.com/laurent22/joplin/releases/tag/ios-v12.12.3) - 2023-09-11T20:05:19Z
|
||||
|
||||
- Improved: Add screen reader labels to search/note actions buttons (#8797) (#8796 by Henry Heino)
|
||||
|
@@ -1,32 +0,0 @@
|
||||
---
|
||||
tweet: Working in the shadows with white-hat hackers
|
||||
forum_url: https://discourse.joplinapp.org/t/33283
|
||||
---
|
||||
|
||||
# Working in the shadows with white-hat hackers
|
||||
|
||||
The majority of Joplin's development is carried out in the public domain. This includes the discussion of issues on GitHub, as well as the submission of pull requests and related discussions. The transparency of these processes allows for collaborative problem-solving and shared insights.
|
||||
|
||||
However, there is one aspect that operates behind closed doors, and for good reason: addressing cybersecurity vulnerabilities. It is imperative that these issues remain undisclosed until they have been resolved. Once a solution is implemented, it is usually accompanied by discreet commits and a message in the changelog to signify the progress made.
|
||||
|
||||
Typically, the process begins with an email from a security researcher. They provide valuable insights, such as a specially crafted note that triggers a bug, or an API call, along with an explanation of how the application's security can be circumvented. We examine the vulnerability, create a fix, and create automated test units to prevent any accidental reintroduction of the vulnerability in future code updates. An example of such a commit is: [9e90d9016daf79b5414646a93fd369aedb035071](https://github.com/laurent22/joplin/commit/9e90d9016daf79b5414646a93fd369aedb035071)
|
||||
|
||||
We then share our fix with the researcher for validation. Additionally, we often apply the fix to previous versions of Joplin, depending on the severity of the vulnerability.
|
||||
|
||||
The contribution of security researchers in this regard is immeasurable. They employ their ingenuity to identify inventive methods of bypassing existing security measures and often discover subtle flaws in the code that might otherwise go unnoticed.
|
||||
|
||||
We would like to express our sincere gratitude to the security researchers who have assisted us throughout the years in identifying and rectifying security vulnerabilities!
|
||||
|
||||
- [@Alise](https://github.com/a1ise)
|
||||
- @hexodotsh
|
||||
- [@ly1g3](https://github.com/ly1g3)
|
||||
- [@maple3142](https://twitter.com/maple3142)
|
||||
- Ademar Nowasky Junior
|
||||
- [Benjamin Harris](mailto:ben@mayhem.sg)
|
||||
- [Javier Olmedo](https://github.com/JavierOlmedo)
|
||||
- [Jubair Rehman Yousafzai](https://twitter.com/newfolderj)
|
||||
- lin@UCCU Hacker
|
||||
- [personalizedrefrigerator](https://github.com/personalizedrefrigerator)
|
||||
- [Phil Holbrook](https://twitter.com/fhlipZero)
|
||||
- [RyotaK](https://ryotak.net/)
|
||||
- [Yaniv Nizry](https://twitter.com/YNizry)
|
159
yarn.lock
159
yarn.lock
@@ -4666,7 +4666,7 @@ __metadata:
|
||||
"@types/jest": 29.5.4
|
||||
"@types/mustache": 4.2.3
|
||||
"@types/node": 18.17.19
|
||||
"@types/react": 18.2.31
|
||||
"@types/react": 18.2.23
|
||||
"@types/react-redux": 7.1.27
|
||||
"@types/styled-components": 5.1.28
|
||||
async-mutex: 0.4.0
|
||||
@@ -4692,7 +4692,7 @@ __metadata:
|
||||
md5: 2.3.0
|
||||
moment: 2.29.4
|
||||
mustache: 4.2.0
|
||||
nan: 2.18.0
|
||||
nan: 2.17.0
|
||||
node-fetch: 2.6.7
|
||||
node-notifier: 10.0.1
|
||||
node-rsa: 1.1.1
|
||||
@@ -4702,7 +4702,7 @@ __metadata:
|
||||
react-datetime: 3.2.0
|
||||
react-dom: 18.2.0
|
||||
react-redux: 8.1.3
|
||||
react-select: 5.7.7
|
||||
react-select: 5.7.5
|
||||
react-test-renderer: 18.2.0
|
||||
react-toggle-button: 2.2.0
|
||||
react-tooltip: 4.5.1
|
||||
@@ -4754,7 +4754,7 @@ __metadata:
|
||||
"@tsconfig/react-native": 2.0.2
|
||||
"@types/fs-extra": 11.0.2
|
||||
"@types/jest": 29.5.4
|
||||
"@types/react": 18.2.31
|
||||
"@types/react": 18.2.23
|
||||
"@types/react-native": 0.70.6
|
||||
"@types/react-redux": 7.1.27
|
||||
"@types/tar-stream": 2.2.3
|
||||
@@ -4789,7 +4789,7 @@ __metadata:
|
||||
react-native-device-info: 10.9.0
|
||||
react-native-dialogbox: 0.6.10
|
||||
react-native-document-picker: 9.0.1
|
||||
react-native-dropdownalert: 5.1.0
|
||||
react-native-dropdownalert: 4.5.1
|
||||
react-native-exit-app: 2.0.0
|
||||
react-native-file-viewer: 2.1.5
|
||||
react-native-fingerprint-scanner: 6.0.0
|
||||
@@ -4856,7 +4856,7 @@ __metadata:
|
||||
"@replit/codemirror-vim": 6.0.14
|
||||
"@testing-library/react-hooks": 8.0.1
|
||||
"@types/jest": 29.5.4
|
||||
"@types/react": 18.2.31
|
||||
"@types/react": 18.2.23
|
||||
"@types/react-redux": 7.1.27
|
||||
"@types/styled-components": 5.1.28
|
||||
jest: 29.6.3
|
||||
@@ -4939,7 +4939,7 @@ __metadata:
|
||||
"@types/nanoid": 3.0.0
|
||||
"@types/node": 18.17.19
|
||||
"@types/node-rsa": 1.1.2
|
||||
"@types/react": 18.2.31
|
||||
"@types/react": 18.2.23
|
||||
"@types/uuid": 9.0.4
|
||||
async-mutex: 0.4.0
|
||||
base-64: 1.0.0
|
||||
@@ -5011,8 +5011,8 @@ __metadata:
|
||||
"@joplin/lib": ~2.13
|
||||
"@types/jest": 29.5.4
|
||||
"@types/pdfjs-dist": 2.10.378
|
||||
"@types/react": 18.2.31
|
||||
"@types/react-dom": 18.2.14
|
||||
"@types/react": 18.2.23
|
||||
"@types/react-dom": 18.2.8
|
||||
"@types/styled-components": 5.1.28
|
||||
async-mutex: 0.4.0
|
||||
babel-jest: 29.6.4
|
||||
@@ -5103,7 +5103,7 @@ __metadata:
|
||||
jest: 29.6.4
|
||||
jest-environment-jsdom: 29.6.4
|
||||
json-stringify-safe: 5.0.1
|
||||
katex: 0.16.9
|
||||
katex: 0.16.8
|
||||
markdown-it: 13.0.2
|
||||
markdown-it-abbr: 1.0.4
|
||||
markdown-it-anchor: 5.3.0
|
||||
@@ -5221,7 +5221,7 @@ __metadata:
|
||||
relative: 3.0.2
|
||||
request: 2.88.2
|
||||
rss: 1.2.2
|
||||
sass: 1.67.0
|
||||
sass: 1.66.1
|
||||
sharp: 0.32.6
|
||||
source-map-support: 0.5.21
|
||||
sqlite3: 5.1.6
|
||||
@@ -8346,12 +8346,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/react-dom@npm:18.2.14":
|
||||
version: 18.2.14
|
||||
resolution: "@types/react-dom@npm:18.2.14"
|
||||
"@types/react-dom@npm:18.2.8":
|
||||
version: 18.2.8
|
||||
resolution: "@types/react-dom@npm:18.2.8"
|
||||
dependencies:
|
||||
"@types/react": "*"
|
||||
checksum: 890289c70d1966c168037637c09cacefe6205bdd27a33252144a6b432595a2943775ac1a1accac0beddaeb67f8fdf721e076acb1adc990b08e51c3d9fd4e780c
|
||||
checksum: d36264631028d021b73cd9e015f10b95c4959ae1ce8f7a7419f318d1f05b1d063e6afffcd2a349a6bccd64ccc9ee9d2d976e1f0437643f0e7db621fa035bca65
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -8405,14 +8405,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/react@npm:18.2.31":
|
||||
version: 18.2.31
|
||||
resolution: "@types/react@npm:18.2.31"
|
||||
"@types/react@npm:18.2.23":
|
||||
version: 18.2.23
|
||||
resolution: "@types/react@npm:18.2.23"
|
||||
dependencies:
|
||||
"@types/prop-types": "*"
|
||||
"@types/scheduler": "*"
|
||||
csstype: ^3.0.2
|
||||
checksum: b11be8e39174d3303e308461400889e353e422d22b01d09795b2c35b7b99d5351716503d9ec5c58e4c2c871249603fa52840d45a34fb5901dd7a26e06129c716
|
||||
checksum: efb9d1ed1940c0e7ba08a21ffba5e266d8dbbb8fe618cfb97bc902dfc96385fdd8189e3f7f64b4aa13134f8e61947d60560deb23be151253c3a97b0d070897ca
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -10166,7 +10166,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"asap@npm:^2.0.0, asap@npm:~2.0.6":
|
||||
"asap@npm:^2.0.0, asap@npm:~2.0.3, asap@npm:~2.0.6":
|
||||
version: 2.0.6
|
||||
resolution: "asap@npm:2.0.6"
|
||||
checksum: b296c92c4b969e973260e47523207cd5769abd27c245a68c26dc7a0fe8053c55bb04360237cb51cab1df52be939da77150ace99ad331fb7fb13b3423ed73ff3d
|
||||
@@ -13074,6 +13074,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"core-js@npm:^1.0.0":
|
||||
version: 1.2.7
|
||||
resolution: "core-js@npm:1.2.7"
|
||||
checksum: 0b76371bfa98708351cde580f9287e2360d2209920e738ae950ae74ad08639a2e063541020bf666c28778956fc356ed9fe56d962129c88a87a6a4a0612526c75
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"core-util-is@npm:1.0.2":
|
||||
version: 1.0.2
|
||||
resolution: "core-util-is@npm:1.0.2"
|
||||
@@ -17065,6 +17072,21 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fbjs@npm:^0.8.9":
|
||||
version: 0.8.18
|
||||
resolution: "fbjs@npm:0.8.18"
|
||||
dependencies:
|
||||
core-js: ^1.0.0
|
||||
isomorphic-fetch: ^2.1.1
|
||||
loose-envify: ^1.0.0
|
||||
object-assign: ^4.1.0
|
||||
promise: ^7.1.1
|
||||
setimmediate: ^1.0.5
|
||||
ua-parser-js: ^0.7.30
|
||||
checksum: 668731b946a765908c9cbe51d5160f973abb78004b3d122587c3e930e3e1ddcc0ce2b17f2a8637dc9d733e149aa580f8d3035a35cc2d3bc78b78f1b19aab90e2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fd-slicer@npm:~1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "fd-slicer@npm:1.1.0"
|
||||
@@ -20587,7 +20609,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-stream@npm:^1.1.0":
|
||||
"is-stream@npm:^1.0.1, is-stream@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "is-stream@npm:1.1.0"
|
||||
checksum: 063c6bec9d5647aa6d42108d4c59723d2bd4ae42135a2d4db6eadbd49b7ea05b750fd69d279e5c7c45cf9da753ad2c00d8978be354d65aa9f6bb434969c6a2ae
|
||||
@@ -20835,6 +20857,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"isomorphic-fetch@npm:^2.1.1":
|
||||
version: 2.2.1
|
||||
resolution: "isomorphic-fetch@npm:2.2.1"
|
||||
dependencies:
|
||||
node-fetch: ^1.0.1
|
||||
whatwg-fetch: ">=0.10.0"
|
||||
checksum: bb5daa7c3785d6742f4379a81e55b549a469503f7c9bf9411b48592e86632cf5e8fe8ea878dba185c0f33eb7c510c23abdeb55aebfdf5d3c70f031ced68c5424
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"isstream@npm:~0.1.2":
|
||||
version: 0.1.2
|
||||
resolution: "isstream@npm:0.1.2"
|
||||
@@ -22710,14 +22742,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"katex@npm:0.16.9":
|
||||
version: 0.16.9
|
||||
resolution: "katex@npm:0.16.9"
|
||||
"katex@npm:0.16.8":
|
||||
version: 0.16.8
|
||||
resolution: "katex@npm:0.16.8"
|
||||
dependencies:
|
||||
commander: ^8.3.0
|
||||
bin:
|
||||
katex: cli.js
|
||||
checksum: 861194dfd4d86505e657f688fb73048d46ac498edafce71199502a35b03c0ecc35ba930c631be79c4a09d90a0d23476673cd52f6bc367c7a161854d64005fa95
|
||||
checksum: 4e75b4786101cc5eca0404bb814b2985bec506846f9015e9bf00207a3af14215e341ee62b6e7af2455a1032f8244e47a754642f250eea43d7b8007146ac01fae
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -23527,7 +23559,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0":
|
||||
"loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0, loose-envify@npm:^1.3.1, loose-envify@npm:^1.4.0":
|
||||
version: 1.4.0
|
||||
resolution: "loose-envify@npm:1.4.0"
|
||||
dependencies:
|
||||
@@ -25827,16 +25859,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nan@npm:2.18.0":
|
||||
version: 2.18.0
|
||||
resolution: "nan@npm:2.18.0"
|
||||
dependencies:
|
||||
node-gyp: latest
|
||||
checksum: 4fe42f58456504eab3105c04a5cffb72066b5f22bd45decf33523cb17e7d6abc33cca2a19829407b9000539c5cb25f410312d4dc5b30220167a3594896ea6a0a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nan@npm:^2.12.1":
|
||||
"nan@npm:2.17.0, nan@npm:^2.12.1":
|
||||
version: 2.17.0
|
||||
resolution: "nan@npm:2.17.0"
|
||||
dependencies:
|
||||
@@ -26145,6 +26168,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-fetch@npm:^1.0.1":
|
||||
version: 1.7.3
|
||||
resolution: "node-fetch@npm:1.7.3"
|
||||
dependencies:
|
||||
encoding: ^0.1.11
|
||||
is-stream: ^1.0.1
|
||||
checksum: 3bb0528c05d541316ebe52770d71ee25a6dce334df4231fd55df41a644143e07f068637488c18a5b0c43f05041dbd3346752f9e19b50df50569a802484544d5b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-fetch@npm:^2.2.0, node-fetch@npm:^2.5.0, node-fetch@npm:^2.6.0, node-fetch@npm:^2.6.1":
|
||||
version: 2.6.6
|
||||
resolution: "node-fetch@npm:2.6.6"
|
||||
@@ -28715,6 +28748,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"promise@npm:^7.1.1":
|
||||
version: 7.3.1
|
||||
resolution: "promise@npm:7.3.1"
|
||||
dependencies:
|
||||
asap: ~2.0.3
|
||||
checksum: 475bb069130179fbd27ed2ab45f26d8862376a137a57314cf53310bdd85cc986a826fd585829be97ebc0aaf10e9d8e68be1bfe5a4a0364144b1f9eedfa940cf1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"prompts@npm:^2.0.1, prompts@npm:^2.4.0":
|
||||
version: 2.4.2
|
||||
resolution: "prompts@npm:2.4.2"
|
||||
@@ -28745,6 +28787,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"prop-types@npm:15.5.10":
|
||||
version: 15.5.10
|
||||
resolution: "prop-types@npm:15.5.10"
|
||||
dependencies:
|
||||
fbjs: ^0.8.9
|
||||
loose-envify: ^1.3.1
|
||||
checksum: 3e928ad5afa5124d52a341a706170628e7b0caa9340515782be6a767261e6eb0e473116188bb8efbe9d9b62cb3c9501c71bf4ab7d34f2507294ee34c90de6964
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"prop-types@npm:^15.5.7, prop-types@npm:^15.5.8, prop-types@npm:^15.6.0, prop-types@npm:^15.6.2, prop-types@npm:^15.7.2":
|
||||
version: 15.7.2
|
||||
resolution: "prop-types@npm:15.7.2"
|
||||
@@ -29304,10 +29356,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-native-dropdownalert@npm:5.1.0":
|
||||
version: 5.1.0
|
||||
resolution: "react-native-dropdownalert@npm:5.1.0"
|
||||
checksum: 595e409967a28e5305b7895407a801c6eb05091277eaa7362b30dce18213d695dcbf0811c69cbaa20f35ce9aa21f13d44235dd35717119e0020a3159bd06b0ef
|
||||
"react-native-dropdownalert@npm:4.5.1":
|
||||
version: 4.5.1
|
||||
resolution: "react-native-dropdownalert@npm:4.5.1"
|
||||
dependencies:
|
||||
prop-types: 15.5.10
|
||||
checksum: 16346105f130f1aefe8ed4c9171524ce931f2a924c6fa95b41291f99a5613a7c4fb9a3f75b19ef280ac8d47f8ba13ebadf596174d83a92cbbdd00c278c2e2b9f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -29762,9 +29816,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-select@npm:5.7.7":
|
||||
version: 5.7.7
|
||||
resolution: "react-select@npm:5.7.7"
|
||||
"react-select@npm:5.7.5":
|
||||
version: 5.7.5
|
||||
resolution: "react-select@npm:5.7.5"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.12.0
|
||||
"@emotion/cache": ^11.4.0
|
||||
@@ -29778,7 +29832,7 @@ __metadata:
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
checksum: 6fd0c211d377addba6e6762a614ae674936df39a3f46ec19fd06e7acae8d6cadeb93d4723b10e25eff1ff8235077bae9459f293936334d82b28fe5071081c057
|
||||
checksum: 88f2d94c4a6778df525a9fb5d7acac1bf34821f6efcfdc5927ec608f5f933cf3f47e1c4e4fd3b92d7b2ba1d91e44595d45ac4e2fd7528ba420086008ac5a81cf
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -31144,16 +31198,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"sass@npm:1.67.0":
|
||||
version: 1.67.0
|
||||
resolution: "sass@npm:1.67.0"
|
||||
"sass@npm:1.66.1":
|
||||
version: 1.66.1
|
||||
resolution: "sass@npm:1.66.1"
|
||||
dependencies:
|
||||
chokidar: ">=3.0.0 <4.0.0"
|
||||
immutable: ^4.0.0
|
||||
source-map-js: ">=0.6.2 <2.0.0"
|
||||
bin:
|
||||
sass: sass.js
|
||||
checksum: 9e7566e8b7386cf265dddcdb266a023fb5759c5a8f48a11da199c8bf419e5f08f4ff6404d85d6bf5eac01e1f7c7061fdb6b7b65cbfda164e59b0a06b72ac8567
|
||||
checksum: 74fc11d0fcd5e16c5331b57dd59865705a299c64e89f2b99646869caeb011dc8d0b6144a6c74a90c264e9ef70654207dbf44fc9b7e3393f8bd14809b904c8a52
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -34650,6 +34704,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ua-parser-js@npm:^0.7.30":
|
||||
version: 0.7.31
|
||||
resolution: "ua-parser-js@npm:0.7.31"
|
||||
checksum: e2f8324a83d1715601576af85b2b6c03890699aaa7272950fc77ea925c70c5e4f75060ae147dc92124e49f7f0e3d6dd2b0a91e7f40d267e92df8894be967ba8b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"uc.micro@npm:^1.0.1, uc.micro@npm:^1.0.5":
|
||||
version: 1.0.6
|
||||
resolution: "uc.micro@npm:1.0.6"
|
||||
@@ -35812,7 +35873,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"whatwg-fetch@npm:^3.0.0":
|
||||
"whatwg-fetch@npm:>=0.10.0, whatwg-fetch@npm:^3.0.0":
|
||||
version: 3.6.2
|
||||
resolution: "whatwg-fetch@npm:3.6.2"
|
||||
checksum: ee976b7249e7791edb0d0a62cd806b29006ad7ec3a3d89145921ad8c00a3a67e4be8f3fb3ec6bc7b58498724fd568d11aeeeea1f7827e7e1e5eae6c8a275afed
|
||||
|
Reference in New Issue
Block a user