You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-11-26 22:41:17 +02:00
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
|
import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
|
||||||
import Logger from '@joplin/utils/Logger';
|
import Logger from '@joplin/utils/Logger';
|
||||||
import goToNote from './util/goToNote';
|
import goToNote, { GotoNoteOptions } from './util/goToNote';
|
||||||
import Note from '@joplin/lib/models/Note';
|
import Note from '@joplin/lib/models/Note';
|
||||||
import Setting from '@joplin/lib/models/Setting';
|
import Setting from '@joplin/lib/models/Setting';
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ export const declaration: CommandDeclaration = {
|
|||||||
|
|
||||||
export const runtime = (): CommandRuntime => {
|
export const runtime = (): CommandRuntime => {
|
||||||
return {
|
return {
|
||||||
execute: async (_context: CommandContext, body = '', todo = false) => {
|
execute: async (_context: CommandContext, body = '', todo = false, options: GotoNoteOptions = null) => {
|
||||||
const folderId = Setting.value('activeFolderId');
|
const folderId = Setting.value('activeFolderId');
|
||||||
if (!folderId) {
|
if (!folderId) {
|
||||||
logger.warn('Not creating new note -- no active folder ID.');
|
logger.warn('Not creating new note -- no active folder ID.');
|
||||||
@@ -26,7 +26,7 @@ export const runtime = (): CommandRuntime => {
|
|||||||
}, { provisional: true });
|
}, { provisional: true });
|
||||||
|
|
||||||
logger.info(`Navigating to note ${note.id}`);
|
logger.info(`Navigating to note ${note.id}`);
|
||||||
await goToNote(note.id, '');
|
await goToNote(note.id, '', options);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,17 @@
|
|||||||
import Note from '@joplin/lib/models/Note';
|
import Note from '@joplin/lib/models/Note';
|
||||||
import NavService from '@joplin/lib/services/NavService';
|
import NavService from '@joplin/lib/services/NavService';
|
||||||
|
import { AttachFileAction } from '../../components/screens/Note/commands/attachFile';
|
||||||
|
|
||||||
|
export interface GotoNoteOptions {
|
||||||
|
attachFileAction?: AttachFileAction | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const goToNote = async (id: string, hash?: string, options: GotoNoteOptions = null) => {
|
||||||
|
options = {
|
||||||
|
attachFileAction: null,
|
||||||
|
...options,
|
||||||
|
};
|
||||||
|
|
||||||
const goToNote = async (id: string, hash?: string) => {
|
|
||||||
if (!(await Note.load(id))) {
|
if (!(await Note.load(id))) {
|
||||||
throw new Error(`No note with id ${id}`);
|
throw new Error(`No note with id ${id}`);
|
||||||
}
|
}
|
||||||
@@ -9,6 +19,7 @@ const goToNote = async (id: string, hash?: string) => {
|
|||||||
return NavService.go('Note', {
|
return NavService.go('Note', {
|
||||||
noteId: id,
|
noteId: id,
|
||||||
noteHash: hash,
|
noteHash: hash,
|
||||||
|
newNoteAttachFileAction: options.attachFileAction,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ import { CameraResult } from '../../CameraView/types';
|
|||||||
import { DialogContext, DialogControl } from '../../DialogManager';
|
import { DialogContext, DialogControl } from '../../DialogManager';
|
||||||
import { CommandRuntimeProps, EditorMode, PickerResponse } from './types';
|
import { CommandRuntimeProps, EditorMode, PickerResponse } from './types';
|
||||||
import commands from './commands';
|
import commands from './commands';
|
||||||
|
import { AttachFileAction, AttachFileOptions } from './commands/attachFile';
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||||
const emptyArray: any[] = [];
|
const emptyArray: any[] = [];
|
||||||
@@ -83,6 +84,7 @@ interface Props extends BaseProps {
|
|||||||
highlightedWords: string[];
|
highlightedWords: string[];
|
||||||
noteHash: string;
|
noteHash: string;
|
||||||
toolbarEnabled: boolean;
|
toolbarEnabled: boolean;
|
||||||
|
newNoteAttachFileAction: AttachFileAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ComponentProps extends Props {
|
interface ComponentProps extends Props {
|
||||||
@@ -523,6 +525,19 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
|
|||||||
// has already been granted, it doesn't slow down opening the note. If it hasn't
|
// has already been granted, it doesn't slow down opening the note. If it hasn't
|
||||||
// been granted, the popup will open anyway.
|
// been granted, the popup will open anyway.
|
||||||
void this.requestGeoLocationPermissions();
|
void this.requestGeoLocationPermissions();
|
||||||
|
|
||||||
|
if (this.props.newNoteAttachFileAction) {
|
||||||
|
setTimeout(async () => {
|
||||||
|
if (this.props.newNoteAttachFileAction === AttachFileAction.AttachDrawing) {
|
||||||
|
await this.drawPicture_onPress();
|
||||||
|
} else {
|
||||||
|
const options: AttachFileOptions = {
|
||||||
|
action: this.props.newNoteAttachFileAction,
|
||||||
|
};
|
||||||
|
await CommandService.instance().execute('attachFile', '', options);
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||||
@@ -1294,6 +1309,11 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
|
|||||||
done = true;
|
done = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.noteEditorVisible()) {
|
||||||
|
logger.info(`Note editor is not visible - not setting focus on ${fieldToFocus}`);
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (done) {
|
if (done) {
|
||||||
shim.clearInterval(this.focusUpdateIID_);
|
shim.clearInterval(this.focusUpdateIID_);
|
||||||
this.focusUpdateIID_ = null;
|
this.focusUpdateIID_ = null;
|
||||||
@@ -1375,6 +1395,10 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
|
|||||||
this.setState({ voiceTypingDialogShown: false });
|
this.setState({ voiceTypingDialogShown: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private noteEditorVisible() {
|
||||||
|
return !this.state.showCamera && !this.state.showImageEditor;
|
||||||
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
// Commands must be registered before child components can render.
|
// Commands must be registered before child components can render.
|
||||||
// Calling this in the constructor won't work in strict mode, where
|
// Calling this in the constructor won't work in strict mode, where
|
||||||
@@ -1611,6 +1635,7 @@ const NoteScreen = connect((state: AppState) => {
|
|||||||
return {
|
return {
|
||||||
noteId: state.selectedNoteIds.length ? state.selectedNoteIds[0] : null,
|
noteId: state.selectedNoteIds.length ? state.selectedNoteIds[0] : null,
|
||||||
noteHash: state.selectedNoteHash,
|
noteHash: state.selectedNoteHash,
|
||||||
|
newNoteAttachFileAction: state.newNoteAttachFileAction,
|
||||||
itemType: state.selectedItemType,
|
itemType: state.selectedItemType,
|
||||||
folders: state.folders,
|
folders: state.folders,
|
||||||
searchQuery: state.searchQuery,
|
searchQuery: state.searchQuery,
|
||||||
|
|||||||
@@ -8,6 +8,17 @@ import Logger from '@joplin/utils/Logger';
|
|||||||
|
|
||||||
const logger = Logger.create('attachFile');
|
const logger = Logger.create('attachFile');
|
||||||
|
|
||||||
|
export enum AttachFileAction {
|
||||||
|
TakePhoto = 'takePhoto',
|
||||||
|
AttachFile = 'attachFile',
|
||||||
|
AttachPhoto = 'attachPhoto',
|
||||||
|
AttachDrawing = 'attachDrawing',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AttachFileOptions {
|
||||||
|
action?: AttachFileAction | null;
|
||||||
|
}
|
||||||
|
|
||||||
export const declaration: CommandDeclaration = {
|
export const declaration: CommandDeclaration = {
|
||||||
name: 'attachFile',
|
name: 'attachFile',
|
||||||
label: () => _('Attach file'),
|
label: () => _('Attach file'),
|
||||||
@@ -50,35 +61,46 @@ export const runtime = (props: CommandRuntimeProps): CommandRuntime => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const showAttachMenu = async () => {
|
const showAttachMenu = async (action: AttachFileAction = null) => {
|
||||||
props.hideKeyboard();
|
props.hideKeyboard();
|
||||||
|
|
||||||
const buttons = [];
|
let buttonId: AttachFileAction = null;
|
||||||
|
|
||||||
// On iOS, it will show "local files", which means certain files saved from the browser
|
if (action) {
|
||||||
// and the iCloud files, but it doesn't include photos and images from the CameraRoll
|
buttonId = action;
|
||||||
//
|
} else {
|
||||||
// On Android, it will depend on the phone, but usually it will allow browsing all files and photos.
|
const buttons = [];
|
||||||
buttons.push({ text: _('Attach file'), id: 'attachFile' });
|
|
||||||
|
|
||||||
// Disabled on Android because it doesn't work due to permission issues, but enabled on iOS
|
// On iOS, it will show "local files", which means certain files saved from the browser
|
||||||
// because that's only way to browse photos from the camera roll.
|
// and the iCloud files, but it doesn't include photos and images from the CameraRoll
|
||||||
if (Platform.OS === 'ios') buttons.push({ text: _('Attach photo'), id: 'attachPhoto' });
|
//
|
||||||
buttons.push({ text: _('Take photo'), id: 'takePhoto' });
|
// On Android, it will depend on the phone, but usually it will allow browsing all files and photos.
|
||||||
|
buttons.push({ text: _('Attach file'), id: AttachFileAction.AttachFile });
|
||||||
|
|
||||||
const buttonId = await props.dialogs.showMenu(_('Choose an option'), buttons);
|
// Disabled on Android because it doesn't work due to permission issues, but enabled on iOS
|
||||||
|
// because that's only way to browse photos from the camera roll.
|
||||||
|
if (Platform.OS === 'ios') buttons.push({ text: _('Attach photo'), id: AttachFileAction.AttachPhoto });
|
||||||
|
buttons.push({ text: _('Take photo'), id: AttachFileAction.TakePhoto });
|
||||||
|
|
||||||
if (buttonId === 'takePhoto') await takePhoto();
|
buttonId = await props.dialogs.showMenu(_('Choose an option'), buttons) as AttachFileAction;
|
||||||
if (buttonId === 'attachFile') await attachFile();
|
}
|
||||||
if (buttonId === 'attachPhoto') await attachPhoto();
|
|
||||||
|
if (buttonId === AttachFileAction.TakePhoto) await takePhoto();
|
||||||
|
if (buttonId === AttachFileAction.AttachFile) await attachFile();
|
||||||
|
if (buttonId === AttachFileAction.AttachPhoto) await attachPhoto();
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
execute: async (_context: CommandContext, filePath?: string) => {
|
execute: async (_context: CommandContext, filePath?: string, options: AttachFileOptions = null) => {
|
||||||
|
options = {
|
||||||
|
action: null,
|
||||||
|
...options,
|
||||||
|
};
|
||||||
|
|
||||||
if (filePath) {
|
if (filePath) {
|
||||||
await props.attachFile({ uri: filePath }, 'all');
|
await props.attachFile({ uri: filePath }, 'all');
|
||||||
} else {
|
} else {
|
||||||
await showAttachMenu();
|
await showAttachMenu(options.action);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -354,6 +354,10 @@ const appReducer = (state = appDefaultState, action: any) => {
|
|||||||
newState.selectedNoteHash = action.noteHash;
|
newState.selectedNoteHash = action.noteHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ('newNoteAttachFileAction' in action) {
|
||||||
|
newState.newNoteAttachFileAction = action.newNoteAttachFileAction;
|
||||||
|
}
|
||||||
|
|
||||||
if ('sharedData' in action) {
|
if ('sharedData' in action) {
|
||||||
newState.sharedData = action.sharedData;
|
newState.sharedData = action.sharedData;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { Dispatch } from 'redux';
|
|||||||
import CommandService from '@joplin/lib/services/CommandService';
|
import CommandService from '@joplin/lib/services/CommandService';
|
||||||
import Logger from '@joplin/utils/Logger';
|
import Logger from '@joplin/utils/Logger';
|
||||||
import { DeviceEventEmitter } from 'react-native';
|
import { DeviceEventEmitter } from 'react-native';
|
||||||
|
import { GotoNoteOptions } from './commands/util/goToNote';
|
||||||
|
import { AttachFileAction } from './components/screens/Note/commands/attachFile';
|
||||||
|
|
||||||
const logger = Logger.create('setupQuickActions');
|
const logger = Logger.create('setupQuickActions');
|
||||||
|
|
||||||
@@ -19,9 +21,17 @@ export default async (dispatch: Dispatch) => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// List of iOS icons:
|
||||||
|
// https://github.com/EvanBacon/expo-quick-actions?tab=readme-ov-file#system-icons
|
||||||
|
//
|
||||||
|
// Note: on Android, anything beyond the fourth menu item appears to be ignored, at least on
|
||||||
|
// emulator.
|
||||||
QuickActions.setShortcutItems([
|
QuickActions.setShortcutItems([
|
||||||
{ type: 'New note', title: _('New note'), icon: 'Compose', userInfo },
|
{ type: 'newNote', title: _('New note'), icon: 'Compose', userInfo },
|
||||||
{ type: 'New to-do', title: _('New to-do'), icon: 'Add', userInfo },
|
{ type: 'newTodo', title: _('New to-do'), icon: 'Add', userInfo },
|
||||||
|
{ type: 'newPhoto', title: _('New photo'), icon: 'CapturePhoto', userInfo },
|
||||||
|
{ type: 'newResource', title: _('New attachment'), icon: 'Bookmark', userInfo },
|
||||||
|
{ type: 'newDrawing', title: _('New drawing'), icon: 'Favorite', userInfo },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -50,6 +60,18 @@ const quickActionHandler = (dispatch: Dispatch) => async (data: TData) => {
|
|||||||
dispatch({ type: 'NAV_BACK' });
|
dispatch({ type: 'NAV_BACK' });
|
||||||
dispatch({ type: 'SIDE_MENU_CLOSE' });
|
dispatch({ type: 'SIDE_MENU_CLOSE' });
|
||||||
|
|
||||||
const isTodo = data.type === 'New to-do' ? 1 : 0;
|
const isTodo = data.type === 'newTodo' ? 1 : 0;
|
||||||
await CommandService.instance().execute('newNote', '', isTodo);
|
const options: GotoNoteOptions = {
|
||||||
|
attachFileAction: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (data.type === 'newPhoto') {
|
||||||
|
options.attachFileAction = AttachFileAction.TakePhoto;
|
||||||
|
} else if (data.type === 'newResource') {
|
||||||
|
options.attachFileAction = AttachFileAction.AttachFile;
|
||||||
|
} else if (data.type === 'newDrawing') {
|
||||||
|
options.attachFileAction = AttachFileAction.AttachDrawing;
|
||||||
|
}
|
||||||
|
|
||||||
|
await CommandService.instance().execute('newNote', '', isTodo, options);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { defaultState } from '@joplin/lib/reducer';
|
import { defaultState } from '@joplin/lib/reducer';
|
||||||
import { AppState } from './types';
|
import { AppState } from './types';
|
||||||
|
|
||||||
|
|
||||||
export const DEFAULT_ROUTE = {
|
export const DEFAULT_ROUTE = {
|
||||||
type: 'NAV_GO',
|
type: 'NAV_GO',
|
||||||
routeName: 'Notes',
|
routeName: 'Notes',
|
||||||
@@ -10,7 +9,6 @@ export const DEFAULT_ROUTE = {
|
|||||||
|
|
||||||
const appDefaultState: AppState = {
|
const appDefaultState: AppState = {
|
||||||
smartFilterId: undefined,
|
smartFilterId: undefined,
|
||||||
...defaultState,
|
|
||||||
keyboardVisible: false,
|
keyboardVisible: false,
|
||||||
route: DEFAULT_ROUTE,
|
route: DEFAULT_ROUTE,
|
||||||
noteSelectionEnabled: false,
|
noteSelectionEnabled: false,
|
||||||
@@ -18,5 +16,7 @@ const appDefaultState: AppState = {
|
|||||||
isOnMobileData: false,
|
isOnMobileData: false,
|
||||||
disableSideMenuGestures: false,
|
disableSideMenuGestures: false,
|
||||||
showPanelsDialog: false,
|
showPanelsDialog: false,
|
||||||
|
newNoteAttachFileAction: null,
|
||||||
|
...defaultState,
|
||||||
};
|
};
|
||||||
export default appDefaultState;
|
export default appDefaultState;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { State } from '@joplin/lib/reducer';
|
import { State } from '@joplin/lib/reducer';
|
||||||
|
import { AttachFileAction } from '../components/screens/Note/commands/attachFile';
|
||||||
|
|
||||||
export interface AppState extends State {
|
export interface AppState extends State {
|
||||||
showPanelsDialog: boolean;
|
showPanelsDialog: boolean;
|
||||||
@@ -10,4 +11,6 @@ export interface AppState extends State {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||||
noteSideMenuOptions: any;
|
noteSideMenuOptions: any;
|
||||||
disableSideMenuGestures: boolean;
|
disableSideMenuGestures: boolean;
|
||||||
|
|
||||||
|
newNoteAttachFileAction: AttachFileAction | null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -147,5 +147,6 @@ setsize
|
|||||||
Comprar
|
Comprar
|
||||||
seguidores
|
seguidores
|
||||||
devbox
|
devbox
|
||||||
tablist
|
|
||||||
Ökonomie
|
Ökonomie
|
||||||
|
Favorite
|
||||||
|
tablist
|
||||||
|
|||||||
Reference in New Issue
Block a user