Skip to content

Commit

Permalink
fixup! fixup! fixup! feat(files): add conversion action
Browse files Browse the repository at this point in the history
Signed-off-by: skjnldsv <[email protected]>
  • Loading branch information
skjnldsv committed Jan 17, 2025
1 parent 11f87ff commit cc40e96
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 12 deletions.
39 changes: 27 additions & 12 deletions apps/files/src/actions/convertAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,57 @@
import type { Node, View } from '@nextcloud/files'

import { FileAction, registerFileAction } from '@nextcloud/files'
import { generateUrl } from '@nextcloud/router'
import { generateOcsUrl, generateUrl } from '@nextcloud/router'

Check failure on line 8 in apps/files/src/actions/convertAction.ts

View workflow job for this annotation

GitHub Actions / NPM lint

'generateOcsUrl' is defined but never used
import { getCapabilities } from '@nextcloud/capabilities'
import { t } from '@nextcloud/l10n'

import AutoRenewSvg from '@mdi/svg/svg/autorenew.svg?raw'

import logger from '../logger'

Check failure on line 14 in apps/files/src/actions/convertAction.ts

View workflow job for this annotation

GitHub Actions / NPM lint

'logger' is defined but never used
import axios from '@nextcloud/axios'

Check failure on line 15 in apps/files/src/actions/convertAction.ts

View workflow job for this annotation

GitHub Actions / NPM lint

'axios' is defined but never used
import { convertFile, convertFiles } from './convertUtils'
import { showError, showMessage } from '@nextcloud/dialogs'

Check failure on line 17 in apps/files/src/actions/convertAction.ts

View workflow job for this annotation

GitHub Actions / NPM lint

'showError' is defined but never used

Check failure on line 17 in apps/files/src/actions/convertAction.ts

View workflow job for this annotation

GitHub Actions / NPM lint

'showMessage' is defined but never used

type ConversionsProvider = {
from: string,
to: string[],
to: string,
displayName: string,
}

export const ACTION_CONVERT = 'convert'

export const registerConvertActions = () => {
// Generate sub actions
const convertProviders = getCapabilities()?.core?.conversions as ConversionsProvider[] ?? []
const actions = convertProviders.map(provider => {
return provider.to.map(to => new FileAction({
id: `convert-${provider.from}-${to}`,
displayName: () => t('files', 'Save as {to}', { to }),
const convertProviders = getCapabilities()?.files.file_conversions as ConversionsProvider[] ?? []
const actions = convertProviders.map(({ to, from, displayName }) => {
return new FileAction({
id: `convert-${from}-${to}`,
displayName: () => t('files', 'Save as {displayName}', { displayName }),
iconSvgInline: () => generateIconSvg(to),
enabled: (nodes: Node[]) => {
// Check if some of the nodes are not of the right type
return !nodes.some(node => provider.from !== node.mime)
return !nodes.some(node => from !== node.mime)
},

async exec(node: Node) {
logger.debug(`Convert to ${provider.from}`, { node })
// If we're here, we know that the node have a fileid
convertFile(node.fileid as number, to)

// Silently terminate, we'll handle the UI in the background
return null
},

parent: ACTION_CONVERT,
}))
}).flat()
async execBatch(nodes: Node[]) {
const fileIds = nodes.map(node => node.fileid).filter(Boolean) as number[]
convertFiles(fileIds, to)

// Silently terminate, we'll handle the UI in the background
return Array(nodes.length).fill(null)
},

// parent: ACTION_CONVERT,
})
})

// Register main action
registerFileAction(new FileAction({
Expand Down
82 changes: 82 additions & 0 deletions apps/files/src/actions/convertUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import type { AxiosResponse } from 'axios'

Check failure on line 5 in apps/files/src/actions/convertUtils.ts

View workflow job for this annotation

GitHub Actions / NPM lint

"axios" is extraneous

import { generateOcsUrl } from '@nextcloud/router'
import { showError, showMessage } from '@nextcloud/dialogs'
import { t } from '@nextcloud/l10n'
import axios from '@nextcloud/axios'
import PQueue from 'p-queue'

import logger from '../logger'

const queue = new PQueue({ concurrency: 5 })

const requestConversion = function(fileId: number, targetMimeType: string): Promise<AxiosResponse> {
return axios.post(generateOcsUrl('/apps/files/api/v1/convert'), {
fileId,
targetMimeType,
})
}

export const convertFiles = async function(fileIds: number[], targetMimeType: string) {
const conversions = fileIds.map(fileId => queue.add(() => requestConversion(fileId, targetMimeType)))

// Start conversion
const toast = showMessage(t('files', 'Converting files…'))

// Handle results
try {
const results = await Promise.allSettled(conversions)
const failed = results.filter(result => result.status === 'rejected')
if (failed.length) {
const messages = failed.map(result => result.reason?.response?.data?.ocs?.meta?.message) as string[]

// If all failed files have the same error message, show it
if (new Set(messages).size === 1) {
showError(t('files', 'Failed to convert files: {message}', { message: messages[0] }))
return
}

if (failed.length === fileIds.length) {
showError(t('files', 'Failed to convert files'))
return
}
showError(t('files', 'Some files could not be converted'))
return
}

// All files converted
showMessage(t('files', 'Files successfully converted'))
} catch (error) {
// Should not happen as we use allSettled and handle errors above
showError(t('files', 'Failed to convert files'))
logger.error('Failed to convert files', { fileIds, targetMimeType, error })
} finally {
// Hide loader toast
toast.hideToast()
}
}

export const convertFile = async function(fileId: number, targetMimeType: string) {
const toast = showMessage(t('files', 'Converting files…'))

try {
await queue.add(() => requestConversion(fileId, targetMimeType))
showMessage(t('files', 'File successfully converted'))
} catch (error) {
// If the server returned an error message, show it
if (error.response?.data?.ocs?.meta?.message) {
showError(t('files', 'Failed to convert file: {message}', { message: error.response.data.ocs.meta.message }))
return
}

logger.error('Failed to convert file', { fileId, targetMimeType, error })
showError(t('files', 'Failed to convert file'))
} finally {
// Hide loader toast
toast.hideToast()
}
}

0 comments on commit cc40e96

Please sign in to comment.