From 6b2381767a1339f1df1120f8f6021f8b6197fa53 Mon Sep 17 00:00:00 2001
From: Tib Teng <661892+tiberiusteng@users.noreply.github.com>
Date: Tue, 30 Jan 2024 15:21:49 +0900
Subject: [PATCH] Mobile: resolves #8639: implement callback url on android
---
.../android/app/src/main/AndroidManifest.xml | 9 ++++
.../app-mobile/components/screens/Note.tsx | 12 +++++
.../app-mobile/components/screens/Notes.tsx | 29 ++++++++++-
packages/app-mobile/root.tsx | 52 +++++++++++++++++++
4 files changed, 100 insertions(+), 2 deletions(-)
diff --git a/packages/app-mobile/android/app/src/main/AndroidManifest.xml b/packages/app-mobile/android/app/src/main/AndroidManifest.xml
index 3972acaf8a3..60022c03124 100644
--- a/packages/app-mobile/android/app/src/main/AndroidManifest.xml
+++ b/packages/app-mobile/android/app/src/main/AndroidManifest.xml
@@ -92,6 +92,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/packages/app-mobile/components/screens/Note.tsx b/packages/app-mobile/components/screens/Note.tsx
index 57f8922a0e7..5986dd2027e 100644
--- a/packages/app-mobile/components/screens/Note.tsx
+++ b/packages/app-mobile/components/screens/Note.tsx
@@ -57,6 +57,7 @@ import { join } from 'path';
import { Dispatch } from 'redux';
import { RefObject } from 'react';
import { SelectionRange } from '../NoteEditor/types';
+import { getNoteCallbackUrl } from '@joplin/lib/callbackUrlUtils';
const urlUtils = require('@joplin/lib/urlUtils');
const emptyArray: any[] = [];
@@ -1098,6 +1099,11 @@ class NoteScreenComponent extends BaseScreenComponent implements B
Clipboard.setString(Note.markdownTag(note));
}
+ private copyExternalLink_onPress() {
+ const note = this.state.note;
+ Clipboard.setString(getNoteCallbackUrl(note.id));
+ }
+
public sideMenuOptions() {
const note = this.state.note;
if (!note) return [];
@@ -1305,6 +1311,12 @@ class NoteScreenComponent extends BaseScreenComponent implements B
this.copyMarkdownLink_onPress();
},
});
+ output.push({
+ title: _('Copy external link'),
+ onPress: () => {
+ this.copyExternalLink_onPress();
+ },
+ });
}
output.push({
title: _('Properties'),
diff --git a/packages/app-mobile/components/screens/Notes.tsx b/packages/app-mobile/components/screens/Notes.tsx
index 28869f0d488..6042feb8da9 100644
--- a/packages/app-mobile/components/screens/Notes.tsx
+++ b/packages/app-mobile/components/screens/Notes.tsx
@@ -8,7 +8,7 @@ import Tag from '@joplin/lib/models/Tag';
import Note from '@joplin/lib/models/Note';
import Setting from '@joplin/lib/models/Setting';
const { themeStyle } = require('../global-style.js');
-import { ScreenHeader } from '../ScreenHeader';
+import { ScreenHeader, MenuOptionType } from '../ScreenHeader';
import { _ } from '@joplin/lib/locale';
import ActionButton from '../ActionButton';
const { dialogs } = require('../../utils/dialogs.js');
@@ -18,6 +18,8 @@ const { BackButtonService } = require('../../services/back-button.js');
import { AppState } from '../../utils/types';
import { NoteEntity } from '@joplin/lib/services/database/types';
const { ALL_NOTES_FILTER_ID } = require('@joplin/lib/reserved-ids.js');
+const Clipboard = require('@react-native-community/clipboard').default;
+import { getFolderCallbackUrl, getTagCallbackUrl } from '@joplin/lib/callbackUrlUtils';
class NotesScreenComponent extends BaseScreenComponent {
@@ -197,6 +199,29 @@ class NotesScreenComponent extends BaseScreenComponent {
return this.folderPickerOptions_;
}
+ public menuOptions() {
+ const output: MenuOptionType[] = [];
+
+ if (this.props.notesParentType === 'Tag') {
+ output.push({
+ title: _('Copy external link'),
+ onPress: () => {
+ Clipboard.setString(getTagCallbackUrl(this.props.selectedTagId));
+ },
+ });
+ }
+ if (this.props.notesParentType === 'Folder') {
+ output.push({
+ title: _('Copy external link'),
+ onPress: () => {
+ Clipboard.setString(getFolderCallbackUrl(this.props.selectedFolderId));
+ },
+ });
+ }
+
+ return output;
+ }
+
public render() {
const parent = this.parentItem();
const theme = themeStyle(this.props.themeId);
@@ -285,7 +310,7 @@ class NotesScreenComponent extends BaseScreenComponent {
accessibilityElementsHidden={accessibilityHidden}
importantForAccessibility={accessibilityHidden ? 'no-hide-descendants' : undefined}
>
-
+
{actionButtonComp}
new Promise(res => res);
+ private callbackUrl: string|null = null;
public constructor() {
super();
@@ -794,6 +796,14 @@ class AppComponent extends React.Component {
logger.info('Sharing: handleOpenURL_: Processing share data');
void this.handleShareData();
}
+
+ if (isCallbackUrl(event.url)) {
+ logger.info('received callback url: ', event.url);
+ this.callbackUrl = event.url;
+ if (this.props.biometricsDone) {
+ void this.handleCallbackUrl();
+ }
+ }
};
this.handleNewShare_ = () => {
@@ -952,6 +962,9 @@ class AppComponent extends React.Component {
if (this.props.biometricsDone !== prevProps.biometricsDone && this.props.biometricsDone) {
logger.info('Sharing: componentDidUpdate: biometricsDone');
void this.handleShareData();
+ if (this.callbackUrl !== null) {
+ void this.handleCallbackUrl();
+ }
}
}
@@ -992,6 +1005,45 @@ class AppComponent extends React.Component {
}
}
+ private async handleCallbackUrl() {
+ const url = this.callbackUrl;
+ this.callbackUrl = null;
+ if (url === null) {
+ return;
+ }
+
+ const { command, params } = parseCallbackUrl(url);
+
+ switch (command) {
+
+ case CallbackUrlCommand.OpenNote:
+ this.props.dispatch({
+ type: 'NAV_GO',
+ routeName: 'Note',
+ noteId: params.id,
+ });
+ break;
+
+ case CallbackUrlCommand.OpenTag:
+ this.props.dispatch({
+ type: 'NAV_GO',
+ routeName: 'Notes',
+ tagId: params.id,
+ });
+ break;
+
+ case CallbackUrlCommand.OpenFolder:
+ this.props.dispatch({
+ type: 'NAV_GO',
+ routeName: 'Notes',
+ folderId: params.id,
+ });
+ break;
+
+ }
+ }
+
+
private async handleScreenWidthChange_() {
this.setState({ sideMenuWidth: this.getSideMenuWidth() });
}