feat: You can now share files & text from other apps into SN on iOS (#2358)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { ReactNativeToWebEvent } from '@standardnotes/snjs'
|
||||
import { RefObject } from 'react'
|
||||
import { AppState, NativeEventSubscription, NativeModules, Platform } from 'react-native'
|
||||
import { AppState, Linking, NativeEventSubscription, NativeModules, Platform } from 'react-native'
|
||||
import { readFile } from 'react-native-fs'
|
||||
import WebView from 'react-native-webview'
|
||||
const { ReceiveSharingIntent } = NativeModules
|
||||
@@ -13,13 +13,19 @@ type ReceivedItem = {
|
||||
text?: string | null
|
||||
weblink?: string | null
|
||||
subject?: string | null
|
||||
path?: string | null
|
||||
}
|
||||
|
||||
type ReceivedFile = ReceivedItem & {
|
||||
type ReceivedAndroidFile = ReceivedItem & {
|
||||
contentUri: string
|
||||
mimeType: string
|
||||
}
|
||||
|
||||
type ReceivedIosFile = ReceivedItem & {
|
||||
path: string
|
||||
mimeType: string
|
||||
}
|
||||
|
||||
type ReceivedWeblink = ReceivedItem & {
|
||||
weblink: string
|
||||
}
|
||||
@@ -28,10 +34,14 @@ type ReceivedText = ReceivedItem & {
|
||||
text: string
|
||||
}
|
||||
|
||||
const isReceivedFile = (item: ReceivedItem): item is ReceivedFile => {
|
||||
const isReceivedAndroidFile = (item: ReceivedItem): item is ReceivedAndroidFile => {
|
||||
return !!item.contentUri && !!item.mimeType
|
||||
}
|
||||
|
||||
const isReceivedIosFile = (item: ReceivedItem): item is ReceivedIosFile => {
|
||||
return !!item.path
|
||||
}
|
||||
|
||||
const isReceivedWeblink = (item: ReceivedItem): item is ReceivedWeblink => {
|
||||
return !!item.weblink
|
||||
}
|
||||
@@ -40,15 +50,16 @@ const isReceivedText = (item: ReceivedItem): item is ReceivedText => {
|
||||
return !!item.text
|
||||
}
|
||||
|
||||
const BundleIdentifier = 'com.standardnotes.standardnotes'
|
||||
const IosUrlToCheckFor = `${BundleIdentifier}://dataUrl`
|
||||
|
||||
export class ReceivedSharedItemsHandler {
|
||||
private appStateEventSub: NativeEventSubscription | null = null
|
||||
private eventSub: NativeEventSubscription | null = null
|
||||
private receivedItemsQueue: ReceivedItem[] = []
|
||||
private isApplicationLaunched = false
|
||||
|
||||
constructor(private webViewRef: RefObject<WebView>) {
|
||||
if (Platform.OS === 'android') {
|
||||
this.registerNativeEventSub()
|
||||
}
|
||||
this.registerNativeEventSub()
|
||||
}
|
||||
|
||||
setIsApplicationLaunched = (isApplicationLaunched: boolean) => {
|
||||
@@ -61,23 +72,29 @@ export class ReceivedSharedItemsHandler {
|
||||
|
||||
deinit() {
|
||||
this.receivedItemsQueue = []
|
||||
this.appStateEventSub?.remove()
|
||||
this.eventSub?.remove()
|
||||
}
|
||||
|
||||
private registerNativeEventSub = () => {
|
||||
this.appStateEventSub = AppState.addEventListener('change', (state) => {
|
||||
if (state === 'active') {
|
||||
ReceiveSharingIntent.getFileNames()
|
||||
.then(async (filesObject: Record<string, ReceivedItem>) => {
|
||||
const items = Object.values(filesObject)
|
||||
this.receivedItemsQueue.push(...items)
|
||||
if (Platform.OS === 'ios') {
|
||||
Linking.getInitialURL()
|
||||
.then((url) => {
|
||||
if (url && url.startsWith(IosUrlToCheckFor)) {
|
||||
this.addSharedItemsToQueue(url)
|
||||
}
|
||||
})
|
||||
.catch(console.error)
|
||||
this.eventSub = Linking.addEventListener('url', ({ url }) => {
|
||||
if (url && url.startsWith(IosUrlToCheckFor)) {
|
||||
this.addSharedItemsToQueue(url)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (this.isApplicationLaunched) {
|
||||
this.handleItemsQueue().catch(console.error)
|
||||
}
|
||||
})
|
||||
.then(() => ReceiveSharingIntent.clearFileNames())
|
||||
.catch(console.error)
|
||||
this.eventSub = AppState.addEventListener('change', (state) => {
|
||||
if (state === 'active') {
|
||||
this.addSharedItemsToQueue()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -92,7 +109,7 @@ export class ReceivedSharedItemsHandler {
|
||||
return
|
||||
}
|
||||
|
||||
if (isReceivedFile(item)) {
|
||||
if (isReceivedAndroidFile(item)) {
|
||||
const data = await readFile(item.contentUri, 'base64')
|
||||
const file = {
|
||||
name: item.fileName || item.contentUri,
|
||||
@@ -106,6 +123,20 @@ export class ReceivedSharedItemsHandler {
|
||||
messageData: file,
|
||||
}),
|
||||
)
|
||||
} else if (isReceivedIosFile(item)) {
|
||||
const data = await readFile(item.path, 'base64')
|
||||
const file = {
|
||||
name: item.fileName || item.path,
|
||||
data,
|
||||
mimeType: item.mimeType,
|
||||
}
|
||||
this.webViewRef.current?.postMessage(
|
||||
JSON.stringify({
|
||||
reactNativeEvent: ReactNativeToWebEvent.ReceivedFile,
|
||||
messageType: 'event',
|
||||
messageData: file,
|
||||
}),
|
||||
)
|
||||
} else if (isReceivedWeblink(item)) {
|
||||
this.webViewRef.current?.postMessage(
|
||||
JSON.stringify({
|
||||
@@ -131,4 +162,49 @@ export class ReceivedSharedItemsHandler {
|
||||
|
||||
this.handleItemsQueue().catch(console.error)
|
||||
}
|
||||
|
||||
private addSharedItemsToQueue = (url?: string) => {
|
||||
ReceiveSharingIntent.getFileNames(url)
|
||||
.then(async (received: unknown) => {
|
||||
if (!received) {
|
||||
return
|
||||
}
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
const items = Object.values(received as Record<string, ReceivedItem>)
|
||||
this.receivedItemsQueue.push(...items)
|
||||
} else if (typeof received === 'string') {
|
||||
const parsed: unknown = JSON.parse(received)
|
||||
if (typeof parsed !== 'object') {
|
||||
return
|
||||
}
|
||||
if (!parsed) {
|
||||
return
|
||||
}
|
||||
if ('media' in parsed && Array.isArray(parsed.media)) {
|
||||
this.receivedItemsQueue.push(...parsed.media)
|
||||
}
|
||||
if ('text' in parsed && Array.isArray(parsed.text)) {
|
||||
this.receivedItemsQueue.push(
|
||||
...parsed.text.map((text: string) => ({
|
||||
text: text,
|
||||
})),
|
||||
)
|
||||
}
|
||||
if ('urls' in parsed && Array.isArray(parsed.urls)) {
|
||||
this.receivedItemsQueue.push(
|
||||
...parsed.urls.map((url: string) => ({
|
||||
weblink: url,
|
||||
})),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isApplicationLaunched) {
|
||||
this.handleItemsQueue().catch(console.error)
|
||||
}
|
||||
})
|
||||
.then(() => ReceiveSharingIntent.clearFileNames())
|
||||
.catch(console.error)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user