feat: On Android, you can now share text & files from other apps directly into Standard Notes (#2352)
This commit is contained in:
@@ -24,11 +24,13 @@ import {
|
||||
BackupServiceInterface,
|
||||
InternalFeatureService,
|
||||
InternalFeatureServiceInterface,
|
||||
NoteContent,
|
||||
SNNote,
|
||||
} from '@standardnotes/snjs'
|
||||
import { makeObservable, observable } from 'mobx'
|
||||
import { startAuthentication, startRegistration } from '@simplewebauthn/browser'
|
||||
import { PanelResizedData } from '@/Types/PanelResizedData'
|
||||
import { isAndroid, isDesktopApplication, isDev, isIOS } from '@/Utils'
|
||||
import { getBlobFromBase64, isAndroid, isDesktopApplication, isDev, isIOS } from '@/Utils'
|
||||
import { DesktopManager } from './Device/DesktopManager'
|
||||
import {
|
||||
ArchiveManager,
|
||||
@@ -66,6 +68,7 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
||||
private readonly mobileWebReceiver?: MobileWebReceiver
|
||||
private readonly androidBackHandler?: AndroidBackHandler
|
||||
private readonly visibilityObserver?: VisibilityObserver
|
||||
private readonly mobileAppEventObserver?: () => void
|
||||
|
||||
public readonly devMode?: DevMode
|
||||
|
||||
@@ -137,6 +140,9 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
||||
if (this.isNativeMobileWeb()) {
|
||||
this.mobileWebReceiver = new MobileWebReceiver(this)
|
||||
this.androidBackHandler = new AndroidBackHandler()
|
||||
this.mobileAppEventObserver = this.addEventObserver(async (event) => {
|
||||
this.mobileDevice().notifyApplicationEvent(event)
|
||||
})
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log = (...args) => {
|
||||
@@ -186,6 +192,11 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
||||
this.visibilityObserver.deinit()
|
||||
;(this.visibilityObserver as unknown) = undefined
|
||||
}
|
||||
|
||||
if (this.mobileAppEventObserver) {
|
||||
this.mobileAppEventObserver()
|
||||
;(this.mobileAppEventObserver as unknown) = undefined
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error while deiniting application', error)
|
||||
}
|
||||
@@ -376,6 +387,27 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
||||
this.notifyWebEvent(WebAppEvent.MobileKeyboardDidChangeFrame, frame)
|
||||
}
|
||||
|
||||
handleReceivedFileEvent(file: { name: string; mimeType: string; data: string }): void {
|
||||
const filesController = this.getViewControllerManager().filesController
|
||||
const blob = getBlobFromBase64(file.data, file.mimeType)
|
||||
const mappedFile = new File([blob], file.name, { type: file.mimeType })
|
||||
void filesController.uploadNewFile(mappedFile, true)
|
||||
}
|
||||
|
||||
async handleReceivedTextEvent({ text, title }: { text: string; title?: string | undefined }) {
|
||||
const titleForNote = title || this.getViewControllerManager().itemListController.titleForNewNote()
|
||||
|
||||
const note = this.items.createTemplateItem<NoteContent, SNNote>(ContentType.Note, {
|
||||
title: titleForNote,
|
||||
text: text,
|
||||
references: [],
|
||||
})
|
||||
|
||||
const insertedNote = await this.mutator.insertItem(note)
|
||||
|
||||
this.getViewControllerManager().selectionController.selectItem(insertedNote.uuid, true).catch(console.error)
|
||||
}
|
||||
|
||||
private async lockApplicationAfterMobileEventIfApplicable(): Promise<void> {
|
||||
const isLocked = await this.isLocked()
|
||||
if (isLocked) {
|
||||
|
||||
@@ -83,6 +83,18 @@ export class MobileWebReceiver {
|
||||
messageData as { height: number; contentHeight: number },
|
||||
)
|
||||
break
|
||||
case ReactNativeToWebEvent.ReceivedFile:
|
||||
void this.application.handleReceivedFileEvent(
|
||||
messageData as {
|
||||
name: string
|
||||
mimeType: string
|
||||
data: string
|
||||
},
|
||||
)
|
||||
break
|
||||
case ReactNativeToWebEvent.ReceivedText:
|
||||
void this.application.handleReceivedTextEvent(messageData as { text: string; title?: string })
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
|
||||
@@ -229,3 +229,23 @@ export const getBase64FromBlob = (blob: Blob) => {
|
||||
reader.readAsDataURL(blob)
|
||||
})
|
||||
}
|
||||
|
||||
export const getBlobFromBase64 = (b64Data: string, contentType = '', sliceSize = 512) => {
|
||||
const byteCharacters = atob(b64Data)
|
||||
const byteArrays = []
|
||||
|
||||
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
|
||||
const slice = byteCharacters.slice(offset, offset + sliceSize)
|
||||
|
||||
const byteNumbers = new Array(slice.length)
|
||||
for (let i = 0; i < slice.length; i++) {
|
||||
byteNumbers[i] = slice.charCodeAt(i)
|
||||
}
|
||||
|
||||
const byteArray = new Uint8Array(byteNumbers)
|
||||
byteArrays.push(byteArray)
|
||||
}
|
||||
|
||||
const blob = new Blob(byteArrays, { type: contentType })
|
||||
return blob
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user