mirror of
https://github.com/enso-org/enso.git
synced 2024-11-23 16:18:23 +03:00
Hard delete of items in Trash (#9091)
- Close https://github.com/enso-org/cloud-v2/issues/698 - Requires https://github.com/enso-org/cloud-v2/pull/905 # Important Notes None
This commit is contained in:
parent
1a76f6383d
commit
618080b803
@ -233,51 +233,54 @@ export default function AssetRow(props: AssetRowProps) {
|
||||
/* should never change */ setIsAssetPanelTemporarilyVisible,
|
||||
])
|
||||
|
||||
const doDelete = React.useCallback(async () => {
|
||||
setInsertionVisibility(Visibility.hidden)
|
||||
if (asset.type === backendModule.AssetType.directory) {
|
||||
dispatchAssetListEvent({
|
||||
type: AssetListEventType.closeFolder,
|
||||
id: asset.id,
|
||||
// This is SAFE, as this asset is already known to be a directory.
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
key: item.key as backendModule.DirectoryId,
|
||||
})
|
||||
}
|
||||
try {
|
||||
dispatchAssetListEvent({ type: AssetListEventType.willDelete, key: item.key })
|
||||
if (
|
||||
asset.type === backendModule.AssetType.project &&
|
||||
backend.type === backendModule.BackendType.local
|
||||
) {
|
||||
if (
|
||||
asset.projectState.type !== backendModule.ProjectState.placeholder &&
|
||||
asset.projectState.type !== backendModule.ProjectState.closed
|
||||
) {
|
||||
await backend.openProject(asset.id, null, asset.title)
|
||||
}
|
||||
try {
|
||||
await backend.closeProject(asset.id, asset.title)
|
||||
} catch {
|
||||
// Ignored. The project was already closed.
|
||||
}
|
||||
const doDelete = React.useCallback(
|
||||
async (forever = false) => {
|
||||
setInsertionVisibility(Visibility.hidden)
|
||||
if (asset.type === backendModule.AssetType.directory) {
|
||||
dispatchAssetListEvent({
|
||||
type: AssetListEventType.closeFolder,
|
||||
id: asset.id,
|
||||
// This is SAFE, as this asset is already known to be a directory.
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
key: item.key as backendModule.DirectoryId,
|
||||
})
|
||||
}
|
||||
await backend.deleteAsset(asset.id, asset.title)
|
||||
dispatchAssetListEvent({ type: AssetListEventType.delete, key: item.key })
|
||||
} catch (error) {
|
||||
setInsertionVisibility(Visibility.visible)
|
||||
toastAndLog(
|
||||
errorModule.tryGetMessage(error)?.slice(0, -1) ??
|
||||
`Could not delete ${backendModule.ASSET_TYPE_NAME[asset.type]}`
|
||||
)
|
||||
}
|
||||
}, [
|
||||
backend,
|
||||
dispatchAssetListEvent,
|
||||
asset,
|
||||
/* should never change */ item.key,
|
||||
/* should never change */ toastAndLog,
|
||||
])
|
||||
try {
|
||||
dispatchAssetListEvent({ type: AssetListEventType.willDelete, key: item.key })
|
||||
if (
|
||||
asset.type === backendModule.AssetType.project &&
|
||||
backend.type === backendModule.BackendType.local
|
||||
) {
|
||||
if (
|
||||
asset.projectState.type !== backendModule.ProjectState.placeholder &&
|
||||
asset.projectState.type !== backendModule.ProjectState.closed
|
||||
) {
|
||||
await backend.openProject(asset.id, null, asset.title)
|
||||
}
|
||||
try {
|
||||
await backend.closeProject(asset.id, asset.title)
|
||||
} catch {
|
||||
// Ignored. The project was already closed.
|
||||
}
|
||||
}
|
||||
await backend.deleteAsset(asset.id, forever, asset.title)
|
||||
dispatchAssetListEvent({ type: AssetListEventType.delete, key: item.key })
|
||||
} catch (error) {
|
||||
setInsertionVisibility(Visibility.visible)
|
||||
toastAndLog(
|
||||
errorModule.tryGetMessage(error)?.slice(0, -1) ??
|
||||
`Could not delete ${backendModule.ASSET_TYPE_NAME[asset.type]}`
|
||||
)
|
||||
}
|
||||
},
|
||||
[
|
||||
backend,
|
||||
dispatchAssetListEvent,
|
||||
asset,
|
||||
/* should never change */ item.key,
|
||||
/* should never change */ toastAndLog,
|
||||
]
|
||||
)
|
||||
|
||||
const doRestore = React.useCallback(async () => {
|
||||
// Visually, the asset is deleted from the Trash view.
|
||||
@ -337,7 +340,13 @@ export default function AssetRow(props: AssetRowProps) {
|
||||
}
|
||||
case AssetEventType.delete: {
|
||||
if (event.ids.has(item.key)) {
|
||||
await doDelete()
|
||||
await doDelete(false)
|
||||
}
|
||||
break
|
||||
}
|
||||
case AssetEventType.deleteForever: {
|
||||
if (event.ids.has(item.key)) {
|
||||
await doDelete(true)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
@ -67,6 +67,7 @@ export default function DataLinkNameColumn(props: DataLinkNameColumnProps) {
|
||||
case AssetEventType.cancelCut:
|
||||
case AssetEventType.move:
|
||||
case AssetEventType.delete:
|
||||
case AssetEventType.deleteForever:
|
||||
case AssetEventType.restore:
|
||||
case AssetEventType.download:
|
||||
case AssetEventType.downloadSelected:
|
||||
@ -77,8 +78,8 @@ export default function DataLinkNameColumn(props: DataLinkNameColumnProps) {
|
||||
case AssetEventType.removeLabels:
|
||||
case AssetEventType.deleteLabel: {
|
||||
// Ignored. These events should all be unrelated to secrets.
|
||||
// `deleteMultiple`, `restoreMultiple`, `download`,
|
||||
// and `downloadSelected` are handled by `AssetRow`.
|
||||
// `delete`, `deleteForever`, `restoreMultiple`, `download`, and `downloadSelected`
|
||||
// are handled by `AssetRow`.
|
||||
break
|
||||
}
|
||||
case AssetEventType.newDataLink: {
|
||||
|
@ -81,6 +81,7 @@ export default function DirectoryNameColumn(props: DirectoryNameColumnProps) {
|
||||
case AssetEventType.cancelCut:
|
||||
case AssetEventType.move:
|
||||
case AssetEventType.delete:
|
||||
case AssetEventType.deleteForever:
|
||||
case AssetEventType.restore:
|
||||
case AssetEventType.download:
|
||||
case AssetEventType.downloadSelected:
|
||||
@ -91,8 +92,8 @@ export default function DirectoryNameColumn(props: DirectoryNameColumnProps) {
|
||||
case AssetEventType.removeLabels:
|
||||
case AssetEventType.deleteLabel: {
|
||||
// Ignored. These events should all be unrelated to directories.
|
||||
// `deleteMultiple`, `restoreMultiple`, `download`,
|
||||
// and `downloadSelected` are handled by `AssetRow`.
|
||||
// `delete`, `deleteForever`, `restore`, `download`, and `downloadSelected`
|
||||
// are handled by`AssetRow`.
|
||||
break
|
||||
}
|
||||
case AssetEventType.newFolder: {
|
||||
|
@ -66,6 +66,7 @@ export default function FileNameColumn(props: FileNameColumnProps) {
|
||||
case AssetEventType.cancelCut:
|
||||
case AssetEventType.move:
|
||||
case AssetEventType.delete:
|
||||
case AssetEventType.deleteForever:
|
||||
case AssetEventType.restore:
|
||||
case AssetEventType.download:
|
||||
case AssetEventType.downloadSelected:
|
||||
@ -76,7 +77,7 @@ export default function FileNameColumn(props: FileNameColumnProps) {
|
||||
case AssetEventType.removeLabels:
|
||||
case AssetEventType.deleteLabel: {
|
||||
// Ignored. These events should all be unrelated to projects.
|
||||
// `deleteMultiple`, `restoreMultiple`, `download`, and `downloadSelected`
|
||||
// `delete`, `deleteForever`, `restoreMultiple`, `download`, and `downloadSelected`
|
||||
// are handled by `AssetRow`.
|
||||
break
|
||||
}
|
||||
|
@ -234,6 +234,7 @@ export default function ProjectIcon(props: ProjectIconProps) {
|
||||
case AssetEventType.cancelCut:
|
||||
case AssetEventType.move:
|
||||
case AssetEventType.delete:
|
||||
case AssetEventType.deleteForever:
|
||||
case AssetEventType.restore:
|
||||
case AssetEventType.download:
|
||||
case AssetEventType.downloadSelected:
|
||||
@ -243,9 +244,9 @@ export default function ProjectIcon(props: ProjectIconProps) {
|
||||
case AssetEventType.addLabels:
|
||||
case AssetEventType.removeLabels:
|
||||
case AssetEventType.deleteLabel: {
|
||||
// Ignored. Any missing project-related events should be handled by
|
||||
// `ProjectNameColumn`. `deleteMultiple`, `restoreMultiple`, `download`,
|
||||
// and `downloadSelected` are handled by `AssetRow`.
|
||||
// Ignored. Any missing project-related events should be handled by `ProjectNameColumn`.
|
||||
// `delete`, `deleteForever`, `restore`, `download`, and `downloadSelected`
|
||||
// are handled by`AssetRow`.
|
||||
break
|
||||
}
|
||||
case AssetEventType.openProject: {
|
||||
|
@ -102,6 +102,7 @@ export default function ProjectNameColumn(props: ProjectNameColumnProps) {
|
||||
case AssetEventType.cancelCut:
|
||||
case AssetEventType.move:
|
||||
case AssetEventType.delete:
|
||||
case AssetEventType.deleteForever:
|
||||
case AssetEventType.restore:
|
||||
case AssetEventType.download:
|
||||
case AssetEventType.downloadSelected:
|
||||
@ -112,8 +113,8 @@ export default function ProjectNameColumn(props: ProjectNameColumnProps) {
|
||||
case AssetEventType.removeLabels:
|
||||
case AssetEventType.deleteLabel: {
|
||||
// Ignored. Any missing project-related events should be handled by `ProjectIcon`.
|
||||
// `deleteMultiple`, `restoreMultiple`, `download`, and `downloadSelected`
|
||||
// are handled by `AssetRow`.
|
||||
// `delete`, `deleteForever`, `restore`, `download`, and `downloadSelected`
|
||||
// are handled by`AssetRow`.
|
||||
break
|
||||
}
|
||||
case AssetEventType.newProject: {
|
||||
|
@ -64,6 +64,7 @@ export default function SecretNameColumn(props: SecretNameColumnProps) {
|
||||
case AssetEventType.cancelCut:
|
||||
case AssetEventType.move:
|
||||
case AssetEventType.delete:
|
||||
case AssetEventType.deleteForever:
|
||||
case AssetEventType.restore:
|
||||
case AssetEventType.download:
|
||||
case AssetEventType.downloadSelected:
|
||||
@ -74,8 +75,8 @@ export default function SecretNameColumn(props: SecretNameColumnProps) {
|
||||
case AssetEventType.removeLabels:
|
||||
case AssetEventType.deleteLabel: {
|
||||
// Ignored. These events should all be unrelated to secrets.
|
||||
// `deleteMultiple`, `restoreMultiple`, `download`,
|
||||
// and `downloadSelected` are handled by `AssetRow`.
|
||||
// `delete`, `deleteForever`, `restore`, `download`, and `downloadSelected`
|
||||
// are handled by`AssetRow`.
|
||||
break
|
||||
}
|
||||
case AssetEventType.newSecret: {
|
||||
|
@ -19,6 +19,7 @@ enum AssetEventType {
|
||||
cancelCut = 'cancel-cut',
|
||||
move = 'move',
|
||||
delete = 'delete',
|
||||
deleteForever = 'delete-forever',
|
||||
restore = 'restore',
|
||||
download = 'download',
|
||||
downloadSelected = 'download-selected',
|
||||
|
@ -13,6 +13,7 @@ enum AssetListEventType {
|
||||
move = 'move',
|
||||
willDelete = 'will-delete',
|
||||
delete = 'delete',
|
||||
emptyTrash = 'empty-trash',
|
||||
removeSelf = 'remove-self',
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,7 @@ interface AssetEvents {
|
||||
readonly cancelCut: AssetCancelCutEvent
|
||||
readonly move: AssetMoveEvent
|
||||
readonly delete: AssetDeleteEvent
|
||||
readonly deleteForever: AssetDeleteForeverEvent
|
||||
readonly restore: AssetRestoreEvent
|
||||
readonly download: AssetDownloadEvent
|
||||
readonly downloadSelected: AssetDownloadSelectedEvent
|
||||
@ -134,6 +135,11 @@ export interface AssetDeleteEvent extends AssetBaseEvent<AssetEventType.delete>
|
||||
readonly ids: ReadonlySet<backendModule.AssetId>
|
||||
}
|
||||
|
||||
/** A signal to delete assets forever. */
|
||||
export interface AssetDeleteForeverEvent extends AssetBaseEvent<AssetEventType.deleteForever> {
|
||||
readonly ids: ReadonlySet<backendModule.AssetId>
|
||||
}
|
||||
|
||||
/** A signal to restore assets from trash. */
|
||||
export interface AssetRestoreEvent extends AssetBaseEvent<AssetEventType.restore> {
|
||||
readonly ids: ReadonlySet<backendModule.AssetId>
|
||||
|
@ -36,6 +36,7 @@ interface AssetListEvents {
|
||||
readonly move: AssetListMoveEvent
|
||||
readonly willDelete: AssetListWillDeleteEvent
|
||||
readonly delete: AssetListDeleteEvent
|
||||
readonly emptyTrash: AssetListEmptyTrashEvent
|
||||
readonly removeSelf: AssetListRemoveSelfEvent
|
||||
}
|
||||
|
||||
@ -127,6 +128,9 @@ interface AssetListDeleteEvent extends AssetListBaseEvent<AssetListEventType.del
|
||||
readonly key: backend.AssetId
|
||||
}
|
||||
|
||||
/** A signal to permanently delete all files in Trash. */
|
||||
interface AssetListEmptyTrashEvent extends AssetListBaseEvent<AssetListEventType.emptyTrash> {}
|
||||
|
||||
/** A signal for a file to remove itself from the asset list, without being deleted. */
|
||||
interface AssetListRemoveSelfEvent extends AssetListBaseEvent<AssetListEventType.removeSelf> {
|
||||
readonly id: backend.AssetId
|
||||
|
@ -98,6 +98,7 @@ export default function AssetContextMenu(props: AssetContextMenuProps) {
|
||||
},
|
||||
[/* should never change */ setItem]
|
||||
)
|
||||
|
||||
return category === Category.trash ? (
|
||||
!ownsThisAsset ? null : (
|
||||
<ContextMenus hidden={hidden} key={asset.id} event={event}>
|
||||
@ -108,10 +109,23 @@ export default function AssetContextMenu(props: AssetContextMenuProps) {
|
||||
label="Restore From Trash"
|
||||
doAction={() => {
|
||||
unsetModal()
|
||||
dispatchAssetEvent({
|
||||
type: AssetEventType.restore,
|
||||
ids: new Set([asset.id]),
|
||||
})
|
||||
dispatchAssetEvent({ type: AssetEventType.restore, ids: new Set([asset.id]) })
|
||||
}}
|
||||
/>
|
||||
<MenuEntry
|
||||
hidden={hidden}
|
||||
action="delete"
|
||||
label="Delete Forever"
|
||||
doAction={() => {
|
||||
setModal(
|
||||
<ConfirmDeleteModal
|
||||
actionText={`delete the ${asset.type} '${asset.title}' forever`}
|
||||
doDelete={() => {
|
||||
const ids = new Set([asset.id])
|
||||
dispatchAssetEvent({ type: AssetEventType.deleteForever, ids })
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</ContextMenu>
|
||||
|
@ -1727,11 +1727,20 @@ export default function AssetsTable(props: AssetsTableProps) {
|
||||
deleteAsset(event.key)
|
||||
break
|
||||
}
|
||||
case AssetListEventType.emptyTrash: {
|
||||
if (category !== Category.trash) {
|
||||
toastAndLog('Can only empty trash when in Trash')
|
||||
} else if (assetTree.children != null) {
|
||||
const ids = new Set(assetTree.children.map(child => child.item.id))
|
||||
// This is required to prevent an infinite loop,
|
||||
window.setTimeout(() => {
|
||||
dispatchAssetEvent({ type: AssetEventType.deleteForever, ids })
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
case AssetListEventType.removeSelf: {
|
||||
dispatchAssetEvent({
|
||||
type: AssetEventType.removeSelf,
|
||||
id: event.id,
|
||||
})
|
||||
dispatchAssetEvent({ type: AssetEventType.removeSelf, id: event.id })
|
||||
break
|
||||
}
|
||||
case AssetListEventType.closeFolder: {
|
||||
|
@ -86,6 +86,7 @@ export default function AssetsTableContextMenu(props: AssetsTableContextMenuProp
|
||||
)
|
||||
return selfPermission?.permission === permissions.PermissionAction.own
|
||||
}).every(isOwner => isOwner))
|
||||
|
||||
// This is not a React component even though it contains JSX.
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
const doDeleteAll = () => {
|
||||
@ -104,15 +105,7 @@ export default function AssetsTableContextMenu(props: AssetsTableContextMenuProp
|
||||
)
|
||||
}
|
||||
}
|
||||
// This is not a React component even though it contains JSX.
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
const doRestoreAll = () => {
|
||||
unsetModal()
|
||||
dispatchAssetEvent({
|
||||
type: AssetEventType.restore,
|
||||
ids: selectedKeys,
|
||||
})
|
||||
}
|
||||
|
||||
if (category === Category.trash) {
|
||||
return selectedKeys.size === 0 ? (
|
||||
<></>
|
||||
@ -123,8 +116,29 @@ export default function AssetsTableContextMenu(props: AssetsTableContextMenuProp
|
||||
hidden={hidden}
|
||||
action="undelete"
|
||||
label="Restore All From Trash"
|
||||
doAction={doRestoreAll}
|
||||
doAction={() => {
|
||||
unsetModal()
|
||||
dispatchAssetEvent({ type: AssetEventType.restore, ids: selectedKeys })
|
||||
}}
|
||||
/>
|
||||
{isCloud && (
|
||||
<MenuEntry
|
||||
hidden={hidden}
|
||||
action="delete"
|
||||
label="Delete All Forever"
|
||||
doAction={() => {
|
||||
setModal(
|
||||
<ConfirmDeleteModal
|
||||
actionText={`delete ${selectedKeys.size} selected ${pluralized} forever`}
|
||||
doDelete={() => {
|
||||
clearSelectedKeys()
|
||||
dispatchAssetEvent({ type: AssetEventType.deleteForever, ids: selectedKeys })
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</ContextMenu>
|
||||
</ContextMenus>
|
||||
)
|
||||
|
@ -185,6 +185,10 @@ export default function Drive(props: DriveProps) {
|
||||
[backend, user, rootDirectoryId, toastAndLog, /* should never change */ dispatchAssetListEvent]
|
||||
)
|
||||
|
||||
const doEmptyTrash = React.useCallback(() => {
|
||||
dispatchAssetListEvent({ type: AssetListEventType.emptyTrash })
|
||||
}, [/* should never change */ dispatchAssetListEvent])
|
||||
|
||||
const doCreateProject = React.useCallback(
|
||||
(
|
||||
templateId: string | null = null,
|
||||
@ -363,6 +367,7 @@ export default function Drive(props: DriveProps) {
|
||||
<DriveBar
|
||||
category={category}
|
||||
canDownloadFiles={canDownloadFiles}
|
||||
doEmptyTrash={doEmptyTrash}
|
||||
doCreateProject={doCreateProject}
|
||||
doUploadFiles={doUploadFiles}
|
||||
doCreateDirectory={doCreateDirectory}
|
||||
|
@ -19,6 +19,7 @@ import Category from '#/layouts/CategorySwitcher/Category'
|
||||
|
||||
import Button from '#/components/Button'
|
||||
|
||||
import ConfirmDeleteModal from '#/modals/ConfirmDeleteModal'
|
||||
import UpsertDataLinkModal from '#/modals/UpsertDataLinkModal'
|
||||
import UpsertSecretModal from '#/modals/UpsertSecretModal'
|
||||
|
||||
@ -34,6 +35,7 @@ import * as sanitizedEventTargets from '#/utilities/sanitizedEventTargets'
|
||||
export interface DriveBarProps {
|
||||
readonly category: Category
|
||||
readonly canDownloadFiles: boolean
|
||||
readonly doEmptyTrash: () => void
|
||||
readonly doCreateProject: () => void
|
||||
readonly doCreateDirectory: () => void
|
||||
readonly doCreateSecret: (name: string, value: string) => void
|
||||
@ -45,7 +47,7 @@ export interface DriveBarProps {
|
||||
/** Displays the current directory path and permissions, upload and download buttons,
|
||||
* and a column display mode switcher. */
|
||||
export default function DriveBar(props: DriveBarProps) {
|
||||
const { category, canDownloadFiles, doCreateProject, doCreateDirectory } = props
|
||||
const { category, canDownloadFiles, doEmptyTrash, doCreateProject, doCreateDirectory } = props
|
||||
const { doCreateSecret, doCreateDataLink, doUploadFiles, dispatchAssetEvent } = props
|
||||
const { backend } = backendProvider.useBackend()
|
||||
const { setModal, unsetModal } = modalProvider.useSetModal()
|
||||
@ -72,7 +74,23 @@ export default function DriveBar(props: DriveBarProps) {
|
||||
})
|
||||
}, [backend.type, doCreateDirectory, doCreateProject, /* should never change */ inputBindings])
|
||||
|
||||
return (
|
||||
return category === Category.trash ? (
|
||||
<div className="flex h-8 py-0.5">
|
||||
<div className="flex gap-2.5">
|
||||
<button
|
||||
className="flex items-center bg-frame rounded-full h-8 px-2.5"
|
||||
onClick={event => {
|
||||
event.stopPropagation()
|
||||
setModal(
|
||||
<ConfirmDeleteModal actionText="all trashed items forever" doDelete={doEmptyTrash} />
|
||||
)
|
||||
}}
|
||||
>
|
||||
<span className="font-semibold whitespace-nowrap leading-5 h-6 py-px">Clear Trash</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex h-8 py-0.5">
|
||||
<div className="flex gap-2.5">
|
||||
<button
|
||||
@ -169,11 +187,7 @@ export default function DriveBar(props: DriveBarProps) {
|
||||
disabled={!canDownloadFiles}
|
||||
image={DataDownloadIcon}
|
||||
alt="Download Files"
|
||||
error={
|
||||
category === Category.trash
|
||||
? 'You cannot download files from Trash.'
|
||||
: 'You currently can only download files.'
|
||||
}
|
||||
error="You currently can only download files."
|
||||
disabledOpacityClassName="opacity-20"
|
||||
onClick={event => {
|
||||
event.stopPropagation()
|
||||
|
@ -54,8 +54,6 @@ export default function ConfirmDeleteModal(props: ConfirmDeleteModalProps) {
|
||||
}}
|
||||
onSubmit={event => {
|
||||
event.preventDefault()
|
||||
// Consider not calling `onSubmit()` here to make it harder to accidentally
|
||||
// delete an important asset.
|
||||
onSubmit()
|
||||
}}
|
||||
>
|
||||
|
@ -1079,7 +1079,7 @@ export default abstract class Backend {
|
||||
title: string | null
|
||||
): Promise<void>
|
||||
/** Delete an arbitrary asset. */
|
||||
abstract deleteAsset(assetId: AssetId, title: string | null): Promise<void>
|
||||
abstract deleteAsset(assetId: AssetId, force: boolean, title: string | null): Promise<void>
|
||||
/** Restore an arbitrary asset from the trash. */
|
||||
abstract undoDeleteAsset(assetId: AssetId, title: string | null): Promise<void>
|
||||
/** Copy an arbitrary asset to another directory. */
|
||||
|
@ -269,7 +269,11 @@ export default class LocalBackend extends Backend {
|
||||
|
||||
/** Delete an arbitrary asset.
|
||||
* @throws An error if the JSON-RPC call fails. */
|
||||
override async deleteAsset(assetId: backend.AssetId, title: string | null): Promise<void> {
|
||||
override async deleteAsset(
|
||||
assetId: backend.AssetId,
|
||||
_force: boolean,
|
||||
title: string | null
|
||||
): Promise<void> {
|
||||
// This is SAFE, as the only asset type on the local backend is projects.
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
const projectId = assetId as backend.ProjectId
|
||||
|
@ -440,8 +440,9 @@ export default class RemoteBackend extends Backend {
|
||||
|
||||
/** Delete an arbitrary asset.
|
||||
* @throws An error if a non-successful status code (not 200-299) was received. */
|
||||
override async deleteAsset(assetId: backendModule.AssetId, title: string | null) {
|
||||
const path = remoteBackendPaths.deleteAssetPath(assetId)
|
||||
override async deleteAsset(assetId: backendModule.AssetId, force: boolean, title: string | null) {
|
||||
const paramsString = new URLSearchParams([['force', String(force)]]).toString()
|
||||
const path = remoteBackendPaths.deleteAssetPath(assetId) + '?' + paramsString
|
||||
const response = await this.delete(path)
|
||||
if (!responseIsSuccessful(response)) {
|
||||
const name = title != null ? `'${title}'` : `asset with ID '${assetId}'`
|
||||
|
Loading…
Reference in New Issue
Block a user