parent
dcf9603da8
commit
58c7955c64
1
TODO.md
1
TODO.md
@ -10,7 +10,6 @@ Planned for later.
|
|||||||
|
|
||||||
* Soft errors and retry logic
|
* Soft errors and retry logic
|
||||||
* Big caveat: When the server still responds, like 50x errors which would be recoverable, we actually have no way of knowing it did in respond in such a way. See P4 - Handle Errors remarks.
|
* Big caveat: When the server still responds, like 50x errors which would be recoverable, we actually have no way of knowing it did in respond in such a way. See P4 - Handle Errors remarks.
|
||||||
* Delete files (well, as far as the browser allows)
|
|
||||||
* Inter-addon API (basic)
|
* Inter-addon API (basic)
|
||||||
* Add downloads
|
* Add downloads
|
||||||
* vtable perf: cache column widths
|
* vtable perf: cache column widths
|
||||||
|
@ -207,6 +207,22 @@
|
|||||||
"description": "button text",
|
"description": "button text",
|
||||||
"message": "Delete"
|
"message": "Delete"
|
||||||
},
|
},
|
||||||
|
"deletefiles": {
|
||||||
|
"description": "menu action",
|
||||||
|
"message": "Delete Files"
|
||||||
|
},
|
||||||
|
"deletefiles_button": {
|
||||||
|
"description": "button text",
|
||||||
|
"message": "Delete"
|
||||||
|
},
|
||||||
|
"deletefiles_text": {
|
||||||
|
"description": "messagebox text",
|
||||||
|
"message": "Are you sure you want to delete the following files?"
|
||||||
|
},
|
||||||
|
"deletefiles_title": {
|
||||||
|
"description": "messagebox title",
|
||||||
|
"message": "Delete Files"
|
||||||
|
},
|
||||||
"description": {
|
"description": {
|
||||||
"description": "Description (keep it short); e.g. the description column in select",
|
"description": "Description (keep it short); e.g. the description column in select",
|
||||||
"message": "Description"
|
"message": "Description"
|
||||||
@ -529,10 +545,6 @@
|
|||||||
"description": "Status text; manager",
|
"description": "Status text; manager",
|
||||||
"message": "Paused"
|
"message": "Paused"
|
||||||
},
|
},
|
||||||
"pref_sounds": {
|
|
||||||
"description": "checkbox text",
|
|
||||||
"message": "Play sounds"
|
|
||||||
},
|
|
||||||
"pref_add_paused": {
|
"pref_add_paused": {
|
||||||
"description": "Preferences/General",
|
"description": "Preferences/General",
|
||||||
"message": "Add new downloads paused, instead of starting them immediately"
|
"message": "Add new downloads paused, instead of starting them immediately"
|
||||||
@ -585,6 +597,10 @@
|
|||||||
"description": "Preferences/General",
|
"description": "Preferences/General",
|
||||||
"message": "Show URLs instead of Names"
|
"message": "Show URLs instead of Names"
|
||||||
},
|
},
|
||||||
|
"pref_sounds": {
|
||||||
|
"description": "checkbox text",
|
||||||
|
"message": "Play sounds"
|
||||||
|
},
|
||||||
"pref_text_links": {
|
"pref_text_links": {
|
||||||
"description": "Preferences/General",
|
"description": "Preferences/General",
|
||||||
"message": "Try to find links in the website text (slower)"
|
"message": "Try to find links in the website text (slower)"
|
||||||
|
@ -81,6 +81,7 @@ interface Downloads {
|
|||||||
search(query: DownloadsQuery): Promise<any[]>;
|
search(query: DownloadsQuery): Promise<any[]>;
|
||||||
getFileIcon(id: number, options?: any): Promise<string>;
|
getFileIcon(id: number, options?: any): Promise<string>;
|
||||||
setShelfEnabled(state: boolean): void;
|
setShelfEnabled(state: boolean): void;
|
||||||
|
removeFile(manId: number): Promise<void>;
|
||||||
onCreated: ExtensionListener;
|
onCreated: ExtensionListener;
|
||||||
onChanged: ExtensionListener;
|
onChanged: ExtensionListener;
|
||||||
onErased: ExtensionListener;
|
onErased: ExtensionListener;
|
||||||
|
@ -511,4 +511,19 @@ body > * {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: var(--done-color);
|
background: var(--done-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.deletefiles-list {
|
||||||
|
padding-left: 1ex;
|
||||||
|
padding-right: 1.5ex;
|
||||||
|
border: 1px solid lightgray;
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color: rgba(128,128,128,0.1);
|
||||||
|
max-height: 8em;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
.deletefiles-list > li {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
@ -75,6 +75,7 @@
|
|||||||
<ul id="table-context" class="table-context">
|
<ul id="table-context" class="table-context">
|
||||||
<li id="ctx-open-file" data-key="Enter" data-i18n="open-file" data-icon="icon-file">Open File</li>
|
<li id="ctx-open-file" data-key="Enter" data-i18n="open-file" data-icon="icon-file">Open File</li>
|
||||||
<li id="ctx-open-directory" data-key="ACCEL-Enter" data-i18n="open-directory" data-icon="icon-folder">Open Directory</li>
|
<li id="ctx-open-directory" data-key="ACCEL-Enter" data-i18n="open-directory" data-icon="icon-folder">Open Directory</li>
|
||||||
|
<li id="ctx-delete-files" data-key="ACCEL-Delete" data-i18n="deletefiles" data-icon="icon-delete"></li>
|
||||||
<li>-</li>
|
<li>-</li>
|
||||||
<li id="ctx-resume" data-key="ACCEL-KeyR" data-i18n="resume-download" data-icon="icon-go">Resume</li>
|
<li id="ctx-resume" data-key="ACCEL-KeyR" data-i18n="resume-download" data-icon="icon-go">Resume</li>
|
||||||
<li id="ctx-pause" data-key="ACCEL-KeyP" data-i18n="pause-download" data-icon="icon-pause">Pause</li>
|
<li id="ctx-pause" data-key="ACCEL-KeyP" data-i18n="pause-download" data-icon="icon-pause">Pause</li>
|
||||||
@ -125,6 +126,12 @@
|
|||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template id="deletefiles-template">
|
||||||
|
<h1 class="deletefiles-title" data-i18n="deletefiles_title"></h1>
|
||||||
|
<p class="deletefiles-text" data-i18n="deletefiles_text"></p>
|
||||||
|
<ul class="deletefiles-list"></ul>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template id="menufilter-template">
|
<template id="menufilter-template">
|
||||||
<ul>
|
<ul>
|
||||||
<li id="ctx-menufilter-seperator">-</li>
|
<li id="ctx-menufilter-seperator">-</li>
|
||||||
|
@ -7,7 +7,7 @@ import { Prefs } from "../../lib/prefs";
|
|||||||
import { Keys } from "../keys";
|
import { Keys } from "../keys";
|
||||||
import { $ } from "../winutil";
|
import { $ } from "../winutil";
|
||||||
|
|
||||||
export default class RemovalModalDialog extends ModalDialog {
|
export class RemovalModalDialog extends ModalDialog {
|
||||||
private readonly text: string;
|
private readonly text: string;
|
||||||
|
|
||||||
private readonly pref: string;
|
private readonly pref: string;
|
||||||
@ -68,3 +68,57 @@ export default class RemovalModalDialog extends ModalDialog {
|
|||||||
this.focusDefault();
|
this.focusDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class DeleteFilesDialog extends ModalDialog {
|
||||||
|
private readonly paths: string[];
|
||||||
|
|
||||||
|
constructor(paths: string[]) {
|
||||||
|
super();
|
||||||
|
this.paths = paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getContent() {
|
||||||
|
const content = $<HTMLTemplateElement>("#deletefiles-template").
|
||||||
|
content.cloneNode(true) as DocumentFragment;
|
||||||
|
await localize(content);
|
||||||
|
const list = $(".deletefiles-list", content);
|
||||||
|
for (const path of this.paths) {
|
||||||
|
const li = document.createElement("li");
|
||||||
|
li.textContent = path;
|
||||||
|
list.appendChild(li);
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
get buttons() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: _("deletefiles_button"),
|
||||||
|
value: "ok",
|
||||||
|
default: true,
|
||||||
|
dismiss: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: _("cancel"),
|
||||||
|
value: "cancel",
|
||||||
|
default: false,
|
||||||
|
dismiss: true,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
async show() {
|
||||||
|
Keys.suppressed = true;
|
||||||
|
try {
|
||||||
|
return await super.show();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
Keys.suppressed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shown() {
|
||||||
|
this.focusDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ import {
|
|||||||
MenuFilter
|
MenuFilter
|
||||||
} from "./itemfilters";
|
} from "./itemfilters";
|
||||||
import { FilteredCollection } from "./itemfilters";
|
import { FilteredCollection } from "./itemfilters";
|
||||||
import RemovalModalDialog from "./removaldlg";
|
import { RemovalModalDialog, DeleteFilesDialog } from "./removaldlg";
|
||||||
import { Stats } from "./stats";
|
import { Stats } from "./stats";
|
||||||
import PORT from "./port";
|
import PORT from "./port";
|
||||||
import { DownloadState, StateTexts, StateClasses, StateIcons } from "./state";
|
import { DownloadState, StateTexts, StateClasses, StateIcons } from "./state";
|
||||||
@ -404,6 +404,8 @@ export class DownloadTable extends VirtualTable {
|
|||||||
|
|
||||||
private readonly openDirectoryAction: Broadcaster;
|
private readonly openDirectoryAction: Broadcaster;
|
||||||
|
|
||||||
|
private readonly deleteFilesAction: Broadcaster;
|
||||||
|
|
||||||
private readonly moveTopAction: Broadcaster;
|
private readonly moveTopAction: Broadcaster;
|
||||||
|
|
||||||
private readonly moveUpAction: Broadcaster;
|
private readonly moveUpAction: Broadcaster;
|
||||||
@ -579,6 +581,9 @@ export class DownloadTable extends VirtualTable {
|
|||||||
this.openDirectoryAction = new Broadcaster("ctx-open-directory");
|
this.openDirectoryAction = new Broadcaster("ctx-open-directory");
|
||||||
this.openDirectoryAction.onaction = this.openDirectory.bind(this);
|
this.openDirectoryAction.onaction = this.openDirectory.bind(this);
|
||||||
|
|
||||||
|
this.deleteFilesAction = new Broadcaster("ctx-delete-files");
|
||||||
|
this.deleteFilesAction.onaction = this.deleteFiles.bind(this);
|
||||||
|
|
||||||
const moveAction = (method: string) => {
|
const moveAction = (method: string) => {
|
||||||
if (this.selection.empty) {
|
if (this.selection.empty) {
|
||||||
return;
|
return;
|
||||||
@ -610,6 +615,7 @@ export class DownloadTable extends VirtualTable {
|
|||||||
this.moveBottomAction,
|
this.moveBottomAction,
|
||||||
this.openFileAction,
|
this.openFileAction,
|
||||||
this.openDirectoryAction,
|
this.openDirectoryAction,
|
||||||
|
this.deleteFilesAction,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
this.on(
|
this.on(
|
||||||
@ -782,6 +788,10 @@ export class DownloadTable extends VirtualTable {
|
|||||||
this.cancelAction.disabled = true;
|
this.cancelAction.disabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(states & DownloadState.DONE)) {
|
||||||
|
this.deleteFilesAction.disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
const item = this.focusRow >= 0 ?
|
const item = this.focusRow >= 0 ?
|
||||||
this.downloads.filtered[this.focusRow] :
|
this.downloads.filtered[this.focusRow] :
|
||||||
null;
|
null;
|
||||||
@ -860,6 +870,33 @@ export class DownloadTable extends VirtualTable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async deleteFiles() {
|
||||||
|
const items = [];
|
||||||
|
for (const rowid of this.selection) {
|
||||||
|
const item = this.downloads.filtered[rowid];
|
||||||
|
if (item.state === DownloadState.DONE && item.manId) {
|
||||||
|
items.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!items.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const sids = items.map(i => i.sessionId);
|
||||||
|
const paths = items.map(i => i.destFull);
|
||||||
|
await new DeleteFilesDialog(paths).show();
|
||||||
|
await Promise.all(items.map(async item => {
|
||||||
|
try {
|
||||||
|
if (item.manId && item.state === DownloadState.DONE) {
|
||||||
|
await downloads.removeFile(item.manId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
this.removeDownloadsInternal(sids);
|
||||||
|
}
|
||||||
|
|
||||||
removeDownloadsInternal(sids?: number[]) {
|
removeDownloadsInternal(sids?: number[]) {
|
||||||
if (!sids) {
|
if (!sids) {
|
||||||
sids = [];
|
sids = [];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user