fix: downloading backups in mobile webview (#1703)

This commit is contained in:
Aman Harwara
2022-09-30 21:33:30 +05:30
committed by GitHub
parent 1c530ab534
commit 5f24e7285e
10 changed files with 138 additions and 100 deletions

View File

@@ -16,8 +16,8 @@ import { formatDateForContextMenu } from '@/Utils/DateUtils'
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
import { getNoteBlob, getNoteFileName } from '@/Utils/NoteExportUtils'
import { shareSelectedItems } from '@/NativeMobileWeb/ShareSelectedItems'
import { downloadSelectedItemsOnAndroid } from '@/NativeMobileWeb/DownloadSelectedItemsOnAndroid'
import { shareSelectedNotes } from '@/NativeMobileWeb/ShareSelectedNotes'
import { downloadSelectedNotesOnAndroid } from '@/NativeMobileWeb/DownloadSelectedNotesOnAndroid'
type DeletePermanentlyButtonProps = {
onClick: () => void
@@ -355,7 +355,7 @@ const NotesOptions = ({
<button
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none"
onClick={() => {
application.isNativeMobileWeb() ? shareSelectedItems(application, notes) : downloadSelectedItems()
application.isNativeMobileWeb() ? shareSelectedNotes(application, notes) : downloadSelectedItems()
}}
>
<Icon type={application.platform === Platform.Android ? 'share' : 'download'} className={iconClass} />
@@ -364,7 +364,7 @@ const NotesOptions = ({
{application.platform === Platform.Android && (
<button
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none"
onClick={() => downloadSelectedItemsOnAndroid(application, notes)}
onClick={() => downloadSelectedNotesOnAndroid(application, notes)}
>
<Icon type="download" className={iconClass} />
Export

View File

@@ -1,5 +1,5 @@
import { isDesktopApplication } from '@/Utils'
import { alertDialog } from '@standardnotes/ui-services'
import { alertDialog, sanitizeFileName } from '@standardnotes/ui-services'
import {
STRING_IMPORT_SUCCESS,
STRING_INVALID_IMPORT_FILE,
@@ -21,6 +21,7 @@ import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup'
import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment'
import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator'
import Spinner from '@/Components/Spinner/Spinner'
import { downloadOrShareBlobBasedOnPlatform } from '@/Utils/DownloadOrShareBasedOnPlatform'
type Props = {
application: WebApplication
@@ -59,8 +60,31 @@ const DataBackups = ({ application, viewControllerManager }: Props) => {
refreshEncryptionStatus()
}, [refreshEncryptionStatus])
const downloadDataArchive = () => {
application.getArchiveService().downloadBackup(isBackupEncrypted).catch(console.error)
const downloadDataArchive = async () => {
const data = isBackupEncrypted
? await application.createEncryptedBackupFile()
: await application.createDecryptedBackupFile()
if (!data) {
return
}
const blobData = new Blob([JSON.stringify(data, null, 2)], {
type: 'text/json',
})
if (isBackupEncrypted) {
const filename = `Standard Notes Encrypted Backup and Import File - ${application
.getArchiveService()
.formattedDateForExports()}`
const sanitizedFilename = sanitizeFileName(filename) + '.txt'
downloadOrShareBlobBasedOnPlatform(application, blobData, sanitizedFilename)
} else {
const zippedDecryptedItemsBlob = await application.getArchiveService().getZippedDecryptedItemsBlob(data)
const filename = `Standard Notes Backup - ${application.getArchiveService().formattedDateForExports()}`
const sanitizedFilename = sanitizeFileName(filename) + '.zip'
downloadOrShareBlobBasedOnPlatform(application, zippedDecryptedItemsBlob, sanitizedFilename)
}
}
const readFile = async (file: File): Promise<any> => {

View File

@@ -0,0 +1,28 @@
import { WebApplication } from '@/Application/Application'
import { getBase64FromBlob } from '@/Utils'
import { Platform } from '@standardnotes/snjs'
import { addToast, ToastType, dismissToast } from '@standardnotes/toast'
export const downloadBlobOnAndroid = async (application: WebApplication, blob: Blob, filename: string) => {
if (!application.isNativeMobileWeb() || application.platform !== Platform.Android) {
throw new Error('Download function being used on non-android platform')
}
const loadingToastId = addToast({
type: ToastType.Loading,
message: `Downloading ${filename}..`,
})
const base64 = await getBase64FromBlob(blob)
const downloaded = await application.mobileDevice.downloadBase64AsFile(base64, filename)
if (downloaded) {
dismissToast(loadingToastId)
addToast({
type: ToastType.Success,
message: `Downloaded ${filename}`,
})
} else {
addToast({
type: ToastType.Error,
message: `Could not download ${filename}`,
})
}
}

View File

@@ -1,67 +0,0 @@
import { WebApplication } from '@/Application/Application'
import { getBase64FromBlob } from '@/Utils'
import { getNoteBlob, getNoteFileName } from '@/Utils/NoteExportUtils'
import { parseFileName } from '@standardnotes/filepicker'
import { Platform, SNNote } from '@standardnotes/snjs'
import { addToast, dismissToast, ToastType } from '@standardnotes/toast'
import { sanitizeFileName } from '@standardnotes/ui-services'
export const downloadSelectedItemsOnAndroid = async (application: WebApplication, notes: SNNote[]) => {
if (!application.isNativeMobileWeb() || application.platform !== Platform.Android) {
throw new Error('Function being used on non-android platform')
}
if (notes.length === 1) {
const note = notes[0]
const blob = getNoteBlob(application, note)
const base64 = await getBase64FromBlob(blob)
const { name, ext } = parseFileName(getNoteFileName(application, note))
const filename = `${sanitizeFileName(name)}.${ext}`
const loadingToastId = addToast({
type: ToastType.Loading,
message: `Exporting ${filename}..`,
})
const downloaded = await application.mobileDevice.downloadBase64AsFile(base64, filename)
if (downloaded) {
dismissToast(loadingToastId)
addToast({
type: ToastType.Success,
message: `Exported ${filename}`,
})
} else {
addToast({
type: ToastType.Error,
message: `Could not export ${filename}`,
})
}
return
}
if (notes.length > 1) {
const zippedDataBlob = await application.getArchiveService().zipData(
notes.map((note) => {
return {
name: getNoteFileName(application, note),
content: getNoteBlob(application, note),
}
}),
)
const zippedDataAsBase64 = await getBase64FromBlob(zippedDataBlob)
const filename = `Standard Notes Export - ${application.getArchiveService().formattedDateForExports()}.zip`
const loadingToastId = addToast({
type: ToastType.Loading,
message: `Exporting ${filename}..`,
})
const downloaded = await application.mobileDevice.downloadBase64AsFile(zippedDataAsBase64, filename)
if (downloaded) {
dismissToast(loadingToastId)
addToast({
type: ToastType.Success,
message: `Exported ${filename}`,
})
} else {
addToast({
type: ToastType.Error,
message: `Could not export ${filename}`,
})
}
}
}

View File

@@ -0,0 +1,32 @@
import { WebApplication } from '@/Application/Application'
import { getNoteBlob, getNoteFileName } from '@/Utils/NoteExportUtils'
import { parseFileName } from '@standardnotes/filepicker'
import { Platform, SNNote } from '@standardnotes/snjs'
import { sanitizeFileName } from '@standardnotes/ui-services'
import { downloadBlobOnAndroid } from './DownloadBlobOnAndroid'
export const downloadSelectedNotesOnAndroid = async (application: WebApplication, notes: SNNote[]) => {
if (!application.isNativeMobileWeb() || application.platform !== Platform.Android) {
throw new Error('Function being used on non-android platform')
}
if (notes.length === 1) {
const note = notes[0]
const blob = getNoteBlob(application, note)
const { name, ext } = parseFileName(getNoteFileName(application, note))
const filename = `${sanitizeFileName(name)}.${ext}`
await downloadBlobOnAndroid(application, blob, filename)
return
}
if (notes.length > 1) {
const zippedDataBlob = await application.getArchiveService().zipData(
notes.map((note) => {
return {
name: getNoteFileName(application, note),
content: getNoteBlob(application, note),
}
}),
)
const filename = `Standard Notes Export - ${application.getArchiveService().formattedDateForExports()}.zip`
await downloadBlobOnAndroid(application, zippedDataBlob, filename)
}
}

View File

@@ -0,0 +1,10 @@
import { WebApplication } from '@/Application/Application'
import { getBase64FromBlob } from '@/Utils'
export const shareBlobOnMobile = async (application: WebApplication, blob: Blob, filename: string) => {
if (!application.isNativeMobileWeb()) {
throw new Error('Share function being used outside mobile webview')
}
const base64 = await getBase64FromBlob(blob)
application.mobileDevice.shareBase64AsFile(base64, filename)
}

View File

@@ -1,21 +1,20 @@
import { WebApplication } from '@/Application/Application'
import { getBase64FromBlob } from '@/Utils'
import { getNoteBlob, getNoteFileName } from '@/Utils/NoteExportUtils'
import { parseFileName } from '@standardnotes/filepicker'
import { SNNote } from '@standardnotes/snjs'
import { sanitizeFileName } from '@standardnotes/ui-services'
import { shareBlobOnMobile } from './ShareBlobOnMobile'
export const shareSelectedItems = async (application: WebApplication, notes: SNNote[]) => {
export const shareSelectedNotes = async (application: WebApplication, notes: SNNote[]) => {
if (!application.isNativeMobileWeb()) {
throw new Error('Share function being used outside mobile webview')
}
if (notes.length === 1) {
const note = notes[0]
const blob = getNoteBlob(application, note)
const base64 = await getBase64FromBlob(blob)
const { name, ext } = parseFileName(getNoteFileName(application, note))
const filename = `${sanitizeFileName(name)}.${ext}`
application.mobileDevice.shareBase64AsFile(base64, filename)
void shareBlobOnMobile(application, blob, filename)
return
}
if (notes.length > 1) {
@@ -27,9 +26,9 @@ export const shareSelectedItems = async (application: WebApplication, notes: SNN
}
}),
)
const zippedDataAsBase64 = await getBase64FromBlob(zippedDataBlob)
application.mobileDevice.shareBase64AsFile(
zippedDataAsBase64,
void shareBlobOnMobile(
application,
zippedDataBlob,
`Standard Notes Export - ${application.getArchiveService().formattedDateForExports()}.zip`,
)
}

View File

@@ -0,0 +1,21 @@
import { WebApplication } from '@/Application/Application'
import { downloadBlobOnAndroid } from '@/NativeMobileWeb/DownloadBlobOnAndroid'
import { shareBlobOnMobile } from '@/NativeMobileWeb/ShareBlobOnMobile'
import { Platform } from '@standardnotes/snjs'
export const downloadOrShareBlobBasedOnPlatform = async (application: WebApplication, blob: Blob, filename: string) => {
if (!application.isNativeMobileWeb()) {
application.getArchiveService().downloadData(blob, filename)
return
}
if (application.platform === Platform.Ios) {
shareBlobOnMobile(application, blob, filename)
return
}
if (application.platform === Platform.Android) {
downloadBlobOnAndroid(application, blob, filename)
return
}
}