fix(mobile): Fixes issue where file upload doesn't work with immediate biometrics (#2083)
This commit is contained in:
@@ -18,16 +18,31 @@ function maximumFileSize(): number {
|
|||||||
return 50 * 1_000_000
|
return 50 * 1_000_000
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const FileInputId = 'classic-reader-file-input'
|
||||||
|
function createFileInputOrReturnExisting(): HTMLInputElement {
|
||||||
|
let fileInput = document.getElementById(FileInputId) as HTMLInputElement
|
||||||
|
if (fileInput) {
|
||||||
|
return fileInput
|
||||||
|
}
|
||||||
|
|
||||||
|
fileInput = document.createElement('input')
|
||||||
|
fileInput.id = FileInputId
|
||||||
|
fileInput.type = 'file'
|
||||||
|
fileInput.className = 'absolute top-0 left-0 -z-50 h-px w-px opacity-0'
|
||||||
|
fileInput.multiple = true
|
||||||
|
document.body.appendChild(fileInput)
|
||||||
|
|
||||||
|
return fileInput
|
||||||
|
}
|
||||||
|
|
||||||
function selectFiles(): Promise<File[]> {
|
function selectFiles(): Promise<File[]> {
|
||||||
const input = document.createElement('input') as HTMLInputElement
|
const input = createFileInputOrReturnExisting()
|
||||||
input.type = 'file'
|
|
||||||
input.multiple = true
|
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
input.onchange = async (event) => {
|
input.onchange = async (event) => {
|
||||||
const target = event.target as HTMLInputElement
|
const target = event.target as HTMLInputElement
|
||||||
const files = []
|
const files = []
|
||||||
for (const file of target.files as FileList) {
|
for (const file of Array.from(target.files as FileList)) {
|
||||||
files.push(file)
|
files.push(file)
|
||||||
}
|
}
|
||||||
resolve(files)
|
resolve(files)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { WebApplication } from '@/Application/Application'
|
|||||||
import { PANEL_NAME_NOTES } from '@/Constants/Constants'
|
import { PANEL_NAME_NOTES } from '@/Constants/Constants'
|
||||||
import { FileItem, PrefKey, WebAppEvent } from '@standardnotes/snjs'
|
import { FileItem, PrefKey, WebAppEvent } from '@standardnotes/snjs'
|
||||||
import { observer } from 'mobx-react-lite'
|
import { observer } from 'mobx-react-lite'
|
||||||
import { forwardRef, useCallback, useEffect, useMemo, useRef } from 'react'
|
import { forwardRef, useCallback, useEffect, useMemo } from 'react'
|
||||||
import ContentList from '@/Components/ContentListView/ContentList'
|
import ContentList from '@/Components/ContentListView/ContentList'
|
||||||
import NoAccountWarning from '@/Components/NoAccountWarning/NoAccountWarning'
|
import NoAccountWarning from '@/Components/NoAccountWarning/NoAccountWarning'
|
||||||
import { ItemListController } from '@/Controllers/ItemList/ItemListController'
|
import { ItemListController } from '@/Controllers/ItemList/ItemListController'
|
||||||
@@ -25,7 +25,6 @@ import { ElementIds } from '@/Constants/ElementIDs'
|
|||||||
import ContentListHeader from './Header/ContentListHeader'
|
import ContentListHeader from './Header/ContentListHeader'
|
||||||
import { AppPaneId } from '../Panes/AppPaneMetadata'
|
import { AppPaneId } from '../Panes/AppPaneMetadata'
|
||||||
import { useResponsiveAppPane } from '../Panes/ResponsivePaneProvider'
|
import { useResponsiveAppPane } from '../Panes/ResponsivePaneProvider'
|
||||||
import { StreamingFileReader } from '@standardnotes/filepicker'
|
|
||||||
import SearchBar from '../SearchBar/SearchBar'
|
import SearchBar from '../SearchBar/SearchBar'
|
||||||
import { SearchOptionsController } from '@/Controllers/SearchOptionsController'
|
import { SearchOptionsController } from '@/Controllers/SearchOptionsController'
|
||||||
import { classNames } from '@standardnotes/utils'
|
import { classNames } from '@standardnotes/utils'
|
||||||
@@ -88,7 +87,6 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
|||||||
isCurrentNoteTemplate,
|
isCurrentNoteTemplate,
|
||||||
} = itemListController
|
} = itemListController
|
||||||
|
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
|
||||||
const innerRef = useForwardedRef(ref)
|
const innerRef = useForwardedRef(ref)
|
||||||
|
|
||||||
const { addDragTarget, removeDragTarget } = useFileDragNDrop()
|
const { addDragTarget, removeDragTarget } = useFileDragNDrop()
|
||||||
@@ -172,12 +170,7 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StreamingFileReader.available()) {
|
|
||||||
void filesController.uploadNewFile()
|
void filesController.uploadNewFile()
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fileInputRef.current?.click()
|
|
||||||
} else {
|
} else {
|
||||||
await createNewNote()
|
await createNewNote()
|
||||||
toggleAppPane(AppPaneId.Editor)
|
toggleAppPane(AppPaneId.Editor)
|
||||||
@@ -296,23 +289,6 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
|||||||
>
|
>
|
||||||
<div id="items-title-bar" className="section-title-bar border-b border-solid border-border">
|
<div id="items-title-bar" className="section-title-bar border-b border-solid border-border">
|
||||||
<div id="items-title-bar-container">
|
<div id="items-title-bar-container">
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
className="absolute top-0 left-0 -z-50 h-px w-px opacity-0"
|
|
||||||
multiple
|
|
||||||
ref={fileInputRef}
|
|
||||||
onChange={(event) => {
|
|
||||||
const files = event.currentTarget.files
|
|
||||||
|
|
||||||
if (!files) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < files.length; i++) {
|
|
||||||
void filesController.uploadNewFile(files[i])
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{selectedTag && (
|
{selectedTag && (
|
||||||
<ContentListHeader
|
<ContentListHeader
|
||||||
application={application}
|
application={application}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { LinkingController } from '@/Controllers/LinkingController'
|
|||||||
import { classNames } from '@standardnotes/utils'
|
import { classNames } from '@standardnotes/utils'
|
||||||
import { getLinkingSearchResults } from '@/Utils/Items/Search/getSearchResults'
|
import { getLinkingSearchResults } from '@/Utils/Items/Search/getSearchResults'
|
||||||
import { observer } from 'mobx-react-lite'
|
import { observer } from 'mobx-react-lite'
|
||||||
import { ChangeEventHandler, useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { useApplication } from '../ApplicationProvider'
|
import { useApplication } from '../ApplicationProvider'
|
||||||
import ClearInputButton from '../ClearInputButton/ClearInputButton'
|
import ClearInputButton from '../ClearInputButton/ClearInputButton'
|
||||||
import Icon from '../Icon/Icon'
|
import Icon from '../Icon/Icon'
|
||||||
@@ -42,7 +42,6 @@ const LinkedItemsPanel = ({
|
|||||||
const { entitledToFiles } = featuresController
|
const { entitledToFiles } = featuresController
|
||||||
const application = useApplication()
|
const application = useApplication()
|
||||||
|
|
||||||
const fileInputRef = useRef<HTMLInputElement | null>(null)
|
|
||||||
const searchInputRef = useRef<HTMLInputElement | null>(null)
|
const searchInputRef = useRef<HTMLInputElement | null>(null)
|
||||||
const [searchQuery, setSearchQuery] = useState('')
|
const [searchQuery, setSearchQuery] = useState('')
|
||||||
const isSearching = !!searchQuery.length
|
const isSearching = !!searchQuery.length
|
||||||
@@ -58,33 +57,19 @@ const LinkedItemsPanel = ({
|
|||||||
}
|
}
|
||||||
}, [isOpen])
|
}, [isOpen])
|
||||||
|
|
||||||
const handleFileInputChange: ChangeEventHandler<HTMLInputElement> = (event) => {
|
const selectAndUploadFiles = async () => {
|
||||||
const files = event.currentTarget.files
|
|
||||||
|
|
||||||
if (!files) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < files.length; i++) {
|
|
||||||
void filesController.uploadNewFile(files[i]).then((uploadedFiles) => {
|
|
||||||
if (uploadedFiles) {
|
|
||||||
void linkItemToSelectedItem(uploadedFiles[0])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectAndUploadFiles = () => {
|
|
||||||
if (!entitledToFiles) {
|
if (!entitledToFiles) {
|
||||||
void featuresController.showPremiumAlert(FeatureName.Files)
|
void featuresController.showPremiumAlert(FeatureName.Files)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fileInputRef.current) {
|
const uploadedFiles = await filesController.uploadNewFile()
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fileInputRef.current.click()
|
if (uploadedFiles && uploadedFiles.length) {
|
||||||
|
uploadedFiles.forEach((file) => {
|
||||||
|
void linkItemToSelectedItem(file)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -177,13 +162,6 @@ const LinkedItemsPanel = ({
|
|||||||
<div>
|
<div>
|
||||||
<div className="mt-3 mb-1 px-3 text-menu-item font-semibold uppercase text-passive-0">Linked Files</div>
|
<div className="mt-3 mb-1 px-3 text-menu-item font-semibold uppercase text-passive-0">Linked Files</div>
|
||||||
<div className="my-1">
|
<div className="my-1">
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
className="absolute top-0 left-0 -z-50 h-px w-px opacity-0"
|
|
||||||
multiple
|
|
||||||
ref={fileInputRef}
|
|
||||||
onChange={handleFileInputChange}
|
|
||||||
/>
|
|
||||||
<button
|
<button
|
||||||
className="flex w-full cursor-pointer items-center gap-3 bg-transparent px-3 py-2 text-left text-base text-text hover:bg-info-backdrop hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-sm"
|
className="flex w-full cursor-pointer items-center gap-3 bg-transparent px-3 py-2 text-left text-base text-text hover:bg-info-backdrop hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-sm"
|
||||||
onClick={selectAndUploadFiles}
|
onClick={selectAndUploadFiles}
|
||||||
|
|||||||
Reference in New Issue
Block a user