1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-08-24 20:19:10 +02:00

Compare commits

...

2 Commits

Author SHA1 Message Date
Laurent Cozic
812666957c update 2025-02-12 18:13:27 +00:00
Laurent Cozic
dabb7e08b4 init 2025-02-12 15:30:21 +00:00
29 changed files with 345 additions and 123 deletions

View File

@@ -456,7 +456,6 @@ packages/app-desktop/gui/WindowCommandsAndDialogs/commands/restoreNote.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/revealResourceFile.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/search.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/setTags.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showEditorPlugin.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showModalMessage.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showNoteContentProperties.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showNoteProperties.js
@@ -465,7 +464,6 @@ packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showShareFolderDialog
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showShareNoteDialog.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showSpellCheckerMenu.test.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showSpellCheckerMenu.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/toggleEditorPlugin.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/toggleEditors.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/toggleLayoutMoveMode.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/toggleMenuBar.js
@@ -1021,7 +1019,9 @@ packages/lib/commands/openMasterPasswordDialog.js
packages/lib/commands/permanentlyDeleteNote.js
packages/lib/commands/renderMarkup.test.js
packages/lib/commands/renderMarkup.js
packages/lib/commands/showEditorPlugin.js
packages/lib/commands/synchronize.js
packages/lib/commands/toggleEditorPlugin.js
packages/lib/components/EncryptionConfigScreen/utils.test.js
packages/lib/components/EncryptionConfigScreen/utils.js
packages/lib/components/shared/NoteList/getEmptyFolderMessage.js
@@ -1303,6 +1303,7 @@ packages/lib/services/plugins/utils/getPluginIssueReportUrl.js
packages/lib/services/plugins/utils/getPluginNamespacedSettingKey.js
packages/lib/services/plugins/utils/getPluginSettingKeyPrefix.js
packages/lib/services/plugins/utils/getPluginSettingValue.js
packages/lib/services/plugins/utils/getShownPluginEditorView.js
packages/lib/services/plugins/utils/isCompatible/getDefaultPlatforms.js
packages/lib/services/plugins/utils/isCompatible/index.test.js
packages/lib/services/plugins/utils/isCompatible/index.js

5
.gitignore vendored
View File

@@ -431,7 +431,6 @@ packages/app-desktop/gui/WindowCommandsAndDialogs/commands/restoreNote.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/revealResourceFile.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/search.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/setTags.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showEditorPlugin.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showModalMessage.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showNoteContentProperties.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showNoteProperties.js
@@ -440,7 +439,6 @@ packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showShareFolderDialog
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showShareNoteDialog.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showSpellCheckerMenu.test.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showSpellCheckerMenu.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/toggleEditorPlugin.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/toggleEditors.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/toggleLayoutMoveMode.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/toggleMenuBar.js
@@ -996,7 +994,9 @@ packages/lib/commands/openMasterPasswordDialog.js
packages/lib/commands/permanentlyDeleteNote.js
packages/lib/commands/renderMarkup.test.js
packages/lib/commands/renderMarkup.js
packages/lib/commands/showEditorPlugin.js
packages/lib/commands/synchronize.js
packages/lib/commands/toggleEditorPlugin.js
packages/lib/components/EncryptionConfigScreen/utils.test.js
packages/lib/components/EncryptionConfigScreen/utils.js
packages/lib/components/shared/NoteList/getEmptyFolderMessage.js
@@ -1278,6 +1278,7 @@ packages/lib/services/plugins/utils/getPluginIssueReportUrl.js
packages/lib/services/plugins/utils/getPluginNamespacedSettingKey.js
packages/lib/services/plugins/utils/getPluginSettingKeyPrefix.js
packages/lib/services/plugins/utils/getPluginSettingValue.js
packages/lib/services/plugins/utils/getShownPluginEditorView.js
packages/lib/services/plugins/utils/isCompatible/getDefaultPlatforms.js
packages/lib/services/plugins/utils/isCompatible/index.test.js
packages/lib/services/plugins/utils/isCompatible/index.js

View File

@@ -1,15 +1,11 @@
import { useMemo } from 'react';
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
import getActivePluginEditorView from '@joplin/lib/services/plugins/utils/getActivePluginEditorView';
import getShownPluginEditorView from '@joplin/lib/services/plugins/utils/getShownPluginEditorView';
// If a plugin editor should be shown for the current note, this function will return the plugin and
// associated view.
export default (plugins: PluginStates, shownEditorViewIds: string[]) => {
return useMemo(() => {
const { editorPlugin, editorView } = getActivePluginEditorView(plugins);
if (editorView) {
if (!shownEditorViewIds.includes(editorView.id)) return { editorPlugin: null, editorView: null };
}
return { editorPlugin, editorView };
return getShownPluginEditorView(plugins, shownEditorViewIds);
}, [plugins, shownEditorViewIds]);
};

View File

@@ -28,7 +28,6 @@ import * as restoreNote from './restoreNote';
import * as revealResourceFile from './revealResourceFile';
import * as search from './search';
import * as setTags from './setTags';
import * as showEditorPlugin from './showEditorPlugin';
import * as showModalMessage from './showModalMessage';
import * as showNoteContentProperties from './showNoteContentProperties';
import * as showNoteProperties from './showNoteProperties';
@@ -36,7 +35,6 @@ import * as showPrompt from './showPrompt';
import * as showShareFolderDialog from './showShareFolderDialog';
import * as showShareNoteDialog from './showShareNoteDialog';
import * as showSpellCheckerMenu from './showSpellCheckerMenu';
import * as toggleEditorPlugin from './toggleEditorPlugin';
import * as toggleEditors from './toggleEditors';
import * as toggleLayoutMoveMode from './toggleLayoutMoveMode';
import * as toggleMenuBar from './toggleMenuBar';
@@ -78,7 +76,6 @@ const index: any[] = [
revealResourceFile,
search,
setTags,
showEditorPlugin,
showModalMessage,
showNoteContentProperties,
showNoteProperties,
@@ -86,7 +83,6 @@ const index: any[] = [
showShareFolderDialog,
showShareNoteDialog,
showSpellCheckerMenu,
toggleEditorPlugin,
toggleEditors,
toggleLayoutMoveMode,
toggleMenuBar,

View File

@@ -25,6 +25,7 @@ import WebBetaButton from './WebBetaButton';
import Menu, { MenuOptionType } from './Menu';
import shim from '@joplin/lib/shim';
import CommandService from '@joplin/lib/services/CommandService';
export { MenuOptionType };
// Rather than applying a padding to the whole bar, it is applied to each
@@ -67,6 +68,7 @@ interface ScreenHeaderProps {
showSideMenuButton?: boolean;
showSearchButton?: boolean;
showContextMenuButton?: boolean;
showPluginEditorButton?: boolean;
showBackButton?: boolean;
saveButtonDisabled?: boolean;
@@ -419,6 +421,28 @@ class ScreenHeaderComponent extends PureComponent<ScreenHeaderProps, ScreenHeade
);
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const renderTogglePluginEditorButton = (styles: any, onPress: OnPressCallback, disabled: boolean) => {
if (!this.props.showPluginEditorButton) return null;
return (
<IconButton
onPress={onPress}
disabled={disabled}
themeId={themeId}
description={_('Toggle plugin editor')}
accessibilityHint={
disabled ? null : _('Toggle plugin editor')
}
contentWrapperStyle={disabled ? styles.iconButtonDisabled : styles.iconButton}
iconName='ionicon eye'
iconStyle={styles.topIcon}
/>
);
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
function deleteButton(styles: any, onPress: OnPressCallback, disabled: boolean) {
return (
@@ -631,6 +655,8 @@ class ScreenHeaderComponent extends PureComponent<ScreenHeaderProps, ScreenHeade
</Menu>
);
const togglePluginEditorButton = renderTogglePluginEditorButton(this.styles(), () => CommandService.instance().execute('toggleEditorPlugin'), false);
return (
<View style={this.styles().container}>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
@@ -653,6 +679,7 @@ class ScreenHeaderComponent extends PureComponent<ScreenHeaderProps, ScreenHeade
{restoreButtonComp}
{duplicateButtonComp}
{sortButtonComp}
{togglePluginEditorButton}
{menuComp}
</View>
<WarningBanner

View File

@@ -28,7 +28,7 @@ const PluginDialogManager: React.FC<Props> = props => {
const dialogs: ReactElement[] = [];
for (const viewInfo of viewInfos) {
if (viewInfo.view.containerType === ContainerType.Panel || !viewInfo.view.opened) {
if (viewInfo.view.containerType === ContainerType.Panel || viewInfo.view.containerType === ContainerType.Editor || !viewInfo.view.opened) {
continue;
}

View File

@@ -1,4 +1,4 @@
import AsyncActionQueue from '@joplin/lib/AsyncActionQueue';
import AsyncActionQueue, { IntervalType } from '@joplin/lib/AsyncActionQueue';
import uuid from '@joplin/lib/uuid';
import Setting from '@joplin/lib/models/Setting';
import shim from '@joplin/lib/shim';
@@ -51,7 +51,7 @@ import { getNoteCallbackUrl } from '@joplin/lib/callbackUrlUtils';
import { AppState } from '../../../utils/types';
import restoreItems from '@joplin/lib/services/trash/restoreItems';
import { getDisplayParentTitle } from '@joplin/lib/services/trash';
import { PluginStates, utils as pluginUtils } from '@joplin/lib/services/plugins/reducer';
import { PluginHtmlContents, PluginStates, utils as pluginUtils } from '@joplin/lib/services/plugins/reducer';
import debounce from '../../../utils/debounce';
import { focus } from '@joplin/lib/utils/focusHandler';
import CommandService, { RegisteredRuntime } from '@joplin/lib/services/CommandService';
@@ -63,6 +63,14 @@ import { DialogContext, DialogControl } from '../../DialogManager';
import { CommandRuntimeProps, EditorMode, PickerResponse } from './types';
import commands from './commands';
import { AttachFileAction, AttachFileOptions } from './commands/attachFile';
import { EditorActivationCheckFilterObject } from '@joplin/lib/services/plugins/api/types';
import eventManager from '@joplin/lib/eventManager';
import PluginService from '@joplin/lib/services/plugins/PluginService';
import WebviewController from '@joplin/lib/services/plugins/WebviewController';
import PluginUserWebView from '../../plugins/dialogs/PluginUserWebView';
import getShownPluginEditorView from '@joplin/lib/services/plugins/utils/getShownPluginEditorView';
import getActivePluginEditorView from '@joplin/lib/services/plugins/utils/getActivePluginEditorView';
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const emptyArray: any[] = [];
@@ -97,6 +105,9 @@ interface Props extends BaseProps {
highlightedWords: string[];
noteHash: string;
toolbarEnabled: boolean;
'plugins.shownEditorViewIds': string[];
pluginHtmlContents: PluginHtmlContents;
editorNoteReloadTimeRequest: number;
}
interface ComponentProps extends Props {
@@ -122,6 +133,7 @@ interface State {
imageEditorResourceFilepath: string;
noteResources: Record<string, ResourceInfo>;
newAndNoTitleChangeNoteId: boolean|null;
noteLastLoadTime: number;
undoRedoButtonState: {
canUndo: boolean;
@@ -131,6 +143,20 @@ interface State {
voiceTypingDialogShown: boolean;
}
// TODO: COPIED FROM DESKTOP
const makeNoteUpdateAction = (shownEditorViewIds: string[]) => {
return async () => {
for (const viewId of shownEditorViewIds) {
const controller = PluginService.instance().viewControllerByViewId(viewId) as WebviewController;
if (controller) controller.emitUpdate();
}
};
};
class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> implements BaseNoteScreenComponent {
// This isn't in this.state because we don't want changing scroll to trigger
// a re-render.
@@ -163,6 +189,8 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
public dialogbox: any;
private commandRegistration_: RegisteredRuntime|null = null;
private viewUpdateAsyncQueue_ = new AsyncActionQueue(100, IntervalType.Fixed);
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public static navigationOptions(): any {
return { header: null };
@@ -189,6 +217,7 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
noteResources: {},
imageEditorResourceFilepath: null,
newAndNoTitleChangeNoteId: null,
noteLastLoadTime: Date.now(),
undoRedoButtonState: {
canUndo: false,
@@ -551,6 +580,9 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
}
}, 100);
}
// TODO: set shownEditorViewIds
this.viewUpdateAsyncQueue_.push(makeNoteUpdateAction(['plugin-view-org.joplinapp.plugins.YesYouKan-kanbanBoard']));
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
@@ -610,6 +642,28 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
noteHash: noteHash,
});
}
if (this.props['plugins.shownEditorViewIds'] !== prevProps['plugins.shownEditorViewIds']) {
const { editorPlugin } = getShownPluginEditorView(this.props.plugins, this.props['plugins.shownEditorViewIds']);
if (!editorPlugin && this.props.editorNoteReloadTimeRequest > this.state.noteLastLoadTime) {
void shared.reloadNote(this);
}
}
// **************** TODO: REUSED FROM DESKTOP APP
setTimeout(async () => {
let filterObject: EditorActivationCheckFilterObject = {
activatedEditors: [],
};
filterObject = await eventManager.filterEmit('editorActivationCheck', filterObject);
for (const editor of filterObject.activatedEditors) {
const controller = PluginService.instance().pluginById(editor.pluginId).viewController(editor.viewId) as WebviewController;
controller.setActive(editor.isActive);
}
}, 50);
// **************** REUSED FROM DESKTOP APP
}
public componentWillUnmount() {
@@ -1420,6 +1474,8 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
// multiple times.
this.registerCommands();
const { editorPlugin, editorView } = getShownPluginEditorView(this.props.plugins, this.props['plugins.shownEditorViewIds']);
if (this.state.isLoading) {
return (
<View style={this.styles().screen}>
@@ -1448,10 +1504,25 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
/>;
}
const renderPluginEditor = () => {
return <PluginUserWebView
viewInfo={{ plugin: editorPlugin, view: editorView }}
themeId={this.props.themeId}
onLoadEnd={() => {}}
pluginHtmlContents={this.props.pluginHtmlContents}
setDialogControl={() => {}}
style={{}}
/>;
};
// Currently keyword highlighting is supported only when FTS is available.
const keywords = this.props.searchQuery && !!this.props.ftsEnabled ? this.props.highlightedWords : emptyArray;
let bodyComponent = null;
if (editorView) {
bodyComponent = renderPluginEditor();
} else {
if (this.state.mode === 'view') {
// Note: as of 2018-12-29 it's important not to display the viewer if the note body is empty,
// to avoid the HACK_webviewLoadingState related bug.
@@ -1541,6 +1612,7 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
/>;
}
}
}
const renderActionButton = () => {
if (this.state.voiceTypingDialogShown) return null;
@@ -1595,6 +1667,8 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
return <VoiceTypingDialog locale={currentLocale()} onText={this.voiceTypingDialog_onText} onDismiss={this.voiceTypingDialog_onDismiss}/>;
};
const { editorPlugin: activeEditorPlugin } = getActivePluginEditorView(this.props.plugins);
return (
<View style={this.rootStyle(this.props.themeId).root}>
<ScreenHeader
@@ -1607,6 +1681,7 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
showSearchButton={false}
showUndoButton={(this.state.undoRedoButtonState.canUndo || this.state.undoRedoButtonState.canRedo) && this.state.mode === 'edit'}
showRedoButton={this.state.undoRedoButtonState.canRedo && this.state.mode === 'edit'}
showPluginEditorButton={!!activeEditorPlugin}
undoButtonDisabled={!this.state.undoRedoButtonState.canUndo && this.state.undoRedoButtonState.canRedo}
onUndoButtonPress={this.screenHeader_undoButtonPress}
onRedoButtonPress={this.screenHeader_redoButtonPress}
@@ -1654,6 +1729,9 @@ const NoteScreen = connect((state: AppState) => {
provisionalNoteIds: state.provisionalNoteIds,
highlightedWords: state.highlightedWords,
plugins: state.pluginService.plugins,
'plugins.shownEditorViewIds': state.settings['plugins.shownEditorViewIds'] || [],
pluginHtmlContents: state.pluginService.pluginHtmlContents,
editorNoteReloadTimeRequest: state.editorNoteReloadTimeRequest,
// What we call "beta editor" in this component is actually the (now
// default) CodeMirror editor. That should be refactored to make it less

View File

@@ -288,7 +288,14 @@ class NotesScreenComponent extends BaseScreenComponent<ComponentProps, State> {
inert={accessibilityHidden}
>
<ScreenHeader title={iconString + title} showBackButton={false} sortButton_press={this.sortButton_press} folderPickerOptions={this.folderPickerOptions()} showSearchButton={true} showSideMenuButton={true} />
<ScreenHeader
title={iconString + title}
showBackButton={false}
sortButton_press={this.sortButton_press}
folderPickerOptions={this.folderPickerOptions()}
showSearchButton={true}
showSideMenuButton={true}
/>
<NoteList />
{actionButtonComp}
</AccessibleView>

View File

@@ -1 +1 @@
module.exports = {"hash":"cfa07333af79f4db4bc9ca008fb257f8","files":["highlight.js/atom-one-dark-reasonable.css","highlight.js/atom-one-light.css","katex/fonts/KaTeX_AMS-Regular.woff2","katex/fonts/KaTeX_Caligraphic-Bold.woff2","katex/fonts/KaTeX_Caligraphic-Regular.woff2","katex/fonts/KaTeX_Fraktur-Bold.woff2","katex/fonts/KaTeX_Fraktur-Regular.woff2","katex/fonts/KaTeX_Main-Bold.woff2","katex/fonts/KaTeX_Main-BoldItalic.woff2","katex/fonts/KaTeX_Main-Italic.woff2","katex/fonts/KaTeX_Main-Regular.woff2","katex/fonts/KaTeX_Math-BoldItalic.woff2","katex/fonts/KaTeX_Math-Italic.woff2","katex/fonts/KaTeX_SansSerif-Bold.woff2","katex/fonts/KaTeX_SansSerif-Italic.woff2","katex/fonts/KaTeX_SansSerif-Regular.woff2","katex/fonts/KaTeX_Script-Regular.woff2","katex/fonts/KaTeX_Size1-Regular.woff2","katex/fonts/KaTeX_Size2-Regular.woff2","katex/fonts/KaTeX_Size3-Regular.woff2","katex/fonts/KaTeX_Size4-Regular.woff2","katex/fonts/KaTeX_Typewriter-Regular.woff2","katex/katex.css","mermaid/mermaid.min.js","mermaid/mermaid_render.js"]}
module.exports = {"hash":"cfa07333af79f4db4bc9ca008fb257f8","files":[".DS_Store","highlight.js/atom-one-dark-reasonable.css","highlight.js/atom-one-light.css","katex/fonts/KaTeX_AMS-Regular.woff2","katex/fonts/KaTeX_Caligraphic-Bold.woff2","katex/fonts/KaTeX_Caligraphic-Regular.woff2","katex/fonts/KaTeX_Fraktur-Bold.woff2","katex/fonts/KaTeX_Fraktur-Regular.woff2","katex/fonts/KaTeX_Main-Bold.woff2","katex/fonts/KaTeX_Main-BoldItalic.woff2","katex/fonts/KaTeX_Main-Italic.woff2","katex/fonts/KaTeX_Main-Regular.woff2","katex/fonts/KaTeX_Math-BoldItalic.woff2","katex/fonts/KaTeX_Math-Italic.woff2","katex/fonts/KaTeX_SansSerif-Bold.woff2","katex/fonts/KaTeX_SansSerif-Italic.woff2","katex/fonts/KaTeX_SansSerif-Regular.woff2","katex/fonts/KaTeX_Script-Regular.woff2","katex/fonts/KaTeX_Size1-Regular.woff2","katex/fonts/KaTeX_Size2-Regular.woff2","katex/fonts/KaTeX_Size3-Regular.woff2","katex/fonts/KaTeX_Size4-Regular.woff2","katex/fonts/KaTeX_Typewriter-Regular.woff2","katex/katex.css","mermaid/mermaid.min.js","mermaid/mermaid_render.js"]}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,13 @@
module.exports = {
hash: '55cc4fcf19c129e3824873d98ad417c9', files: {
'fontawesome/css/all.min.css': { data: require('./fontawesome/css/all.min.css.base64.js'), mime: 'text/css', encoding: 'base64' },
'fontawesome/webfonts/fa-brands-400.ttf': { data: require('./fontawesome/webfonts/fa-brands-400.ttf.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'fontawesome/webfonts/fa-brands-400.woff2': { data: require('./fontawesome/webfonts/fa-brands-400.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'fontawesome/webfonts/fa-regular-400.ttf': { data: require('./fontawesome/webfonts/fa-regular-400.ttf.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'fontawesome/webfonts/fa-regular-400.woff2': { data: require('./fontawesome/webfonts/fa-regular-400.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'fontawesome/webfonts/fa-solid-900.ttf': { data: require('./fontawesome/webfonts/fa-solid-900.ttf.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'fontawesome/webfonts/fa-solid-900.woff2': { data: require('./fontawesome/webfonts/fa-solid-900.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'fontawesome/webfonts/fa-v4compatibility.ttf': { data: require('./fontawesome/webfonts/fa-v4compatibility.ttf.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'fontawesome/webfonts/fa-v4compatibility.woff2': { data: require('./fontawesome/webfonts/fa-v4compatibility.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
},
};

View File

@@ -0,0 +1 @@
module.exports = { 'hash': '55cc4fcf19c129e3824873d98ad417c9', 'files': ['fontawesome/css/all.min.css', 'fontawesome/webfonts/fa-brands-400.ttf', 'fontawesome/webfonts/fa-brands-400.woff2', 'fontawesome/webfonts/fa-regular-400.ttf', 'fontawesome/webfonts/fa-regular-400.woff2', 'fontawesome/webfonts/fa-solid-900.ttf', 'fontawesome/webfonts/fa-solid-900.woff2', 'fontawesome/webfonts/fa-v4compatibility.ttf', 'fontawesome/webfonts/fa-v4compatibility.woff2'] };

View File

@@ -5,7 +5,7 @@ const path = require('path');
const md5 = require('md5');
const rootDir = `${__dirname}/..`;
const outputDir = `${rootDir}/pluginAssets`;
const defaultOutputDir = `${rootDir}/pluginAssets`;
const walk = function(dir) {
let results = [];
@@ -37,7 +37,7 @@ const readAsBase64 = async (path, mime) => {
return buffer.toString('base64');
};
async function encodeFile(sourcePath, destPath) {
async function encodeFile(sourcePath, destPath, outputDir) {
const ext = utils.fileExtension(sourcePath).toLowerCase();
let mime = 'application/octet-stream';
if (ext === 'js') mime = 'application/javascript';
@@ -60,19 +60,35 @@ async function encodeFile(sourcePath, destPath) {
};
}
async function main() {
const copyFontAwesomeAssets = async () => {
const sourceDir = `${rootDir}/node_modules/@fortawesome/fontawesome-free`;
const targetDir = `${rootDir}/fontawesome-temp`;
await fs.remove(targetDir);
await fs.mkdirp(`${targetDir}/fontawesome/css`);
await fs.mkdirp(`${targetDir}/fontawesome/webfonts`);
await fs.copyFile(`${sourceDir}/css/all.min.css`, `${targetDir}/fontawesome/css/all.min.css`);
await fs.copy(`${sourceDir}/webfonts`, `${targetDir}/fontawesome/webfonts`);
return targetDir;
};
const encodeDirectory = async (sourceAssetDir) => {
for (let i = 0; i < 3; i++) {
try {
const outputDir = sourceAssetDir.destination ? sourceAssetDir.destination : defaultOutputDir;
await fs.remove(outputDir);
await utils.mkdirp(outputDir);
const encodedFiles = [];
const sourceAssetDir = `${rootDir}/../renderer/assets`;
const files = walk(sourceAssetDir);
const files = walk(sourceAssetDir.source);
for (const file of files) {
const destFile = file.substr(sourceAssetDir.length + 1);
encodedFiles.push(await encodeFile(file, destFile));
if (file.endsWith('.DS_Store')) continue;
const destFile = file.substr(sourceAssetDir.source.length + 1);
encodedFiles.push(await encodeFile(file, destFile, outputDir));
}
const hashes = [];
@@ -87,7 +103,7 @@ async function main() {
await fs.writeFile(`${outputDir}/index.js`, `module.exports = {\nhash:"${hash}", files: {\n${indexJs.join('\n')}\n}\n};`);
await fs.writeFile(`${outputDir}/index.web.js`, `module.exports = ${JSON.stringify({
hash,
files: files.map(file => toForwardSlashes(path.relative(sourceAssetDir, file))),
files: files.map(file => toForwardSlashes(path.relative(sourceAssetDir.source, file))),
})}`);
return;
@@ -115,6 +131,28 @@ async function main() {
}
throw new Error('Could not encode file after multiple attempts. See above for errors.');
};
async function main() {
const fontAwesomeAssetDir = await copyFontAwesomeAssets();
const sourceAssetDirs = [
{
source: `${rootDir}/../renderer/assets`,
},
{
source: fontAwesomeAssetDir,
destination: `${rootDir}/plugins/pluginUserWebViewAssets/fontawesome`,
},
];
try {
for (const sourceAssetDir of sourceAssetDirs) {
await encodeDirectory(sourceAssetDir);
}
} finally {
await fs.remove(fontAwesomeAssetDir);
}
}
module.exports = main;

View File

@@ -5,7 +5,9 @@ import * as historyForward from './historyForward';
import * as openMasterPasswordDialog from './openMasterPasswordDialog';
import * as permanentlyDeleteNote from './permanentlyDeleteNote';
import * as renderMarkup from './renderMarkup';
import * as showEditorPlugin from './showEditorPlugin';
import * as synchronize from './synchronize';
import * as toggleEditorPlugin from './toggleEditorPlugin';
const index: any[] = [
deleteNote,
@@ -14,7 +16,9 @@ const index: any[] = [
openMasterPasswordDialog,
permanentlyDeleteNote,
renderMarkup,
showEditorPlugin,
synchronize,
toggleEditorPlugin,
];
export default index;

View File

@@ -1,6 +1,6 @@
import { CommandContext, CommandDeclaration, CommandRuntime } from '@joplin/lib/services/CommandService';
import Setting from '@joplin/lib/models/Setting';
import getActivePluginEditorView from '@joplin/lib/services/plugins/utils/getActivePluginEditorView';
import { CommandContext, CommandDeclaration, CommandRuntime } from '../services/CommandService';
import Setting from '../models/Setting';
import getActivePluginEditorView from '../services/plugins/utils/getActivePluginEditorView';
import Logger from '@joplin/utils/Logger';
const logger = Logger.create('showEditorPlugin');

View File

@@ -1,7 +1,7 @@
import { CommandContext, CommandDeclaration, CommandRuntime } from '@joplin/lib/services/CommandService';
import { _ } from '@joplin/lib/locale';
import Setting from '@joplin/lib/models/Setting';
import getActivePluginEditorView from '@joplin/lib/services/plugins/utils/getActivePluginEditorView';
import { CommandContext, CommandDeclaration, CommandRuntime } from '../services/CommandService';
import { _ } from '../locale';
import Setting from '../models/Setting';
import getActivePluginEditorView from '../services/plugins/utils/getActivePluginEditorView';
import Logger from '@joplin/utils/Logger';
const logger = Logger.create('toggleEditorPlugin');
@@ -24,14 +24,26 @@ export const runtime = (): CommandRuntime => {
}
const idx = shownEditorViewIds.indexOf(editorView.id);
let hasBeenHidden = false;
if (idx < 0) {
shownEditorViewIds.push(editorView.id);
} else {
shownEditorViewIds.splice(idx, 1);
hasBeenHidden = true;
}
logger.info('New shown editor views: ', shownEditorViewIds);
Setting.setValue('plugins.shownEditorViewIds', shownEditorViewIds);
if (hasBeenHidden) {
// When the plugin editor goes from visible to hidden, we need to reload the note
// because it may have been changed via the data API.
context.dispatch({
type: 'EDITOR_NOTE_NEEDS_RELOAD',
});
}
},
};
};

View File

@@ -67,6 +67,8 @@ interface Shared {
installResourceHandling?: (refreshResourceHandler: any)=> void;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
uninstallResourceHandling?: (refreshResourceHandler: any)=> void;
reloadNote?: (comp: BaseNoteScreenComponent)=> Promise<NoteEntity>;
}
const shared: Shared = {};
@@ -268,7 +270,7 @@ shared.isModified = function(comp: BaseNoteScreenComponent) {
return !!Object.getOwnPropertyNames(diff).length;
};
shared.initState = async function(comp: BaseNoteScreenComponent) {
shared.reloadNote = async (comp: BaseNoteScreenComponent) => {
const isProvisionalNote = comp.props.provisionalNoteIds.includes(comp.props.noteId);
const note = await Note.load(comp.props.noteId);
@@ -292,6 +294,7 @@ shared.initState = async function(comp: BaseNoteScreenComponent) {
fromShare: !!comp.props.sharedData,
noteResources: await shared.attachedResources(note ? note.body : ''),
readOnly: itemIsReadOnlySync(ModelType.Note, ItemChange.SOURCE_UNSPECIFIED, note as ItemSlice, Setting.value('sync.userId'), BaseItem.syncShareCache),
noteLastLoadTime: Date.now(),
});
} else {
// Handle the case where a non-existent note is loaded. This can happen briefly after deleting a note.
@@ -304,9 +307,16 @@ shared.initState = async function(comp: BaseNoteScreenComponent) {
fromShare,
noteResources: [],
readOnly: true,
noteLastLoadTime: Date.now(),
});
}
return note;
};
shared.initState = async function(comp: BaseNoteScreenComponent) {
const note = await shared.reloadNote(comp);
if (comp.props.sharedData) {
if (comp.props.sharedData.title) {
this.noteComponent_change(comp, 'title', comp.props.sharedData.title);

View File

@@ -170,6 +170,7 @@ export interface State extends WindowState {
mustUpgradeAppMessage: string;
mustAuthenticate: boolean;
toast: Toast | null;
editorNoteReloadTimeRequest: number;
allowSelectionInOtherFolders: boolean;
@@ -241,6 +242,7 @@ export const defaultState: State = {
mustUpgradeAppMessage: '',
mustAuthenticate: false,
allowSelectionInOtherFolders: false,
editorNoteReloadTimeRequest: 0,
pluginService: pluginServiceDefaultState,
shareService: shareServiceDefaultState,
@@ -1512,6 +1514,12 @@ const reducer = produce((draft: Draft<State> = defaultState, action: any) => {
}
break;
case 'EDITOR_NOTE_NEEDS_RELOAD':
{
draft.editorNoteReloadTimeRequest = Date.now();
}
break;
case 'TOAST_SHOW':
draft.toast = {
duration: 6000,

View File

@@ -157,10 +157,13 @@ export default class WebviewController extends ViewController {
public emitUpdate() {
if (!this.updateListener_) return;
if (this.containerType_ === ContainerType.Editor && (!this.isActive() || !this.isVisible())) {
logger.info('emitMessage: Not emitting update because editor is disabled or hidden:', this.pluginId, this.handle, this.isActive(), this.isVisible());
return;
}
// TODO:
// if (this.containerType_ === ContainerType.Editor && (!this.isActive() || !this.isVisible())) {
// logger.info('emitMessage: Not emitting update because editor is disabled or hidden:', this.pluginId, this.handle, this.isActive(), this.isVisible());
// return;
// }
this.updateListener_();
}
@@ -279,7 +282,7 @@ export default class WebviewController extends ViewController {
return this.storeView.opened;
}
public async isVisible(): Promise<boolean> {
public isVisible(): boolean {
if (!this.storeView.opened) return false;
const shownEditorViewIds: string[] = this.store.getState().settings['plugins.shownEditorViewIds'];
return shownEditorViewIds.includes(this.handle);

View File

@@ -0,0 +1,10 @@
import { PluginStates } from '../reducer';
import getActivePluginEditorView from './getActivePluginEditorView';
export default (plugins: PluginStates, shownEditorViewIds: string[]) => {
const { editorPlugin, editorView } = getActivePluginEditorView(plugins);
if (editorView) {
if (!shownEditorViewIds.includes(editorView.id)) return { editorPlugin: null, editorView: null };
}
return { editorPlugin, editorView };
};

View File

@@ -7468,6 +7468,13 @@ __metadata:
languageName: node
linkType: hard
"@fortawesome/fontawesome-free@npm:^6.7.2":
version: 6.7.2
resolution: "@fortawesome/fontawesome-free@npm:6.7.2"
checksum: 2ceb384ada8e4a1e8a8e24384a35e3afa01589ddec67c8c52e3ad5d7db1662d0fc92560bd9a23baa4e0676e721e423aef99fb79fe6899bf13900fd1e611b6760
languageName: node
linkType: hard
"@fortawesome/fontawesome-svg-core@npm:6.1.2":
version: 6.1.2
resolution: "@fortawesome/fontawesome-svg-core@npm:6.1.2"
@@ -8355,6 +8362,7 @@ __metadata:
"@babel/preset-env": 7.24.7
"@babel/runtime": 7.24.7
"@bam.tech/react-native-image-resizer": 3.0.10
"@fortawesome/fontawesome-free": ^6.7.2
"@joplin/editor": ~3.3
"@joplin/lib": ~3.3
"@joplin/react-native-alarm-notification": ~3.3