chore: on android use notification to display file upload progress instead of toast (#2628) [skip e2e]
This commit is contained in:
BIN
.yarn/cache/@notifee-react-native-npm-7.8.0-c618815190-800233c950.zip
vendored
Normal file
BIN
.yarn/cache/@notifee-react-native-npm-7.8.0-c618815190-800233c950.zip
vendored
Normal file
Binary file not shown.
@@ -507,6 +507,11 @@ PODS:
|
|||||||
- React-Core
|
- React-Core
|
||||||
- RNKeychain (8.1.2):
|
- RNKeychain (8.1.2):
|
||||||
- React-Core
|
- React-Core
|
||||||
|
- RNNotifee (7.8.0):
|
||||||
|
- React-Core
|
||||||
|
- RNNotifee/NotifeeCore (= 7.8.0)
|
||||||
|
- RNNotifee/NotifeeCore (7.8.0):
|
||||||
|
- React-Core
|
||||||
- RNPrivacySnapshot (1.0.0):
|
- RNPrivacySnapshot (1.0.0):
|
||||||
- React-Core
|
- React-Core
|
||||||
- RNShare (9.4.1):
|
- RNShare (9.4.1):
|
||||||
@@ -593,6 +598,7 @@ DEPENDENCIES:
|
|||||||
- RNFS (from `../node_modules/react-native-fs`)
|
- RNFS (from `../node_modules/react-native-fs`)
|
||||||
- RNIap (from `../node_modules/react-native-iap`)
|
- RNIap (from `../node_modules/react-native-iap`)
|
||||||
- RNKeychain (from `../node_modules/react-native-keychain`)
|
- RNKeychain (from `../node_modules/react-native-keychain`)
|
||||||
|
- "RNNotifee (from `../node_modules/@notifee/react-native`)"
|
||||||
- RNPrivacySnapshot (from `../node_modules/react-native-privacy-snapshot`)
|
- RNPrivacySnapshot (from `../node_modules/react-native-privacy-snapshot`)
|
||||||
- RNShare (from `../node_modules/react-native-share`)
|
- RNShare (from `../node_modules/react-native-share`)
|
||||||
- RNStoreReview (from `../node_modules/react-native-store-review`)
|
- RNStoreReview (from `../node_modules/react-native-store-review`)
|
||||||
@@ -716,6 +722,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../node_modules/react-native-iap"
|
:path: "../node_modules/react-native-iap"
|
||||||
RNKeychain:
|
RNKeychain:
|
||||||
:path: "../node_modules/react-native-keychain"
|
:path: "../node_modules/react-native-keychain"
|
||||||
|
RNNotifee:
|
||||||
|
:path: "../node_modules/@notifee/react-native"
|
||||||
RNPrivacySnapshot:
|
RNPrivacySnapshot:
|
||||||
:path: "../node_modules/react-native-privacy-snapshot"
|
:path: "../node_modules/react-native-privacy-snapshot"
|
||||||
RNShare:
|
RNShare:
|
||||||
@@ -789,6 +797,7 @@ SPEC CHECKSUMS:
|
|||||||
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
|
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
|
||||||
RNIap: c397f49db45af3b10dca64b2325f21bb8078ad21
|
RNIap: c397f49db45af3b10dca64b2325f21bb8078ad21
|
||||||
RNKeychain: a65256b6ca6ba6976132cc4124b238a5b13b3d9c
|
RNKeychain: a65256b6ca6ba6976132cc4124b238a5b13b3d9c
|
||||||
|
RNNotifee: f3c01b391dd8e98e67f539f9a35a9cbcd3bae744
|
||||||
RNPrivacySnapshot: 8eaf571478a353f2e5184f5c803164f22428b023
|
RNPrivacySnapshot: 8eaf571478a353f2e5184f5c803164f22428b023
|
||||||
RNShare: 32e97adc8d8c97d4a26bcdd3c45516882184f8b6
|
RNShare: 32e97adc8d8c97d4a26bcdd3c45516882184f8b6
|
||||||
RNStoreReview: 923b1c888c13469925bf0256dc2c046eab557ce5
|
RNStoreReview: 923b1c888c13469925bf0256dc2c046eab557ce5
|
||||||
|
|||||||
@@ -72,6 +72,7 @@
|
|||||||
"node": ">=16"
|
"node": ">=16"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@notifee/react-native": "^7.8.0",
|
||||||
"react-native-store-review": "^0.4.1"
|
"react-native-store-review": "^0.4.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ import { Database } from './Database/Database'
|
|||||||
import { isLegacyIdentifier } from './Database/LegacyIdentifier'
|
import { isLegacyIdentifier } from './Database/LegacyIdentifier'
|
||||||
import { LegacyKeyValueStore } from './Database/LegacyKeyValueStore'
|
import { LegacyKeyValueStore } from './Database/LegacyKeyValueStore'
|
||||||
import Keychain from './Keychain'
|
import Keychain from './Keychain'
|
||||||
|
import notifee, { AuthorizationStatus, Notification } from '@notifee/react-native'
|
||||||
|
|
||||||
export type BiometricsType = 'Fingerprint' | 'Face ID' | 'Biometrics' | 'Touch ID'
|
export type BiometricsType = 'Fingerprint' | 'Face ID' | 'Biometrics' | 'Touch ID'
|
||||||
|
|
||||||
@@ -75,7 +76,40 @@ export class MobileDevice implements MobileDeviceInterface {
|
|||||||
private stateObserverService?: AppStateObserverService,
|
private stateObserverService?: AppStateObserverService,
|
||||||
private androidBackHandlerService?: AndroidBackHandlerService,
|
private androidBackHandlerService?: AndroidBackHandlerService,
|
||||||
private colorSchemeService?: ColorSchemeObserverService,
|
private colorSchemeService?: ColorSchemeObserverService,
|
||||||
) {}
|
) {
|
||||||
|
this.initializeNotifications().catch(console.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
async initializeNotifications() {
|
||||||
|
if (Platform.OS !== 'android') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await notifee.createChannel({
|
||||||
|
id: 'files',
|
||||||
|
name: 'File Upload/Download',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async canDisplayNotifications(): Promise<boolean> {
|
||||||
|
const settings = await notifee.requestPermission()
|
||||||
|
|
||||||
|
return settings.authorizationStatus >= AuthorizationStatus.AUTHORIZED
|
||||||
|
}
|
||||||
|
|
||||||
|
async displayNotification(options: Notification): Promise<string> {
|
||||||
|
return await notifee.displayNotification({
|
||||||
|
...options,
|
||||||
|
android: {
|
||||||
|
...options.android,
|
||||||
|
channelId: 'files',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async cancelNotification(notificationId: string): Promise<void> {
|
||||||
|
await notifee.cancelNotification(notificationId)
|
||||||
|
}
|
||||||
|
|
||||||
async removeRawStorageValuesForIdentifier(identifier: string): Promise<void> {
|
async removeRawStorageValuesForIdentifier(identifier: string): Promise<void> {
|
||||||
await this.removeRawStorageValue(namespacedKey(identifier, RawStorageKey.SnjsVersion))
|
await this.removeRawStorageValue(namespacedKey(identifier, RawStorageKey.SnjsVersion))
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { MobileDevice, MobileDeviceEvent } from './Lib/MobileDevice'
|
|||||||
import { IsDev } from './Lib/Utils'
|
import { IsDev } from './Lib/Utils'
|
||||||
import { ReceivedSharedItemsHandler } from './ReceivedSharedItemsHandler'
|
import { ReceivedSharedItemsHandler } from './ReceivedSharedItemsHandler'
|
||||||
import { ReviewService } from './ReviewService'
|
import { ReviewService } from './ReviewService'
|
||||||
|
import notifee, { EventType } from '@notifee/react-native'
|
||||||
|
|
||||||
const LoggingEnabled = IsDev
|
const LoggingEnabled = IsDev
|
||||||
|
|
||||||
@@ -117,6 +118,34 @@ const MobileWebAppContents = ({ destroyAndReload }: { destroyAndReload: () => vo
|
|||||||
}
|
}
|
||||||
}, [webViewRef, stateService, device, androidBackHandlerService, colorSchemeService])
|
}, [webViewRef, stateService, device, androidBackHandlerService, colorSchemeService])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return notifee.onForegroundEvent(({ type, detail }) => {
|
||||||
|
if (type !== EventType.ACTION_PRESS) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { notification, pressAction } = detail
|
||||||
|
|
||||||
|
if (!notification || !pressAction) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pressAction.id !== 'open-file') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
webViewRef.current?.postMessage(
|
||||||
|
JSON.stringify({
|
||||||
|
reactNativeEvent: ReactNativeToWebEvent.OpenFilePreview,
|
||||||
|
messageType: 'event',
|
||||||
|
messageData: {
|
||||||
|
id: notification.id,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const observer = device.addMobileDeviceEventReceiver((event) => {
|
const observer = device.addMobileDeviceEventReceiver((event) => {
|
||||||
if (event === MobileDeviceEvent.RequestsWebViewReload) {
|
if (event === MobileDeviceEvent.RequestsWebViewReload) {
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import { DeviceInterface } from './DeviceInterface'
|
|||||||
import { AppleIAPReceipt } from '../Subscription/AppleIAPReceipt'
|
import { AppleIAPReceipt } from '../Subscription/AppleIAPReceipt'
|
||||||
import { ApplicationEvent } from '../Event/ApplicationEvent'
|
import { ApplicationEvent } from '../Event/ApplicationEvent'
|
||||||
|
|
||||||
|
import type { Notification } from '../../../../mobile/node_modules/@notifee/react-native/dist/index'
|
||||||
|
|
||||||
export interface MobileDeviceInterface extends DeviceInterface {
|
export interface MobileDeviceInterface extends DeviceInterface {
|
||||||
environment: Environment.Mobile
|
environment: Environment.Mobile
|
||||||
platform: Platform.Ios | Platform.Android
|
platform: Platform.Ios | Platform.Android
|
||||||
@@ -34,4 +36,8 @@ export interface MobileDeviceInterface extends DeviceInterface {
|
|||||||
purchaseSubscriptionIAP(plan: AppleIAPProductId): Promise<AppleIAPReceipt | undefined>
|
purchaseSubscriptionIAP(plan: AppleIAPProductId): Promise<AppleIAPReceipt | undefined>
|
||||||
authenticateWithU2F(authenticationOptionsJSONString: string): Promise<Record<string, unknown> | null>
|
authenticateWithU2F(authenticationOptionsJSONString: string): Promise<Record<string, unknown> | null>
|
||||||
notifyApplicationEvent(event: ApplicationEvent): void
|
notifyApplicationEvent(event: ApplicationEvent): void
|
||||||
|
|
||||||
|
canDisplayNotifications(): Promise<boolean>
|
||||||
|
displayNotification(options: Notification): Promise<string>
|
||||||
|
cancelNotification(notificationId: string): Promise<void>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,4 +12,5 @@ export enum ReactNativeToWebEvent {
|
|||||||
ReceivedFile = 'ReceivedFile',
|
ReceivedFile = 'ReceivedFile',
|
||||||
ReceivedLink = 'ReceivedLink',
|
ReceivedLink = 'ReceivedLink',
|
||||||
ReceivedText = 'ReceivedText',
|
ReceivedText = 'ReceivedText',
|
||||||
|
OpenFilePreview = 'OpenFilePreview',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export interface WebApplicationInterface extends ApplicationInterface {
|
|||||||
handleReceivedFileEvent(file: { name: string; mimeType: string; data: string }): void
|
handleReceivedFileEvent(file: { name: string; mimeType: string; data: string }): void
|
||||||
handleReceivedTextEvent(item: { text: string; title?: string }): Promise<void>
|
handleReceivedTextEvent(item: { text: string; title?: string }): Promise<void>
|
||||||
handleReceivedLinkEvent(item: { link: string; title: string }): Promise<void>
|
handleReceivedLinkEvent(item: { link: string; title: string }): Promise<void>
|
||||||
|
handleOpenFilePreviewEvent(item: { id: string }): void
|
||||||
isNativeMobileWeb(): boolean
|
isNativeMobileWeb(): boolean
|
||||||
handleAndroidBackButtonPressed(): void
|
handleAndroidBackButtonPressed(): void
|
||||||
addAndroidBackHandlerEventListener(listener: () => boolean): (() => void) | undefined
|
addAndroidBackHandlerEventListener(listener: () => boolean): (() => void) | undefined
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
NoteContent,
|
NoteContent,
|
||||||
SNNote,
|
SNNote,
|
||||||
DesktopManagerInterface,
|
DesktopManagerInterface,
|
||||||
|
FileItem,
|
||||||
} from '@standardnotes/snjs'
|
} from '@standardnotes/snjs'
|
||||||
import { action, computed, makeObservable, observable } from 'mobx'
|
import { action, computed, makeObservable, observable } from 'mobx'
|
||||||
import { startAuthentication, startRegistration } from '@simplewebauthn/browser'
|
import { startAuthentication, startRegistration } from '@simplewebauthn/browser'
|
||||||
@@ -76,6 +77,7 @@ import { NoAccountWarningController } from '@/Controllers/NoAccountWarningContro
|
|||||||
import { SearchOptionsController } from '@/Controllers/SearchOptionsController'
|
import { SearchOptionsController } from '@/Controllers/SearchOptionsController'
|
||||||
import { PersistenceService } from '@/Controllers/Abstract/PersistenceService'
|
import { PersistenceService } from '@/Controllers/Abstract/PersistenceService'
|
||||||
import { removeFromArray } from '@standardnotes/utils'
|
import { removeFromArray } from '@standardnotes/utils'
|
||||||
|
import { FileItemActionType } from '@/Components/AttachedFilesPopover/PopoverFileItemAction'
|
||||||
|
|
||||||
export type WebEventObserver = (event: WebAppEvent, data?: unknown) => void
|
export type WebEventObserver = (event: WebAppEvent, data?: unknown) => void
|
||||||
|
|
||||||
@@ -353,6 +355,21 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
|||||||
this.notifyWebEvent(WebAppEvent.MobileKeyboardDidChangeFrame, frame)
|
this.notifyWebEvent(WebAppEvent.MobileKeyboardDidChangeFrame, frame)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleOpenFilePreviewEvent({ id }: { id: string }): void {
|
||||||
|
const file = this.items.findItem<FileItem>(id)
|
||||||
|
if (!file) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.filesController
|
||||||
|
.handleFileAction({
|
||||||
|
type: FileItemActionType.PreviewFile,
|
||||||
|
payload: {
|
||||||
|
file,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch(console.error)
|
||||||
|
}
|
||||||
|
|
||||||
handleReceivedFileEvent(file: { name: string; mimeType: string; data: string }): void {
|
handleReceivedFileEvent(file: { name: string; mimeType: string; data: string }): void {
|
||||||
const filesController = this.filesController
|
const filesController = this.filesController
|
||||||
const blob = getBlobFromBase64(file.data, file.mimeType)
|
const blob = getBlobFromBase64(file.data, file.mimeType)
|
||||||
|
|||||||
@@ -291,6 +291,11 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
|
|||||||
|
|
||||||
private async downloadFile(file: FileItem): Promise<void> {
|
private async downloadFile(file: FileItem): Promise<void> {
|
||||||
let downloadingToastId = ''
|
let downloadingToastId = ''
|
||||||
|
let canShowProgressNotification = false
|
||||||
|
|
||||||
|
if (this.mobileDevice && this.platform === Platform.Android) {
|
||||||
|
canShowProgressNotification = await this.mobileDevice.canDisplayNotifications()
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const saver = StreamingFileSaver.available() ? new StreamingFileSaver(file.name) : new ClassicFileSaver()
|
const saver = StreamingFileSaver.available() ? new StreamingFileSaver(file.name) : new ClassicFileSaver()
|
||||||
@@ -301,11 +306,21 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
|
|||||||
await saver.selectFileToSaveTo()
|
await saver.selectFileToSaveTo()
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadingToastId = addToast({
|
if (this.mobileDevice && canShowProgressNotification) {
|
||||||
type: ToastType.Progress,
|
downloadingToastId = await this.mobileDevice.displayNotification({
|
||||||
message: `Downloading file "${file.name}" (0%)`,
|
title: `Downloading file "${file.name}"`,
|
||||||
progress: 0,
|
android: {
|
||||||
})
|
progress: { max: 100, current: 0, indeterminate: true },
|
||||||
|
onlyAlertOnce: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
downloadingToastId = addToast({
|
||||||
|
type: ToastType.Progress,
|
||||||
|
message: `Downloading file "${file.name}" (0%)`,
|
||||||
|
progress: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const decryptedBytesArray: Uint8Array[] = []
|
const decryptedBytesArray: Uint8Array[] = []
|
||||||
|
|
||||||
@@ -320,10 +335,23 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
|
|||||||
|
|
||||||
const progressPercent = Math.floor(progress.percentComplete)
|
const progressPercent = Math.floor(progress.percentComplete)
|
||||||
|
|
||||||
updateToast(downloadingToastId, {
|
if (this.mobileDevice && canShowProgressNotification) {
|
||||||
message: fileProgressToHumanReadableString(progress, file.name, { showPercent: true }),
|
this.mobileDevice
|
||||||
progress: progressPercent,
|
.displayNotification({
|
||||||
})
|
id: downloadingToastId,
|
||||||
|
title: `Downloading file "${file.name}"`,
|
||||||
|
android: {
|
||||||
|
progress: { max: 100, current: progressPercent, indeterminate: false },
|
||||||
|
onlyAlertOnce: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch(console.error)
|
||||||
|
} else {
|
||||||
|
updateToast(downloadingToastId, {
|
||||||
|
message: fileProgressToHumanReadableString(progress, file.name, { showPercent: true }),
|
||||||
|
progress: progressPercent,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
lastProgress = progress
|
lastProgress = progress
|
||||||
})
|
})
|
||||||
@@ -339,7 +367,6 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
|
|||||||
const blob = new Blob([finalBytes], {
|
const blob = new Blob([finalBytes], {
|
||||||
type: file.mimeType,
|
type: file.mimeType,
|
||||||
})
|
})
|
||||||
// await downloadOrShareBlobBasedOnPlatform(this, blob, file.name, false)
|
|
||||||
await downloadOrShareBlobBasedOnPlatform({
|
await downloadOrShareBlobBasedOnPlatform({
|
||||||
archiveService: this.archiveService,
|
archiveService: this.archiveService,
|
||||||
platform: this.platform,
|
platform: this.platform,
|
||||||
@@ -351,12 +378,18 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
addToast({
|
if (this.mobileDevice && canShowProgressNotification) {
|
||||||
type: ToastType.Success,
|
await this.mobileDevice.displayNotification({
|
||||||
message: `Successfully downloaded file${
|
title: `Successfully downloaded file "${file.name}"`,
|
||||||
lastProgress && lastProgress.source === 'local' ? ' from local backup' : ''
|
})
|
||||||
}`,
|
} else {
|
||||||
})
|
addToast({
|
||||||
|
type: ToastType.Success,
|
||||||
|
message: `Successfully downloaded file${
|
||||||
|
lastProgress && lastProgress.source === 'local' ? ' from local backup' : ''
|
||||||
|
}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
|
||||||
@@ -366,8 +399,12 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (downloadingToastId.length > 0) {
|
if (downloadingToastId) {
|
||||||
dismissToast(downloadingToastId)
|
if (this.mobileDevice && canShowProgressNotification) {
|
||||||
|
this.mobileDevice.cancelNotification(downloadingToastId).catch(console.error)
|
||||||
|
} else {
|
||||||
|
dismissToast(downloadingToastId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,6 +449,11 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
|
|||||||
const { showToast = true, note } = options
|
const { showToast = true, note } = options
|
||||||
|
|
||||||
let toastId: string | undefined
|
let toastId: string | undefined
|
||||||
|
let canShowProgressNotification = false
|
||||||
|
|
||||||
|
if (showToast && this.mobileDevice && this.platform === Platform.Android) {
|
||||||
|
canShowProgressNotification = await this.mobileDevice.canDisplayNotifications()
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const minimumChunkSize = this.files.minimumChunkSize()
|
const minimumChunkSize = this.files.minimumChunkSize()
|
||||||
@@ -449,11 +491,21 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
|
|||||||
const initialProgress = operation.getProgress().percentComplete
|
const initialProgress = operation.getProgress().percentComplete
|
||||||
|
|
||||||
if (showToast) {
|
if (showToast) {
|
||||||
toastId = addToast({
|
if (this.mobileDevice && canShowProgressNotification) {
|
||||||
type: ToastType.Progress,
|
toastId = await this.mobileDevice.displayNotification({
|
||||||
message: `Uploading file "${fileToUpload.name}" (${initialProgress}%)`,
|
title: `Uploading file "${fileToUpload.name}"`,
|
||||||
progress: initialProgress,
|
android: {
|
||||||
})
|
progress: { max: 100, current: initialProgress, indeterminate: true },
|
||||||
|
onlyAlertOnce: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
toastId = addToast({
|
||||||
|
type: ToastType.Progress,
|
||||||
|
message: `Uploading file "${fileToUpload.name}" (${initialProgress}%)`,
|
||||||
|
progress: initialProgress,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onChunk: OnChunkCallbackNoProgress = async ({ data, index, isLast }) => {
|
const onChunk: OnChunkCallbackNoProgress = async ({ data, index, isLast }) => {
|
||||||
@@ -461,10 +513,21 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
|
|||||||
|
|
||||||
const percentComplete = Math.round(operation.getProgress().percentComplete)
|
const percentComplete = Math.round(operation.getProgress().percentComplete)
|
||||||
if (toastId) {
|
if (toastId) {
|
||||||
updateToast(toastId, {
|
if (this.mobileDevice && canShowProgressNotification) {
|
||||||
message: `Uploading file "${fileToUpload.name}" (${percentComplete}%)`,
|
await this.mobileDevice.displayNotification({
|
||||||
progress: percentComplete,
|
id: toastId,
|
||||||
})
|
title: `Uploading file "${fileToUpload.name}"`,
|
||||||
|
android: {
|
||||||
|
progress: { max: 100, current: percentComplete, indeterminate: false },
|
||||||
|
onlyAlertOnce: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
updateToast(toastId, {
|
||||||
|
message: `Uploading file "${fileToUpload.name}" (${percentComplete}%)`,
|
||||||
|
progress: percentComplete,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -480,32 +543,54 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
|
|||||||
if (uploadedFile instanceof ClientDisplayableError) {
|
if (uploadedFile instanceof ClientDisplayableError) {
|
||||||
addToast({
|
addToast({
|
||||||
type: ToastType.Error,
|
type: ToastType.Error,
|
||||||
message: 'Unable to close upload session',
|
message: uploadedFile.text,
|
||||||
})
|
})
|
||||||
throw new Error('Unable to close upload session')
|
throw new Error(uploadedFile.text)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toastId) {
|
if (toastId) {
|
||||||
|
if (this.mobileDevice && canShowProgressNotification) {
|
||||||
|
this.mobileDevice.cancelNotification(toastId).catch(console.error)
|
||||||
|
}
|
||||||
dismissToast(toastId)
|
dismissToast(toastId)
|
||||||
}
|
}
|
||||||
if (showToast) {
|
if (showToast) {
|
||||||
addToast({
|
if (this.mobileDevice && canShowProgressNotification) {
|
||||||
type: ToastType.Success,
|
this.mobileDevice
|
||||||
message: `Uploaded file "${uploadedFile.name}"`,
|
.displayNotification({
|
||||||
actions: [
|
id: uploadedFile.uuid,
|
||||||
{
|
title: `Uploaded file "${uploadedFile.name}"`,
|
||||||
label: 'Open',
|
android: {
|
||||||
handler: (toastId) => {
|
actions: [
|
||||||
void this.handleFileAction({
|
{
|
||||||
type: FileItemActionType.PreviewFile,
|
title: 'Open',
|
||||||
payload: { file: uploadedFile },
|
pressAction: {
|
||||||
})
|
id: 'open-file',
|
||||||
dismissToast(toastId)
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
],
|
.catch(console.error)
|
||||||
autoClose: true,
|
} else {
|
||||||
})
|
addToast({
|
||||||
|
type: ToastType.Success,
|
||||||
|
message: `Uploaded file "${uploadedFile.name}"`,
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: 'Open',
|
||||||
|
handler: (toastId) => {
|
||||||
|
void this.handleFileAction({
|
||||||
|
type: FileItemActionType.PreviewFile,
|
||||||
|
payload: { file: uploadedFile },
|
||||||
|
})
|
||||||
|
dismissToast(toastId)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
autoClose: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return uploadedFile
|
return uploadedFile
|
||||||
@@ -513,12 +598,23 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
|
|||||||
console.error(error)
|
console.error(error)
|
||||||
|
|
||||||
if (toastId) {
|
if (toastId) {
|
||||||
|
if (this.mobileDevice && canShowProgressNotification) {
|
||||||
|
this.mobileDevice.cancelNotification(toastId).catch(console.error)
|
||||||
|
}
|
||||||
dismissToast(toastId)
|
dismissToast(toastId)
|
||||||
}
|
}
|
||||||
addToast({
|
if (this.mobileDevice && canShowProgressNotification) {
|
||||||
type: ToastType.Error,
|
this.mobileDevice
|
||||||
message: 'There was an error while uploading the file',
|
.displayNotification({
|
||||||
})
|
title: 'There was an error while uploading the file',
|
||||||
|
})
|
||||||
|
.catch(console.error)
|
||||||
|
} else {
|
||||||
|
addToast({
|
||||||
|
type: ToastType.Error,
|
||||||
|
message: 'There was an error while uploading the file',
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined
|
return undefined
|
||||||
|
|||||||
@@ -98,7 +98,9 @@ export class MobileWebReceiver {
|
|||||||
case ReactNativeToWebEvent.ReceivedLink:
|
case ReactNativeToWebEvent.ReceivedLink:
|
||||||
void this.application.handleReceivedLinkEvent(messageData as { link: string; title: string })
|
void this.application.handleReceivedLinkEvent(messageData as { link: string; title: string })
|
||||||
break
|
break
|
||||||
|
case ReactNativeToWebEvent.OpenFilePreview:
|
||||||
|
void this.application.handleOpenFilePreviewEvent(messageData as { id: string })
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
10
yarn.lock
10
yarn.lock
@@ -3206,6 +3206,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@notifee/react-native@npm:^7.8.0":
|
||||||
|
version: 7.8.0
|
||||||
|
resolution: "@notifee/react-native@npm:7.8.0"
|
||||||
|
peerDependencies:
|
||||||
|
react-native: "*"
|
||||||
|
checksum: 800233c9505195046e918ee42a4b11d8e84cf1846ea4a0b52599e1a40181b2bbfcab09c64b445b5a7eed98c38556c97550bce32ed24a51ce05ccd8b65047548d
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@npmcli/arborist@npm:^6.2.5":
|
"@npmcli/arborist@npm:^6.2.5":
|
||||||
version: 6.3.0
|
version: 6.3.0
|
||||||
resolution: "@npmcli/arborist@npm:6.3.0"
|
resolution: "@npmcli/arborist@npm:6.3.0"
|
||||||
@@ -4585,6 +4594,7 @@ __metadata:
|
|||||||
"@babel/core": "*"
|
"@babel/core": "*"
|
||||||
"@babel/preset-typescript": ^7.18.6
|
"@babel/preset-typescript": ^7.18.6
|
||||||
"@babel/runtime": ^7.20.1
|
"@babel/runtime": ^7.20.1
|
||||||
|
"@notifee/react-native": ^7.8.0
|
||||||
"@react-native-async-storage/async-storage": 1.19.3
|
"@react-native-async-storage/async-storage": 1.19.3
|
||||||
"@react-native/eslint-config": ^0.72.2
|
"@react-native/eslint-config": ^0.72.2
|
||||||
"@react-native/metro-config": ^0.72.11
|
"@react-native/metro-config": ^0.72.11
|
||||||
|
|||||||
Reference in New Issue
Block a user