Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion packages/mobile/src/Lib/Interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
TransferPayload,
} from '@standardnotes/snjs'
import { Alert, Linking, PermissionsAndroid, Platform, StatusBar } from 'react-native'
import FileViewer from 'react-native-file-viewer'
import FingerprintScanner from 'react-native-fingerprint-scanner'
import FlagSecure from 'react-native-flag-secure-android'
import {
Expand Down Expand Up @@ -541,14 +542,36 @@ export class MobileDevice implements MobileDeviceInterface {

try {
const path = this.getFileDestinationPath(filename, saveInTempLocation)
void this.deleteFileAtPathIfExists(path)
await this.deleteFileAtPathIfExists(path)
await writeFile(path, base64.replace(/data.*base64,/, ''), 'base64')
return path
} catch (error) {
this.consoleLog(`${error}`)
}
}

async previewFile(base64: string, filename: string): Promise<boolean> {
const tempLocation = await this.downloadBase64AsFile(base64, filename, true)

if (!tempLocation) {
this.consoleLog('Error: Could not download file to preview')
return false
}

try {
await FileViewer.open(tempLocation, {
onDismiss: async () => {
await this.deleteFileAtPathIfExists(tempLocation)
},
})
} catch (error) {
this.consoleLog(error)
return false
}

return true
}

confirmAndExit() {
Alert.alert(
'Close app',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export interface WebApplicationInterface extends ApplicationInterface {
handleMobileLosingFocusEvent(): Promise<void>
handleMobileResumingFromBackgroundEvent(): Promise<void>
isNativeMobileWeb(): boolean
get mobileDevice(): MobileDeviceInterface
mobileDevice(): MobileDeviceInterface
handleAndroidBackButtonPressed(): void
addAndroidBackHandlerEventListener(listener: () => boolean): (() => void) | undefined
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ export interface MobileDeviceInterface extends DeviceInterface {
handleThemeSchemeChange(isDark: boolean, bgColor: string): void
shareBase64AsFile(base64: string, filename: string): Promise<void>
downloadBase64AsFile(base64: string, filename: string, saveInTempLocation?: boolean): Promise<string | undefined>
previewFile(base64: string, filename: string): Promise<boolean>
confirmAndExit(): void
}
9 changes: 4 additions & 5 deletions packages/ui-services/src/Theme/ThemeManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,10 +293,9 @@ export class ThemeManager extends AbstractService {

if (this.application.isNativeMobileWeb()) {
setTimeout(() => {
this.application.mobileDevice.handleThemeSchemeChange(
theme.package_info.isDark ?? false,
this.getBackgroundColor(),
)
this.application
.mobileDevice()
.handleThemeSchemeChange(theme.package_info.isDark ?? false, this.getBackgroundColor())
})
}
}
Expand Down Expand Up @@ -335,7 +334,7 @@ export class ThemeManager extends AbstractService {
removeFromArray(this.activeThemes, uuid)

if (this.activeThemes.length === 0 && this.application.isNativeMobileWeb()) {
this.application.mobileDevice.handleThemeSchemeChange(false, '#ffffff')
this.application.mobileDevice().handleThemeSchemeChange(false, '#ffffff')
}
}

Expand Down
8 changes: 4 additions & 4 deletions packages/web/src/javascripts/Application/Application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export class WebApplication extends SNApplication implements WebApplicationInter

// eslint-disable-next-line no-console
console.log = (...args) => {
this.mobileDevice.consoleLog(...args)
this.mobileDevice().consoleLog(...args)
}
}

Expand Down Expand Up @@ -179,7 +179,7 @@ export class WebApplication extends SNApplication implements WebApplicationInter
return undefined
}

get mobileDevice(): MobileDeviceInterface {
mobileDevice(): MobileDeviceInterface {
if (!this.isNativeMobileWeb()) {
throw Error('Attempting to access device as mobile device on non mobile platform')
}
Expand Down Expand Up @@ -238,15 +238,15 @@ export class WebApplication extends SNApplication implements WebApplicationInter

async handleMobileLosingFocusEvent(): Promise<void> {
if (this.getMobileScreenshotPrivacyEnabled()) {
this.mobileDevice.stopHidingMobileInterfaceFromScreenshots()
this.mobileDevice().stopHidingMobileInterfaceFromScreenshots()
}

await this.lockApplicationAfterMobileEventIfApplicable()
}

async handleMobileResumingFromBackgroundEvent(): Promise<void> {
if (this.getMobileScreenshotPrivacyEnabled()) {
this.mobileDevice.hideMobileInterfaceFromScreenshots()
this.mobileDevice().hideMobileInterfaceFromScreenshots()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const BiometricsPrompt = ({ application, onValueChange, prompt, buttonRef }: Pro
fullWidth
colorStyle={authenticated ? 'success' : 'info'}
onClick={async () => {
const authenticated = await application.mobileDevice.authenticateWithBiometrics()
const authenticated = await application.mobileDevice().authenticateWithBiometrics()
setAuthenticated(authenticated)
onValueChange(authenticated, prompt)
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const FilePreview = ({ file, application }: Props) => {
<span className="mt-3">Loading file...</span>
</div>
) : downloadedBytes ? (
<PreviewComponent file={file} bytes={downloadedBytes} />
<PreviewComponent application={application} file={file} bytes={downloadedBytes} />
) : (
<FilePreviewError
file={file}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
import { WebApplication } from '@/Application/Application'
import { getBase64FromBlob } from '@/Utils'
import { FileItem } from '@standardnotes/snjs'
import { FunctionComponent, useEffect, useMemo, useRef } from 'react'
import { FunctionComponent, useCallback, useEffect, useMemo, useRef } from 'react'
import Button from '../Button/Button'
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
import { createObjectURLWithRef } from './CreateObjectURLWithRef'
import ImagePreview from './ImagePreview'
import { PreviewableTextFileTypes } from './isFilePreviewable'
import { PreviewableTextFileTypes, RequiresNativeFilePreview } from './isFilePreviewable'
import TextPreview from './TextPreview'

type Props = {
application: WebApplication
file: FileItem
bytes: Uint8Array
}

const PreviewComponent: FunctionComponent<Props> = ({ file, bytes }) => {
const PreviewComponent: FunctionComponent<Props> = ({ application, file, bytes }) => {
const { selectedPane } = useResponsiveAppPane()

const objectUrlRef = useRef<string>()

const objectUrl = useMemo(() => {
Expand All @@ -28,6 +36,42 @@ const PreviewComponent: FunctionComponent<Props> = ({ file, bytes }) => {
}
}, [])

const isNativeMobileWeb = application.isNativeMobileWeb()
const requiresNativePreview = RequiresNativeFilePreview.includes(file.mimeType)

const openNativeFilePreview = useCallback(async () => {
if (!isNativeMobileWeb) {
throw new Error('Native file preview cannot be used on non-native platform')
}

const fileBase64 = await getBase64FromBlob(
new Blob([bytes], {
type: file.mimeType,
}),
)

application.mobileDevice().previewFile(fileBase64, file.name)
}, [application, bytes, file.mimeType, file.name, isNativeMobileWeb])

useEffect(() => {
const shouldOpenNativePreviewOnLoad =
isNativeMobileWeb && selectedPane === AppPaneId.Editor && requiresNativePreview
if (shouldOpenNativePreviewOnLoad) {
void openNativeFilePreview()
}
}, [isNativeMobileWeb, openNativeFilePreview, requiresNativePreview, selectedPane])

if (isNativeMobileWeb && requiresNativePreview) {
return (
<div className="flex flex-grow flex-col items-center justify-center">
<div className="text-base font-bold">Previewing file...</div>
<Button className="mt-3" primary onClick={openNativeFilePreview}>
Re-open file preview
</Button>
</div>
)
}

if (file.mimeType.startsWith('image/')) {
return <ImagePreview objectUrl={objectUrl} />
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export const PreviewableTextFileTypes = ['text/plain', 'text/csv', 'application/json']

export const RequiresNativeFilePreview = ['application/pdf']

export const isFileTypePreviewable = (fileType: string) => {
const isImage = fileType.startsWith('image/')
const isVideo = fileType.startsWith('video/')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const UpgradeNow = ({ application, featuresController }: Props) => {
}

if (application.isNativeMobileWeb()) {
application.mobileDevice.openUrl(window.plansUrl)
application.mobileDevice().openUrl(window.plansUrl)
} else {
window.location.assign(window.plansUrl)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const CustomNoteTitleFormat = ({ application }: Props) => {
onClick={(event) => {
if (application.isNativeMobileWeb()) {
event.preventDefault()
application.mobileDevice.openUrl(HelpPageUrl)
application.mobileDevice().openUrl(HelpPageUrl)
}
}}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const loadPurchaseFlowUrl = async (application: WebApplication): Promise<
const finalUrl = `${url}${period}${plan}`

if (application.isNativeMobileWeb()) {
application.mobileDevice.openUrl(finalUrl)
application.mobileDevice().openUrl(finalUrl)
} else {
window.location.assign(finalUrl)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const downloadBlobOnAndroid = async (
})
}
const base64 = await getBase64FromBlob(blob)
const downloaded = await application.mobileDevice.downloadBase64AsFile(base64, filename)
const downloaded = await application.mobileDevice().downloadBase64AsFile(base64, filename)
if (loadingToastId) {
dismissToast(loadingToastId)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ export const shareBlobOnMobile = async (application: WebApplication, blob: Blob,
throw new Error('Share function being used outside mobile webview')
}
const base64 = await getBase64FromBlob(blob)
application.mobileDevice.shareBase64AsFile(base64, filename)
application.mobileDevice().shareBase64AsFile(base64, filename)
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const AndroidBackHandlerProvider = ({ application, children }: ProviderProps) =>

useEffect(() => {
const removeListener = addAndroidBackHandler(() => {
application.mobileDevice.confirmAndExit()
application.mobileDevice().confirmAndExit()
return true
})
return () => {
Expand Down