allow selecting folders (#6074)

for google drive #5532

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Mikael Finstad 2025-12-02 10:34:03 +07:00 committed by GitHub
parent 4817585b66
commit e6613488fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 94 additions and 8 deletions

View file

@ -0,0 +1,5 @@
---
"@uppy/provider-views": patch
---
Allow selecting folders with Google Drive Picker. They will be recursively resolved.

View file

@ -105,6 +105,11 @@ export default function GooglePickerView({
appId,
onFilesPicked,
signal,
onLoadingChange: (isLoading: boolean) => setLoading(isLoading),
onError: (err: unknown) => {
uppy.log(err)
uppy.info(i18n('failedToAddFiles'), 'error')
},
})
} else {
// photos
@ -175,6 +180,7 @@ export default function GooglePickerView({
pickerType,
setAccessToken,
uppy,
i18n,
],
)

View file

@ -209,12 +209,16 @@ export async function showDrivePicker({
appId,
onFilesPicked,
signal,
onLoadingChange,
onError,
}: {
token: string
apiKey: string
appId: string
onFilesPicked: (files: PickedItem[], accessToken: string) => void
signal: AbortSignal | undefined
onLoadingChange: (loading: boolean) => void
onError: (err: unknown) => void
}): Promise<void> {
// google drive picker will crash hard if given an invalid token, so we need to check it first
// https://github.com/transloadit/uppy/pull/5443#pullrequestreview-2452439265
@ -222,18 +226,88 @@ export async function showDrivePicker({
throw new InvalidTokenError()
}
const onPicked = (picked: google.picker.ResponseObject) => {
if (picked.action === google.picker.Action.PICKED) {
// console.log('Picker response', JSON.stringify(picked, null, 2));
onFilesPicked(
picked.docs.map((doc) => ({
async function listFilesInDriveFolder({
doc,
token,
signal,
}: {
doc: PickedItemBase
token: string
signal?: AbortSignal
}): Promise<PickedDriveItem[]> {
if (doc.mimeType !== 'application/vnd.google-apps.folder') {
return [
{
platform: 'drive',
id: doc.id,
name: doc.name,
mimeType: doc.mimeType,
})),
token,
},
]
}
const headers = getAuthHeader(token)
const items: PickedDriveItem[] = []
let pageToken: string | undefined
do {
const params = new URLSearchParams({
q: `'${doc.id.replace(/'/g, "\\'")}' in parents and trashed = false`,
fields: 'nextPageToken, files(id, name, mimeType)',
pageSize: '1000',
...(pageToken && { pageToken }),
})
const res = await fetch(
`https://www.googleapis.com/drive/v3/files?${params.toString()}`,
{ headers, signal },
)
if (!res.ok) {
throw new Error(
`Failed to list folder contents for '${doc.name}' (${doc.id}): ${res.status} ${res.statusText}`,
)
}
const json: { nextPageToken?: string; files: PickedItemBase[] } =
await res.json()
pageToken = json.nextPageToken
for (const file of json.files) {
items.push(
...(await listFilesInDriveFolder({ doc: file, token, signal })),
)
}
} while (pageToken)
return items
}
const onPicked = async (picked: google.picker.ResponseObject) => {
if (picked.action !== google.picker.Action.PICKED) return
try {
onLoadingChange(true)
// console.log('Picker response', JSON.stringify(picked, null, 2));
const results: PickedDriveItem[] = []
for (const doc of picked.docs) {
if (doc.mimeType === 'application/vnd.google-apps.folder') {
results.push(
...(await listFilesInDriveFolder({ doc, token, signal })),
)
} else {
results.push({
platform: 'drive',
id: doc.id,
name: doc.name,
mimeType: doc.mimeType,
})
}
}
onFilesPicked(results, token)
} catch (err) {
onError(err)
} finally {
onLoadingChange(false)
}
}
@ -248,7 +322,7 @@ export async function showDrivePicker({
.setIncludeFolders(true)
// Note: setEnableDrives doesn't seem to work
// .setEnableDrives(true)
.setSelectFolderEnabled(false)
.setSelectFolderEnabled(true)
.setMode(google.picker.DocsViewMode.LIST),
)
// NOTE: photos is broken and results in an error being returned from Google

View file

@ -46,5 +46,6 @@ export default {
1: '%{smart_count} more files added',
},
showErrorDetails: 'Show error details',
failedToAddFiles: 'Failed to add files',
},
}