1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-21 09:38:01 +02:00

Mobile: Resolves #10374: Add more options when long pressing the icon on mobile (#11517)

This commit is contained in:
Laurent Cozic 2024-12-16 10:49:46 +01:00 committed by GitHub
parent dc96811940
commit 3983a3a52f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 116 additions and 28 deletions

View File

@ -1,6 +1,6 @@
import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
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 Setting from '@joplin/lib/models/Setting';
@ -12,7 +12,7 @@ export const declaration: CommandDeclaration = {
export const runtime = (): CommandRuntime => {
return {
execute: async (_context: CommandContext, body = '', todo = false) => {
execute: async (_context: CommandContext, body = '', todo = false, options: GotoNoteOptions = null) => {
const folderId = Setting.value('activeFolderId');
if (!folderId) {
logger.warn('Not creating new note -- no active folder ID.');
@ -26,7 +26,7 @@ export const runtime = (): CommandRuntime => {
}, { provisional: true });
logger.info(`Navigating to note ${note.id}`);
await goToNote(note.id, '');
await goToNote(note.id, '', options);
},
};
};

View File

@ -1,7 +1,17 @@
import Note from '@joplin/lib/models/Note';
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))) {
throw new Error(`No note with id ${id}`);
}
@ -9,6 +19,7 @@ const goToNote = async (id: string, hash?: string) => {
return NavService.go('Note', {
noteId: id,
noteHash: hash,
newNoteAttachFileAction: options.attachFileAction,
});
};

View File

@ -62,6 +62,7 @@ import { CameraResult } from '../../CameraView/types';
import { DialogContext, DialogControl } from '../../DialogManager';
import { CommandRuntimeProps, EditorMode, PickerResponse } from './types';
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
const emptyArray: any[] = [];
@ -83,6 +84,7 @@ interface Props extends BaseProps {
highlightedWords: string[];
noteHash: string;
toolbarEnabled: boolean;
newNoteAttachFileAction: AttachFileAction;
}
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
// been granted, the popup will open anyway.
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
@ -1294,6 +1309,11 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
done = true;
}
if (!this.noteEditorVisible()) {
logger.info(`Note editor is not visible - not setting focus on ${fieldToFocus}`);
done = true;
}
if (done) {
shim.clearInterval(this.focusUpdateIID_);
this.focusUpdateIID_ = null;
@ -1375,6 +1395,10 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
this.setState({ voiceTypingDialogShown: false });
}
private noteEditorVisible() {
return !this.state.showCamera && !this.state.showImageEditor;
}
public render() {
// Commands must be registered before child components can render.
// Calling this in the constructor won't work in strict mode, where
@ -1611,6 +1635,7 @@ const NoteScreen = connect((state: AppState) => {
return {
noteId: state.selectedNoteIds.length ? state.selectedNoteIds[0] : null,
noteHash: state.selectedNoteHash,
newNoteAttachFileAction: state.newNoteAttachFileAction,
itemType: state.selectedItemType,
folders: state.folders,
searchQuery: state.searchQuery,

View File

@ -8,6 +8,17 @@ import Logger from '@joplin/utils/Logger';
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 = {
name: 'attachFile',
label: () => _('Attach file'),
@ -50,35 +61,46 @@ export const runtime = (props: CommandRuntimeProps): CommandRuntime => {
}
};
const showAttachMenu = async () => {
const showAttachMenu = async (action: AttachFileAction = null) => {
props.hideKeyboard();
const buttons = [];
let buttonId: AttachFileAction = null;
// On iOS, it will show "local files", which means certain files saved from the browser
// and the iCloud files, but it doesn't include photos and images from the CameraRoll
//
// On Android, it will depend on the phone, but usually it will allow browsing all files and photos.
buttons.push({ text: _('Attach file'), id: 'attachFile' });
if (action) {
buttonId = action;
} else {
const 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: 'attachPhoto' });
buttons.push({ text: _('Take photo'), id: 'takePhoto' });
// On iOS, it will show "local files", which means certain files saved from the browser
// and the iCloud files, but it doesn't include photos and images from the CameraRoll
//
// 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();
if (buttonId === 'attachFile') await attachFile();
if (buttonId === 'attachPhoto') await attachPhoto();
buttonId = await props.dialogs.showMenu(_('Choose an option'), buttons) as AttachFileAction;
}
if (buttonId === AttachFileAction.TakePhoto) await takePhoto();
if (buttonId === AttachFileAction.AttachFile) await attachFile();
if (buttonId === AttachFileAction.AttachPhoto) await attachPhoto();
};
return {
execute: async (_context: CommandContext, filePath?: string) => {
execute: async (_context: CommandContext, filePath?: string, options: AttachFileOptions = null) => {
options = {
action: null,
...options,
};
if (filePath) {
await props.attachFile({ uri: filePath }, 'all');
} else {
await showAttachMenu();
await showAttachMenu(options.action);
}
},

View File

@ -354,6 +354,10 @@ const appReducer = (state = appDefaultState, action: any) => {
newState.selectedNoteHash = action.noteHash;
}
if ('newNoteAttachFileAction' in action) {
newState.newNoteAttachFileAction = action.newNoteAttachFileAction;
}
if ('sharedData' in action) {
newState.sharedData = action.sharedData;
} else {

View File

@ -4,6 +4,8 @@ import { Dispatch } from 'redux';
import CommandService from '@joplin/lib/services/CommandService';
import Logger from '@joplin/utils/Logger';
import { DeviceEventEmitter } from 'react-native';
import { GotoNoteOptions } from './commands/util/goToNote';
import { AttachFileAction } from './components/screens/Note/commands/attachFile';
const logger = Logger.create('setupQuickActions');
@ -19,9 +21,17 @@ export default async (dispatch: Dispatch) => {
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([
{ type: 'New note', title: _('New note'), icon: 'Compose', userInfo },
{ type: 'New to-do', title: _('New to-do'), icon: 'Add', userInfo },
{ type: 'newNote', title: _('New note'), icon: 'Compose', 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 {
@ -50,6 +60,18 @@ const quickActionHandler = (dispatch: Dispatch) => async (data: TData) => {
dispatch({ type: 'NAV_BACK' });
dispatch({ type: 'SIDE_MENU_CLOSE' });
const isTodo = data.type === 'New to-do' ? 1 : 0;
await CommandService.instance().execute('newNote', '', isTodo);
const isTodo = data.type === 'newTodo' ? 1 : 0;
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);
};

View File

@ -1,7 +1,6 @@
import { defaultState } from '@joplin/lib/reducer';
import { AppState } from './types';
export const DEFAULT_ROUTE = {
type: 'NAV_GO',
routeName: 'Notes',
@ -10,7 +9,6 @@ export const DEFAULT_ROUTE = {
const appDefaultState: AppState = {
smartFilterId: undefined,
...defaultState,
keyboardVisible: false,
route: DEFAULT_ROUTE,
noteSelectionEnabled: false,
@ -18,5 +16,7 @@ const appDefaultState: AppState = {
isOnMobileData: false,
disableSideMenuGestures: false,
showPanelsDialog: false,
newNoteAttachFileAction: null,
...defaultState,
};
export default appDefaultState;

View File

@ -1,4 +1,5 @@
import { State } from '@joplin/lib/reducer';
import { AttachFileAction } from '../components/screens/Note/commands/attachFile';
export interface AppState extends State {
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
noteSideMenuOptions: any;
disableSideMenuGestures: boolean;
newNoteAttachFileAction: AttachFileAction | null;
}

View File

@ -147,5 +147,6 @@ setsize
Comprar
seguidores
devbox
tablist
Ökonomie
Favorite
tablist