mirror of
https://github.com/flectar/mail.git
synced 2026-11-11 17:57:54 +00:00
743 lines
52 KiB
Plaintext
743 lines
52 KiB
Plaintext
import { DocumentPreview, TextPreview } from "../components/document-preview.slint";
|
||
import { InfiniteScrollController } from "../components/infinite-scroll-controller.slint";
|
||
import { ScrollFade } from "../components/scroll-fade.slint";
|
||
import { HorizontalBox, VerticalBox, ListView, ScrollView, ComboBox, CheckBox } from "std-widgets.slint";
|
||
import { AppTheme, LayoutMetrics } from "../design-system.slint";
|
||
import { TypeScale } from "../typography.slint";
|
||
import { Button } from "../components/button.slint";
|
||
import { DetailEmptyState } from "../components/detail-empty-state.slint";
|
||
import { AccountRow } from "../models.slint";
|
||
import { SidebarItem, SidebarSectionHeader } from "../components/sidebar-controls.slint";
|
||
import { CollapsibleWorkspaceSidebar, WorkspaceMiniHeading, WorkspacePaneResizeHandle, WorkspaceTitleRow, WorkspaceSearchToolbar, WorkspaceSearchFilter } from "../components/workspace-layout.slint";
|
||
import { WorkspaceListLoadingRow, WorkspaceListTransition } from "../components/workspace-list.slint";
|
||
import { FloatingActionButton, LineEdit, ToolbarIconButton } from "../components/controls.slint";
|
||
import { WindowChrome, WindowDragArea } from "../window-chrome.slint";
|
||
|
||
export struct FileRow {
|
||
identity: string, name: string, detail: string, size: string, date: string, directory: bool,
|
||
// The local day `date` shows, kept to format it again for a new language.
|
||
iso-date: string,
|
||
attachment: bool, readable: bool, writable: bool, renameable: bool,
|
||
deletable: bool, shareable: bool, locked: bool, owns-lock: bool,
|
||
created: string, modified: string, accessed: string, executable: bool, subscribed: bool,
|
||
}
|
||
export struct OperationRow {id:string,action:string,state:string,error:string,bytes:string}
|
||
export global FilesUi {
|
||
in-out property <bool> setup-open: false;
|
||
in-out property <bool> navigation-open: false;
|
||
callback context-changed;
|
||
in-out property <[OperationRow]> operations;
|
||
in-out property <bool> transfers-open: false;
|
||
in-out property <int> collision:0;
|
||
in-out property <int> file-kind:0;
|
||
in-out property <int> file-sort:0;
|
||
in-out property <bool> descending:false;
|
||
in-out property <string> min-size;
|
||
in-out property <string> max-size;
|
||
in-out property <bool> case-insensitive:false;
|
||
in-out property <[FileRow]> rows;
|
||
in-out property <[string]> accounts: [@tr("All accounts")];
|
||
in-out property <int> account: 0;
|
||
in-out property <int> selected-account-id: -1;
|
||
in-out property <[string]> storage-accounts;
|
||
in-out property <int> storage-account: 0;
|
||
in-out property <bool> server-attachments: false;
|
||
in-out property <bool> attachments: true;
|
||
in-out property <bool> connected: false;
|
||
in-out property <int> connection-revision: 0;
|
||
in-out property <bool> busy: false;
|
||
in-out property <bool> list-loading: false;
|
||
in-out property <string> transfer-status;
|
||
in-out property <bool> can-create: false;
|
||
in-out property <bool> can-lock: false;
|
||
in-out property <int> content-revision: 0;
|
||
in-out property <bool> pagination-failed: false;
|
||
in-out property <bool> can-more: false;
|
||
in-out property <bool> can-up: false;
|
||
in-out property <bool> webdav: false;
|
||
in-out property <string> endpoint;
|
||
in-out property <string> path: @tr("Email attachments");
|
||
in-out property <string> query;
|
||
in-out property <string> status;
|
||
in-out property <string> quota;
|
||
in-out property <int> selected: -1;
|
||
in-out property <string> preview-text;
|
||
in-out property <bool> pdf-supported: true;
|
||
in-out property <int> pdf-page: 0;
|
||
in-out property <int> pdf-pages: 0;
|
||
in-out property <int> pdf-zoom: 100;
|
||
in-out property <image> preview-image;
|
||
in-out property <bool> preview-is-image: false;
|
||
in-out property <bool> preview-open: false;
|
||
in-out property <bool> activity-open: false;
|
||
in-out property <string> dialog;
|
||
callback command(string, string, string);
|
||
}
|
||
|
||
// Keep feature navigation on the same shared row surface as Mail and Contacts.
|
||
component FilesSidebarItem inherits SidebarItem {
|
||
sidebar-background: AppTheme.sidebar-bg;
|
||
selection-background: AppTheme.accent-soft;
|
||
accent-color: AppTheme.accent;
|
||
body-color: AppTheme.body;
|
||
muted-color: AppTheme.muted;
|
||
icon-color: root.selected ? AppTheme.accent : AppTheme.secondary;
|
||
row-height: 32px;
|
||
}
|
||
|
||
component FilesNavigation inherits Rectangle {
|
||
in property <[AccountRow]> account-model;
|
||
callback navigate(string, string, string);
|
||
callback setup;
|
||
VerticalBox {
|
||
padding-top: LayoutMetrics.sidebar-action-top;
|
||
padding-left: LayoutMetrics.sidebar-inset;
|
||
padding-right: LayoutMetrics.sidebar-inset;
|
||
padding-bottom: 12px;
|
||
spacing: 8px;
|
||
Button {
|
||
height: LayoutMetrics.sidebar-action-height;
|
||
text: @tr("Connect storage"); primary: true; has-icon: true;
|
||
label-overflow: elide;
|
||
icon: @image-url("../icons/lucide/outline/plus.svg");
|
||
enabled: !FilesUi.busy; clicked => { root.setup(); }
|
||
}
|
||
ScrollView {
|
||
vertical-stretch: 1; content-width: self.visible-width;
|
||
horizontal-scrollbar-policy: always-off;
|
||
VerticalBox {
|
||
padding: 0px; spacing: 6px;
|
||
WorkspaceMiniHeading { label: @tr("UNIFIED"); }
|
||
FilesSidebarItem {
|
||
label: @tr("Email attachments"); left-padding: 12px;
|
||
icon: @image-url("../icons/lucide/filled/paperclip.svg");
|
||
selected: FilesUi.attachments && FilesUi.account == 0;
|
||
clicked(scope) => { root.navigate("account", "0", "attachments"); }
|
||
}
|
||
WorkspaceMiniHeading { label: @tr("ACCOUNTS"); }
|
||
for account in root.account-model : VerticalBox {
|
||
padding: 0px; spacing: 3px;
|
||
private property <bool> expanded: true;
|
||
SidebarSectionHeader {
|
||
label: account.name; detail: account.email;
|
||
avatar: account.initials; avatar-image: account.avatar-small;
|
||
has-avatar: true; has-avatar-image: account.has-avatar;
|
||
open: parent.expanded;
|
||
sidebar-background: AppTheme.sidebar-bg;
|
||
body-color: AppTheme.body; muted-color: AppTheme.muted;
|
||
icon-color: AppTheme.secondary; hover-background: AppTheme.muted-control-bg;
|
||
toggled(open) => { parent.expanded = open; }
|
||
}
|
||
if self.expanded : FilesSidebarItem {
|
||
label: @tr("Email attachments");
|
||
icon: @image-url("../icons/lucide/filled/paperclip.svg");
|
||
selected: FilesUi.attachments && FilesUi.selected-account-id == account.id;
|
||
clicked(scope) => { root.navigate("account-id", "" + account.id, "attachments"); }
|
||
}
|
||
if self.expanded : FilesSidebarItem {
|
||
label: @tr("Server storage");
|
||
icon: @image-url("../icons/lucide/filled/files.svg");
|
||
selected: !FilesUi.attachments && FilesUi.selected-account-id == account.id;
|
||
clicked(scope) => { root.navigate("account-id", "" + account.id, "storage"); }
|
||
}
|
||
}
|
||
if !FilesUi.attachments && FilesUi.storage-accounts.length > 0 : WorkspaceMiniHeading { label: @tr("STORAGE SPACE"); }
|
||
for space[index] in FilesUi.storage-accounts : FilesSidebarItem {
|
||
visible: !FilesUi.attachments;
|
||
label: space; icon: @image-url("../icons/lucide/filled/file-directory.svg");
|
||
selected: index == FilesUi.storage-account;
|
||
clicked(scope) => { root.navigate("space", "" + index, ""); }
|
||
}
|
||
Rectangle { vertical-stretch: 1; }
|
||
}
|
||
}
|
||
if !FilesUi.attachments : Text { text: FilesUi.quota; color: AppTheme.secondary; wrap: word-wrap; font-size: TypeScale.caption; }
|
||
if !FilesUi.attachments && FilesUi.connected && !FilesUi.webdav : Button { text: @tr("Sharing activity"); enabled: !FilesUi.busy; clicked => { root.navigate("notifications", "", ""); } }
|
||
if !FilesUi.attachments : Button { text: @tr("Transfers"); enabled: !FilesUi.busy; clicked => { root.navigate("transfers", "", ""); } }
|
||
|
||
}
|
||
}
|
||
|
||
export component FilesView inherits Rectangle {
|
||
in property <bool> compact: false;
|
||
in property <bool> active: true;
|
||
in property <length> available-width: 0px;
|
||
in property <string> layout-mode: "default";
|
||
in-out property <length> list-pane-width: LayoutMetrics.list-pane-width;
|
||
callback list-pane-width-committed(length);
|
||
in property <[AccountRow]> account-model;
|
||
in-out property <bool> sidebar-collapsed: false;
|
||
in-out property <bool> navigation-open <=> FilesUi.navigation-open;
|
||
private property <bool> single-surface: root.compact || root.layout-mode == "minimal";
|
||
private property <length> list-pane-maximum-width: max(
|
||
LayoutMetrics.list-pane-width,
|
||
root.available-width - workspace-sidebar.current-width - LayoutMetrics.divider-width - LayoutMetrics.detail-pane-min-width
|
||
);
|
||
private property <length> effective-list-pane-width: root.list-pane-width.clamp(
|
||
LayoutMetrics.list-pane-width,
|
||
root.list-pane-maximum-width
|
||
);
|
||
private property <bool> list-visible: !root.single-surface || !FilesUi.preview-open;
|
||
private property <bool> detail-visible: !root.single-surface || FilesUi.preview-open;
|
||
// Details and previews share the detail pane; this decides which one the
|
||
// details toggle in its title row switches to.
|
||
private property <bool> showing-details: false;
|
||
private property <string> search-text: FilesUi.query;
|
||
private property <bool> search-pending: false;
|
||
changed active => { if !root.active { root.search-pending = false; FilesUi.command("release-preview", "", ""); FilesUi.preview-open = false; } }
|
||
public function close-navigation() -> bool {
|
||
if root.setup { root.setup = false; return true; }
|
||
if root.navigation-open { root.navigation-open = false; return true; }
|
||
return false;
|
||
}
|
||
function new-folder() {
|
||
root.value = "";
|
||
FilesUi.collision = 0;
|
||
FilesUi.case-insensitive = false;
|
||
FilesUi.dialog = "folder";
|
||
}
|
||
|
||
function navigate(action: string, value: string, scope: string) {
|
||
if !FilesUi.busy {
|
||
root.navigation-open = false;
|
||
root.search-pending = false;
|
||
if action == "account" || action == "account-id" || action == "space" || action == "up" || action == "attachment-source" { root.search-text = ""; }
|
||
FilesUi.command(action, value, scope);
|
||
}
|
||
}
|
||
function show(action: string, index: string) {
|
||
root.showing-details = action == "details";
|
||
FilesUi.command(action, index, "");
|
||
}
|
||
pagination := InfiniteScrollController {
|
||
active: root.active && root.list-visible && !root.setup && !root.search-pending;
|
||
can-load-more: FilesUi.can-more; loading: FilesUi.busy;
|
||
row-count: FilesUi.rows.length; revision: FilesUi.content-revision;
|
||
content-height: file-list.content-height;
|
||
content-y: file-list.content-y; visible-height: file-list.visible-height;
|
||
prefetch-distance: 246px;
|
||
load-more => { FilesUi.command("more", "", ""); }
|
||
}
|
||
function reset-pagination() {
|
||
file-list.content-y = 0px;
|
||
pagination.reset();
|
||
}
|
||
private property <string> list-scope: "\{FilesUi.account}:\{FilesUi.attachments ? 1 : 0}:\{FilesUi.storage-account}:\{FilesUi.path}:\{FilesUi.query}:\{FilesUi.server-attachments ? 1 : 0}";
|
||
changed list-scope => { root.reset-pagination(); }
|
||
search-delay := Timer {
|
||
interval: 300ms;
|
||
running: root.search-pending && !FilesUi.busy && root.active;
|
||
triggered => { if !FilesUi.busy { root.search-pending = false; FilesUi.command("search", root.search-text, ""); } }
|
||
}
|
||
|
||
Timer { interval: 60s; running: root.active && FilesUi.connected && !FilesUi.attachments && !FilesUi.busy && FilesUi.dialog == "" && !FilesUi.preview-open; triggered => { FilesUi.command("poll", "", ""); } }
|
||
in-out property <bool> setup <=> FilesUi.setup-open;
|
||
private property <bool> connection-attempted: false;
|
||
changed setup => { root.connection-attempted = false; }
|
||
private property <int> connection-revision: FilesUi.connection-revision;
|
||
changed connection-revision => { root.setup = false; }
|
||
|
||
private property <string> value;
|
||
private property <string> destination;
|
||
private property <int> copy-space;
|
||
private property <bool> allow-write: false;
|
||
private property <bool> allow-add: false;
|
||
private property <bool> allow-rename: false;
|
||
private property <bool> allow-delete: false;
|
||
private property <bool> allow-share: false;
|
||
private property <bool> allow-properties:false;
|
||
private property <string> property-namespace:"urn:flectar:files";
|
||
private property <string> property-name:"description";
|
||
private property <bool> shared-lock:false;
|
||
private property <bool> recursive-lock:false;
|
||
private property <string> created;
|
||
private property <string> modified;
|
||
private property <string> accessed;
|
||
private property <bool> executable;
|
||
private property <bool> subscribed;
|
||
private property <FileRow> selected-row: FilesUi.selected >= 0 && FilesUi.selected < FilesUi.rows.length ? FilesUi.rows[FilesUi.selected] : {name: ""};
|
||
background: AppTheme.surface;
|
||
forward-focus: files-focus;
|
||
files-focus := FocusScope {
|
||
width: parent.width; height: parent.height;
|
||
key-pressed(event) => {
|
||
if event.text == Key.Escape {
|
||
if root.navigation-open { root.navigation-open = false; return accept; }
|
||
if FilesUi.preview-open { FilesUi.preview-open = false; if FilesUi.busy { FilesUi.command("cancel", "", ""); } FilesUi.command("release-preview", "", ""); return accept; }
|
||
if FilesUi.transfers-open { FilesUi.transfers-open = false; return accept; }
|
||
if FilesUi.dialog != "" { FilesUi.dialog = ""; return accept; }
|
||
if root.setup { root.setup = false; return accept; }
|
||
}
|
||
return reject;
|
||
}
|
||
HorizontalBox {
|
||
width: parent.width; height: parent.height;
|
||
padding: 0px; spacing: 0px;
|
||
workspace-sidebar := CollapsibleWorkspaceSidebar {
|
||
visible: !root.compact;
|
||
collapsed: root.compact || root.sidebar-collapsed;
|
||
if !root.compact && !root.sidebar-collapsed : FilesNavigation {
|
||
width: parent.width; height: parent.height;
|
||
account-model: root.account-model;
|
||
navigate(action, value, scope) => { root.navigate(action, value, scope); }
|
||
setup => { root.setup = !root.setup; FilesUi.preview-open = false; FilesUi.command("release-preview", "", ""); }
|
||
}
|
||
}
|
||
Rectangle {
|
||
visible: root.list-visible;
|
||
min-width: root.single-surface ? 0px : root.effective-list-pane-width;
|
||
preferred-width: root.single-surface ? 0px : root.effective-list-pane-width;
|
||
max-width: !root.list-visible ? 0px : root.single-surface ? root.available-width : root.effective-list-pane-width;
|
||
horizontal-stretch: root.list-visible && root.single-surface ? 1 : 0;
|
||
VerticalBox {
|
||
padding: 0px; spacing: 0px;
|
||
Rectangle {
|
||
height: root.compact ? LayoutMetrics.compact-header-height : LayoutMetrics.desktop-mail-toolbar-height;
|
||
// Tooltips extend below the toolbar; keep them above the
|
||
// list body, which is declared later in this layout.
|
||
z: 1;
|
||
// The view's focus scope takes the first click, so the
|
||
// window-wide drag band below never sees it here.
|
||
if WindowChrome.controls-visible : WindowDragArea { width: parent.width; height: parent.height; }
|
||
WorkspaceSearchToolbar {
|
||
x: root.compact ? LayoutMetrics.compact-inset : LayoutMetrics.content-inset;
|
||
y: (parent.height - self.height) / 2;
|
||
width: parent.width - 2 * self.x;
|
||
show-leading: true;
|
||
leading-icon: root.compact || root.sidebar-collapsed ? @image-url("../icons/lucide/outline/sidebar-expand.svg") : @image-url("../icons/lucide/outline/sidebar-collapse.svg");
|
||
leading-tooltip: @tr("File sources");
|
||
leading-accessible-id: "files-sidebar-button";
|
||
text <=> root.search-text;
|
||
placeholder-text: @tr("Search files");
|
||
refresh-enabled: !FilesUi.busy;
|
||
refresh-tooltip: @tr("Refresh"); refresh-accessible-id: "files-refresh-button";
|
||
filter-tooltip: @tr("Filter and sort files"); filter-accessible-id: "files-filter-button";
|
||
filter-active: FilesUi.attachments ? FilesUi.server-attachments : FilesUi.file-kind != 0 || FilesUi.file-sort != 0 || FilesUi.descending || FilesUi.min-size != "" || FilesUi.max-size != "";
|
||
private property <[WorkspaceSearchFilter]> attachment-filters: [
|
||
{title: @tr("Synchronized mail"), value: "local"},
|
||
{title: @tr("All mail on server (JMAP)"), value: "server"},
|
||
{title: @tr("Index cached text"), value: "index"}
|
||
];
|
||
private property <[WorkspaceSearchFilter]> storage-filters: [{title: @tr("Filter and sort files"), value: "filter"}];
|
||
filters: FilesUi.attachments ? self.attachment-filters : self.storage-filters;
|
||
leading-clicked => { if root.compact { root.navigation-open = true; } else { root.sidebar-collapsed = !root.sidebar-collapsed; } }
|
||
edited(value) => { root.search-pending = true; search-delay.restart(); }
|
||
submitted(value) => { root.search-pending = false; if !FilesUi.busy { FilesUi.command("search", value, ""); } else { root.search-pending = true; } }
|
||
refresh => { if !root.search-pending { FilesUi.command("refresh", "", ""); } }
|
||
filter-selected(value) => {
|
||
if !FilesUi.busy {
|
||
if value == "filter" { FilesUi.dialog = "filter"; }
|
||
else if value == "index" { FilesUi.command("index-text", "", ""); }
|
||
else { root.navigate("attachment-source", value, ""); }
|
||
}
|
||
}
|
||
|
||
if FilesUi.can-up : ToolbarIconButton {
|
||
icon-color: AppTheme.secondary;
|
||
icon: @image-url("../icons/lucide/outline/arrow-left.svg");
|
||
tooltip-text: @tr("Back");
|
||
interactive: !FilesUi.busy;
|
||
clicked => { root.navigate("up", "", ""); }
|
||
}
|
||
// Phones keep both actions behind the floating "+" below,
|
||
// which leaves the search field room.
|
||
if !FilesUi.attachments && !root.compact : ToolbarIconButton {
|
||
icon-color: AppTheme.secondary;
|
||
icon: @image-url("../icons/lucide/outline/upload.svg");
|
||
tooltip-text: @tr("Upload");
|
||
interactive: FilesUi.can-create && !FilesUi.busy;
|
||
clicked => { root.navigate("upload", "", ""); }
|
||
}
|
||
if !FilesUi.attachments && !root.compact : ToolbarIconButton {
|
||
icon-color: AppTheme.secondary;
|
||
icon: @image-url("../icons/lucide/outline/folder-add.svg");
|
||
tooltip-text: @tr("New folder");
|
||
interactive: FilesUi.can-create && !FilesUi.busy;
|
||
clicked => { root.new-folder(); }
|
||
}
|
||
}
|
||
}
|
||
Rectangle {
|
||
vertical-stretch: 1;
|
||
if FilesUi.list-loading : Rectangle {
|
||
x: LayoutMetrics.list-row-inset;
|
||
y: 0px;
|
||
width: max(0px, parent.width - 2 * LayoutMetrics.list-row-inset);
|
||
height: parent.height;
|
||
background: AppTheme.surface;
|
||
clip: true;
|
||
|
||
for index in 8 : WorkspaceListLoadingRow {
|
||
x: 0px;
|
||
y: index * self.height;
|
||
width: parent.width;
|
||
row-height: 82px;
|
||
leading-size: 24px;
|
||
leading-radius: 5px;
|
||
line-count: 2;
|
||
base-color: AppTheme.control-bg;
|
||
highlight-color: AppTheme.surface;
|
||
}
|
||
}
|
||
if FilesUi.rows.length == 0 && !FilesUi.list-loading : VerticalBox {
|
||
alignment: center; padding: 24px; spacing: 12px;
|
||
Image { source: @image-url("../icons/lucide/outline/files.svg"); width: 40px; height: 40px; colorize: AppTheme.muted; horizontal-stretch: 0; }
|
||
Text { text: @tr("No files to show"); color: AppTheme.heading; font-weight: 600; font-size: TypeScale.body; horizontal-alignment: center; }
|
||
Text { text: FilesUi.attachments ? @tr("Attachments from synchronized messages will appear here.") : FilesUi.connected ? @tr("Upload a file or create a folder to get started.") : @tr("Select an account and connect its storage."); color: AppTheme.secondary; wrap: word-wrap; horizontal-alignment: center; }
|
||
}
|
||
WorkspaceListTransition {
|
||
x: 0px;
|
||
y: 0px;
|
||
width: parent.width;
|
||
height: parent.height;
|
||
scope-key: root.list-scope;
|
||
loading: FilesUi.list-loading;
|
||
|
||
file-list := ListView {
|
||
x: 0px; y: 0px; width: parent.width; height: parent.height;
|
||
mouse-drag-pan-enabled: root.compact;
|
||
accessible-label: @tr("Files"); accessible-item-count: FilesUi.rows.length;
|
||
scrolled => { pagination.user-scrolled(); }
|
||
visible: FilesUi.rows.length > 0 && !FilesUi.list-loading;
|
||
horizontal-scrollbar-policy: always-off;
|
||
content-width: self.visible-width;
|
||
for row[index] in FilesUi.rows : Rectangle {
|
||
x: LayoutMetrics.list-row-inset; width: max(0px, parent.visible-width - 2 * LayoutMetrics.list-row-inset); height: 82px; border-radius: 6px; background: FilesUi.selected == index ? AppTheme.accent-soft : hit.has-hover ? AppTheme.sidebar-tint : transparent;
|
||
HorizontalBox {
|
||
padding: 12px; spacing: 12px;
|
||
Image { width: 24px; height: 24px; source: row.directory ? @image-url("../icons/lucide/outline/file-directory.svg") : row.attachment ? @image-url("../icons/lucide/outline/paperclip.svg") : @image-url("../icons/lucide/outline/file.svg"); colorize: row.directory ? AppTheme.accent : AppTheme.secondary; }
|
||
VerticalBox {
|
||
padding: 0px; spacing: 4px;
|
||
Text { text: row.name; color: AppTheme.heading; font-size: TypeScale.body; font-weight: 600; overflow: elide; }
|
||
Text { text: row.detail; color: AppTheme.secondary; font-size: TypeScale.caption; overflow: elide; }
|
||
}
|
||
|
||
Text { text: row.size; color: AppTheme.secondary; font-size: TypeScale.caption; width: 70px; horizontal-alignment: right; }
|
||
if row.locked : Image { width: LayoutMetrics.icon-size; height: LayoutMetrics.icon-size; source: @image-url("../icons/lucide/outline/lock.svg"); colorize: AppTheme.muted; }
|
||
}
|
||
row-focus := FocusScope {
|
||
enabled: !FilesUi.busy;
|
||
key-pressed(event) => {
|
||
if event.text == Key.Return { FilesUi.selected = index; if row.directory { FilesUi.command("enter", "" + index, ""); } else { root.show("preview", "" + index); } return accept; }
|
||
if event.text == " " { FilesUi.selected = index; return accept; }
|
||
return reject;
|
||
}
|
||
hit := TouchArea {
|
||
enabled: !FilesUi.busy; mouse-cursor: pointer; accessible-role: button; accessible-label: row.name + ", " + row.detail; accessible-checked: FilesUi.selected == index;
|
||
clicked => { row-focus.focus(); FilesUi.selected = index; root.show(row.directory ? "details" : "preview", "" + index); }
|
||
double-clicked => { FilesUi.selected = index; if row.directory { FilesUi.command("enter", "" + index, ""); } else { root.show("preview", "" + index); } }
|
||
accessible-action-default => { row-focus.focus(); FilesUi.selected = index; root.show(row.directory ? "details" : "preview", "" + index); }
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
ScrollFade {
|
||
x: file-list.x; y: file-list.y; width: file-list.visible-width; height: file-list.visible-height;
|
||
surface-color: AppTheme.surface; content-y: file-list.content-y;
|
||
content-height: file-list.content-height; visible-height: file-list.visible-height;
|
||
}
|
||
// On phones, Upload and New folder leave the toolbar for one
|
||
// floating "+", like the mail and contacts create actions.
|
||
if root.compact && !FilesUi.attachments : new-menu := ContextMenuArea {
|
||
x: parent.width - self.width - 16px;
|
||
y: parent.height - self.height - 16px;
|
||
width: new-button.width;
|
||
height: new-button.height;
|
||
z: 100;
|
||
|
||
new-button := FloatingActionButton {
|
||
icon: @image-url("../icons/lucide/outline/plus.svg");
|
||
tooltip-text: @tr("Upload or create a folder");
|
||
accessible-id: "files-new-button";
|
||
interactive: FilesUi.can-create && !FilesUi.busy;
|
||
clicked => { new-menu.show({ x: 0px, y: 0px }); }
|
||
}
|
||
|
||
Menu {
|
||
MenuItem {
|
||
title: @tr("Upload");
|
||
activated => { root.navigate("upload", "", ""); }
|
||
}
|
||
MenuItem {
|
||
title: @tr("New folder");
|
||
activated => { root.new-folder(); }
|
||
}
|
||
}
|
||
}
|
||
}
|
||
HorizontalBox {
|
||
padding: 12px;
|
||
Text { text: FilesUi.busy ? (FilesUi.transfer-status!="" ? FilesUi.transfer-status : @tr("Working…")) : FilesUi.status; color: AppTheme.secondary; wrap: word-wrap; font-size: TypeScale.caption; horizontal-stretch: 1; }
|
||
if FilesUi.busy : Button { text: @tr("Cancel"); clicked => { FilesUi.command("cancel", "", ""); } }
|
||
if FilesUi.can-more && !FilesUi.busy && FilesUi.pagination-failed : Button { text: @tr("Load more"); clicked => { pagination.user-scrolled(); } }
|
||
}
|
||
}
|
||
}
|
||
if !root.single-surface && root.list-visible && root.detail-visible : WorkspacePaneResizeHandle {
|
||
leading-width: root.effective-list-pane-width;
|
||
maximum-leading-width: root.list-pane-maximum-width;
|
||
divider-color: AppTheme.border;
|
||
active-color: AppTheme.accent;
|
||
accessibility-label: @tr("File list width");
|
||
resizing(width) => { root.list-pane-width = width; }
|
||
resize-finished(width) => {
|
||
root.list-pane-width = width;
|
||
root.list-pane-width-committed(width);
|
||
}
|
||
}
|
||
if root.detail-visible : Rectangle {
|
||
horizontal-stretch: 1; min-width: 0px; background: AppTheme.surface;
|
||
if WindowChrome.controls-visible : WindowDragArea { width: parent.width; height: WindowChrome.band-height; }
|
||
VerticalBox {
|
||
width: parent.width; height: parent.height;
|
||
padding: root.compact ? 12px : 24px; spacing: 16px; cross-axis-alignment: stretch;
|
||
if FilesUi.preview-open : WorkspaceTitleRow {
|
||
title: FilesUi.activity-open ? @tr("Sharing activity") : root.selected-row.name;
|
||
show-leading: root.single-surface;
|
||
leading-icon: @image-url("../icons/lucide/outline/arrow-left.svg");
|
||
leading-tooltip: @tr("Back to files");
|
||
leading-clicked => { FilesUi.preview-open = false; if FilesUi.busy { FilesUi.command("cancel", "", ""); } FilesUi.command("release-preview", "", ""); }
|
||
if !FilesUi.activity-open && root.selected-row.directory : Button { width: self.content-width; text: @tr("Open folder"); enabled: !FilesUi.busy && root.selected-row.readable; clicked => { FilesUi.command("enter", "", ""); } }
|
||
if !FilesUi.activity-open && !root.selected-row.directory : ToolbarIconButton { icon-color: AppTheme.secondary; icon: @image-url("../icons/lucide/outline/download.svg"); tooltip-text: @tr("Download"); interactive: !FilesUi.busy && root.selected-row.readable; clicked => { FilesUi.command("download", "", ""); } }
|
||
if !FilesUi.activity-open && FilesUi.attachments && !root.selected-row.directory : ToolbarIconButton { icon-color: AppTheme.secondary; icon: @image-url("../icons/lucide/outline/info.svg"); tooltip-text: root.showing-details ? @tr("Preview") : @tr("Details"); active: root.showing-details; interactive: !FilesUi.busy; clicked => { root.show(root.showing-details ? "preview" : "details", ""); } }
|
||
if !FilesUi.activity-open && !FilesUi.attachments : ToolbarIconButton { icon-color: AppTheme.secondary; icon: @image-url("../icons/lucide/outline/kebab-horizontal.svg"); tooltip-text: @tr("Manage"); interactive: !FilesUi.busy; clicked => { FilesUi.dialog = "manage"; } }
|
||
if !root.single-surface : ToolbarIconButton { icon-color: AppTheme.secondary; icon: @image-url("../icons/lucide/outline/x.svg"); tooltip-text: @tr("Close"); clicked => { FilesUi.preview-open = false; if FilesUi.busy { FilesUi.command("cancel", "", ""); } FilesUi.command("release-preview", "", ""); } }
|
||
}
|
||
if FilesUi.preview-open && !FilesUi.activity-open : Text { vertical-stretch: 0; text: root.selected-row.detail; wrap: word-wrap; color: AppTheme.secondary; font-size: TypeScale.caption; }
|
||
if !FilesUi.preview-open : DetailEmptyState {
|
||
vertical-stretch: 1;
|
||
icon: @image-url("../icons/lucide/outline/files.svg");
|
||
title: @tr("Select a file to preview");
|
||
description: FilesUi.pdf-supported ? @tr("PDFs, images and text appear here. Other formats can be downloaded.") : @tr("Images and text appear here. Other formats can be downloaded.");
|
||
}
|
||
if FilesUi.preview-open && FilesUi.busy : Text { vertical-stretch: 0; text: FilesUi.transfer-status != "" ? FilesUi.transfer-status : @tr("Loading preview…"); color: AppTheme.secondary; wrap: word-wrap; }
|
||
if FilesUi.preview-open && FilesUi.activity-open : Button { vertical-stretch: 0; text: @tr("Clear displayed activity"); enabled: !FilesUi.busy; clicked => { FilesUi.command("clear-notifications", "", ""); } }
|
||
if FilesUi.preview-open && FilesUi.preview-is-image : DocumentPreview {
|
||
compact: root.compact; busy: FilesUi.busy; source: FilesUi.preview-image;
|
||
page: FilesUi.pdf-page; pages: FilesUi.pdf-pages;
|
||
raster-zoom: FilesUi.pdf-pages > 0 ? 2 : 1;
|
||
document-revision: FilesUi.selected;
|
||
render-page(page, zoom) => { FilesUi.command("pdf-page", "" + page, "200"); }
|
||
}
|
||
if FilesUi.preview-open && !FilesUi.preview-is-image : TextPreview { compact: root.compact; text: FilesUi.preview-text; }
|
||
if root.single-surface && FilesUi.preview-open && FilesUi.status != "" : Text { vertical-stretch: 0; text: FilesUi.status; color: AppTheme.secondary; wrap: word-wrap; font-size: TypeScale.caption; }
|
||
}
|
||
}
|
||
}
|
||
if root.compact : Rectangle {
|
||
background: transparent;
|
||
Rectangle {
|
||
visible: root.navigation-open || self.opacity > 0;
|
||
width: parent.width;
|
||
height: parent.height;
|
||
background: #00000060;
|
||
opacity: root.navigation-open ? 1 : 0;
|
||
animate opacity { duration: 180ms; easing: ease-out; }
|
||
TouchArea {
|
||
enabled: root.navigation-open;
|
||
clicked => { root.navigation-open = false; }
|
||
}
|
||
}
|
||
Rectangle {
|
||
visible: root.navigation-open || self.x > -self.width - 11px;
|
||
x: root.navigation-open ? 0px : -self.width - 12px;
|
||
y: 0px;
|
||
width: min(LayoutMetrics.sidebar-width + 40px, parent.width - 24px);
|
||
height: parent.height;
|
||
background: AppTheme.sidebar-bg;
|
||
opacity: root.navigation-open ? 1 : 0.92;
|
||
drop-shadow-blur: root.navigation-open ? 24px : 0px;
|
||
drop-shadow-offset-x: 6px;
|
||
drop-shadow-color: #00000035;
|
||
animate x { duration: 260ms; easing: spring(0.08); }
|
||
animate opacity { duration: 180ms; easing: ease-out; }
|
||
animate drop-shadow-blur { duration: 180ms; easing: ease-out; }
|
||
VerticalBox {
|
||
padding: 0px; spacing: 0px;
|
||
WorkspaceTitleRow { title: @tr("File sources"); horizontal-inset: 12px; Button { text: @tr("Close"); clicked => { root.navigation-open = false; } } }
|
||
FilesNavigation { vertical-stretch: 1; account-model: root.account-model;
|
||
navigate(action, value, scope) => { root.navigate(action, value, scope); }
|
||
setup => { root.navigation-open = false; root.setup = !root.setup; FilesUi.preview-open = false; FilesUi.command("release-preview", "", ""); }
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if FilesUi.dialog != "" : Rectangle {
|
||
background: #00000060;
|
||
TouchArea { }
|
||
Rectangle {
|
||
x: (parent.width - self.width) / 2; y: max(16px, (parent.height - self.height) / 2);
|
||
width: min(440px, parent.width - 24px); height: min(dialog-layout.preferred-height, parent.height - 32px);
|
||
background: AppTheme.surface; border-radius: 12px; border-width: 1px; border-color: AppTheme.border;
|
||
ScrollView {
|
||
content-width: self.width;
|
||
dialog-layout := VerticalBox {
|
||
padding: 24px; spacing: 12px;
|
||
Text { text: FilesUi.dialog == "delete" ? @tr("Delete permanently?") : FilesUi.dialog == "share" ? @tr("Share file or folder") : FilesUi.dialog == "folder" ? @tr("New folder") : FilesUi.dialog == "rename" ? @tr("Rename") : FilesUi.dialog == "move" ? @tr("Move to folder") : FilesUi.dialog == "copy" ? @tr("Copy to folder") : FilesUi.dialog == "metadata" ? @tr("File properties") : FilesUi.dialog == "lock" ? @tr("Lock file or folder") : FilesUi.dialog == "filter" ? @tr("Filter and sort files") : @tr("Manage file"); color: AppTheme.heading; font-size: TypeScale.title; font-weight: 700; }
|
||
if FilesUi.dialog == "filter" : VerticalBox {
|
||
padding:0px;spacing:8px;
|
||
ComboBox {height:36px;vertical-stretch:0;model:[@tr("Files and folders"),@tr("Files"),@tr("Folders")];current-index <=> FilesUi.file-kind;}
|
||
ComboBox {height:36px;vertical-stretch:0;model:[@tr("Name"),@tr("Size"),@tr("Kind")];current-index <=> FilesUi.file-sort;}
|
||
CheckBox {text:@tr("Descending order");checked <=> FilesUi.descending;}
|
||
if FilesUi.file-kind!=2 : LineEdit {input-label:@tr("Minimum bytes");placeholder-text:@tr("Minimum bytes (optional)");text <=> FilesUi.min-size;}
|
||
if FilesUi.file-kind!=2 : LineEdit {input-label:@tr("Maximum bytes");placeholder-text:@tr("Maximum bytes (optional)");text <=> FilesUi.max-size;}
|
||
Button {text:@tr("Reset filters");clicked=>{FilesUi.file-kind=0;FilesUi.file-sort=0;FilesUi.descending=false;FilesUi.min-size="";FilesUi.max-size="";}}
|
||
}
|
||
if FilesUi.dialog == "manage" : VerticalBox {
|
||
padding: 0px; spacing: 8px;
|
||
if !root.selected-row.directory : Button {text:@tr("Keep offline");clicked=>{FilesUi.dialog="";FilesUi.command("keep-offline","","");}}
|
||
if !root.selected-row.directory : Button {text:@tr("Release offline copy");clicked=>{FilesUi.dialog="";FilesUi.command("release-offline","","");}}
|
||
Button { text: @tr("Rename"); enabled: root.selected-row.renameable; clicked => { root.value = root.selected-row.name; FilesUi.collision = 0; FilesUi.case-insensitive = false; FilesUi.dialog = "rename"; } }
|
||
Button { text: @tr("Move…"); enabled: root.selected-row.renameable; clicked => { root.value = root.selected-row.name; root.destination = ""; FilesUi.collision = 0; FilesUi.case-insensitive = false; FilesUi.dialog = "move"; } }
|
||
Button { text: @tr("Copy…"); enabled: root.selected-row.readable; clicked => { root.value = root.selected-row.name; root.destination = ""; root.copy-space = FilesUi.storage-account; FilesUi.collision = 0; FilesUi.case-insensitive = false; FilesUi.dialog = "copy"; } }
|
||
if !root.selected-row.directory : Button { text: @tr("Replace content…"); enabled: root.selected-row.writable; clicked => { FilesUi.dialog = ""; FilesUi.command("replace", "" + FilesUi.selected, ""); } }
|
||
Button { text: @tr("Properties…"); enabled: root.selected-row.writable; clicked => { root.created = root.selected-row.created; root.modified = root.selected-row.modified; root.accessed = root.selected-row.accessed; root.executable = root.selected-row.executable; root.subscribed = root.selected-row.subscribed; root.value = ""; FilesUi.dialog = "metadata"; } }
|
||
Button { text: @tr("Sharing…"); has-icon: true; icon: @image-url("../icons/lucide/outline/share.svg"); enabled: root.selected-row.shareable; clicked => { root.value = ""; root.allow-write = false; root.allow-properties=false; root.allow-add = false; root.allow-rename = false; root.allow-delete = false; root.allow-share = false; FilesUi.dialog = "share"; } }
|
||
if FilesUi.can-lock : Button { text: root.selected-row.owns-lock ? @tr("Release lock") : @tr("Lock…"); enabled: root.selected-row.writable; clicked => { if root.selected-row.owns-lock {FilesUi.dialog="";FilesUi.command("unlock","","");}else{FilesUi.dialog="lock";} } }
|
||
if FilesUi.can-lock && root.selected-row.owns-lock : Button { text: @tr("Renew lock for 5 minutes"); enabled: root.selected-row.writable; clicked => { FilesUi.dialog = ""; FilesUi.command("lock", "300", ""); } }
|
||
Button { text: @tr("Delete…"); destructive: true; enabled: root.selected-row.deletable; clicked => { FilesUi.dialog = "delete"; } }
|
||
}
|
||
if FilesUi.dialog == "delete" : Text { text: @tr("This permanently deletes “{}” and all of its contents from storage. This cannot be undone.", root.selected-row.name); wrap: word-wrap; color: AppTheme.body; }
|
||
if FilesUi.dialog == "folder" || FilesUi.dialog == "rename" || FilesUi.dialog == "move" || FilesUi.dialog == "copy" : LineEdit { text <=> root.value; input-label: @tr("Name"); placeholder-text: @tr("Name"); }
|
||
if FilesUi.dialog == "move" || FilesUi.dialog == "copy" : VerticalBox {
|
||
padding: 0px; spacing: 8px;
|
||
if FilesUi.dialog == "copy" && !FilesUi.webdav : ComboBox { height: 36px; vertical-stretch: 0; model: FilesUi.storage-accounts; current-index <=> root.copy-space; }
|
||
Text { text: @tr("Destination folder path (for example /Documents). Leave empty for the current folder, or the root of another storage space."); wrap: word-wrap; color: AppTheme.secondary; font-size: TypeScale.caption; }
|
||
LineEdit { text <=> root.destination; input-label: @tr("Destination folder"); placeholder-text: "/Documents"; }
|
||
}
|
||
if FilesUi.dialog == "share" : VerticalBox {
|
||
padding: 0px; spacing: 8px;
|
||
Text { text: FilesUi.webdav ? @tr("Enter the recipient’s principal URL on this server.") : @tr("Enter the recipient’s email or principal ID on this server."); wrap: word-wrap; color: AppTheme.secondary; font-size: TypeScale.caption; }
|
||
LineEdit { text <=> root.value; input-label: @tr("Recipient"); placeholder-text: @tr("Recipient"); }
|
||
CheckBox { text: FilesUi.webdav ? @tr("Allow editing") : @tr("Modify content"); checked <=> root.allow-write; }
|
||
VerticalBox {
|
||
padding: 0px; spacing: 4px;
|
||
if FilesUi.webdav : CheckBox {text:@tr("Modify properties");checked <=> root.allow-properties;}
|
||
CheckBox { text: @tr("Add children"); checked <=> root.allow-add; }
|
||
if !FilesUi.webdav : CheckBox { text: @tr("Rename and move"); checked <=> root.allow-rename; }
|
||
CheckBox { text: @tr("Delete"); checked <=> root.allow-delete; }
|
||
CheckBox { text: @tr("Manage sharing"); checked <=> root.allow-share; }
|
||
}
|
||
Button { text: @tr("Remove recipient’s access"); enabled: root.value != ""; clicked => { FilesUi.command("unshare", root.value, ""); FilesUi.dialog = ""; } }
|
||
}
|
||
if FilesUi.dialog == "lock" : VerticalBox {
|
||
padding:0px;spacing:8px;
|
||
CheckBox {text:@tr("Shared lock");checked <=> root.shared-lock;}
|
||
if root.selected-row.directory : CheckBox {text:@tr("Include folder contents");checked <=> root.recursive-lock;}
|
||
Text {text:@tr("Locks expire after 5 minutes unless renewed.");color:AppTheme.secondary;wrap:word-wrap;}
|
||
}
|
||
if FilesUi.dialog=="copy" || FilesUi.dialog=="move" || FilesUi.dialog=="rename" || (FilesUi.dialog=="folder" && !FilesUi.webdav) : VerticalBox {
|
||
padding:0px;spacing:8px;
|
||
Text {text:@tr("If a name already exists");color:AppTheme.secondary;}
|
||
ComboBox {height:36px;vertical-stretch:0;model:[@tr("Stop and report conflict"),@tr("Keep both with a new name"),@tr("Replace existing"),@tr("Keep newest")];current-index <=> FilesUi.collision;}
|
||
if FilesUi.collision>=2 : Text {text:@tr("This can replace existing content at the destination.");color:AppTheme.secondary;wrap:word-wrap;}
|
||
if !FilesUi.webdav : CheckBox {text:@tr("Compare names without case");checked <=> FilesUi.case-insensitive;}
|
||
}
|
||
if FilesUi.dialog == "metadata" : VerticalBox {
|
||
padding: 0px; spacing: 8px;
|
||
if FilesUi.webdav : VerticalBox {
|
||
padding:0px;spacing:8px;
|
||
LineEdit {text <=> root.property-namespace;input-label:@tr("Property namespace");}
|
||
LineEdit {text <=> root.property-name;input-label:@tr("Property name");}
|
||
LineEdit {text <=> root.value;input-label:@tr("Value");}
|
||
Button {text:@tr("Remove property");clicked=>{FilesUi.command("remove-property","",root.property-namespace+"|"+root.property-name);FilesUi.dialog="";}}
|
||
}
|
||
if !FilesUi.webdav : VerticalBox {
|
||
padding: 0px; spacing: 8px;
|
||
Text { text: @tr("Dates use UTC, for example 2026-09-09T12:00:00Z."); wrap: word-wrap; color: AppTheme.secondary; font-size: TypeScale.caption; }
|
||
LineEdit { text <=> root.created; input-label: @tr("Created"); placeholder-text: @tr("Created"); }
|
||
LineEdit { text <=> root.modified; input-label: @tr("Modified"); placeholder-text: @tr("Modified"); }
|
||
LineEdit { text <=> root.accessed; input-label: @tr("Accessed"); placeholder-text: @tr("Accessed"); }
|
||
if !root.selected-row.directory : CheckBox { text: @tr("Executable"); checked <=> root.executable; }
|
||
Text {text:@tr("Subscription changes are unavailable on this server version.");wrap:word-wrap;color:AppTheme.secondary;}
|
||
}
|
||
}
|
||
HorizontalBox {
|
||
padding: 0px;
|
||
Button { text: @tr("Cancel"); clicked => { FilesUi.dialog = ""; } }
|
||
if FilesUi.dialog != "manage" : Button { text: FilesUi.dialog == "delete" ? @tr("Delete permanently") : @tr("Save"); primary: FilesUi.dialog != "delete"; destructive: FilesUi.dialog == "delete"; enabled: !FilesUi.busy && (FilesUi.dialog == "delete" || FilesUi.dialog == "metadata" || FilesUi.dialog == "lock" || FilesUi.dialog == "filter" || root.value != ""); clicked => { if FilesUi.dialog == "metadata" && !FilesUi.webdav {
|
||
FilesUi.command("metadata", root.created + "|" + root.modified + "|" + root.accessed, (root.executable ? "x" : "") + (root.subscribed ? "s" : ""));
|
||
} else if FilesUi.dialog=="filter" {
|
||
FilesUi.command("filter","","");
|
||
} else if FilesUi.dialog=="lock" {
|
||
FilesUi.command("lock","300",(root.shared-lock?"s":"")+(root.recursive-lock?"r":""));
|
||
} else if FilesUi.dialog=="metadata" && FilesUi.webdav {
|
||
FilesUi.command("metadata",root.value,root.property-namespace+"|"+root.property-name);
|
||
} else if FilesUi.dialog == "copy" && !FilesUi.webdav {
|
||
FilesUi.command("copy-space", root.value, "" + root.copy-space + "|" + root.destination);
|
||
} else {
|
||
FilesUi.command(FilesUi.dialog, root.value, FilesUi.dialog == "share" ? (root.allow-write ? "w" : "") + (root.allow-add ? "a" : "") + (root.allow-rename ? "r" : "") + (root.allow-delete ? "d" : "") + (root.allow-share ? "s" : "") + (root.allow-properties ? "p" : "") : root.destination);
|
||
} FilesUi.dialog = ""; } }
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if root.setup : Rectangle {
|
||
background: #00000088; z: 100;
|
||
TouchArea {}
|
||
Rectangle {
|
||
x: (parent.width - self.width) / 2; y: (parent.height - self.height) / 2;
|
||
width: min(480px, parent.width - 24px); height: min(300px + root.account-model.length * 42px + (root.connection-attempted ? 48px : 0px), parent.height - 24px);
|
||
background: AppTheme.surface; border-radius: 12px; border-width: 1px; border-color: AppTheme.border;
|
||
FocusScope {
|
||
init => { self.focus(); }
|
||
key-pressed(event) => {
|
||
if event.text == Key.Escape { root.setup = false; files-focus.focus(); return accept; }
|
||
reject
|
||
}
|
||
VerticalBox {
|
||
padding: 16px; spacing: 12px;
|
||
HorizontalBox { height: 40px; vertical-stretch: 0;
|
||
Text { text: @tr("Connect storage"); color: AppTheme.heading; font-size: TypeScale.title; horizontal-stretch: 1; }
|
||
ToolbarIconButton { compact: root.compact; icon: @image-url("../icons/lucide/outline/x.svg"); icon-color: AppTheme.secondary; tooltip-text: @tr("Close"); clicked => { root.setup = false; } }
|
||
}
|
||
ScrollView { content-width: self.visible-width; content-height: storage-form.preferred-height;
|
||
storage-form := VerticalBox {
|
||
width: parent.visible-width; height: self.preferred-height;
|
||
alignment: start; padding: 0px; spacing: 10px;
|
||
Text { text: @tr("Account"); color: AppTheme.secondary; }
|
||
for account in root.account-model : FilesSidebarItem {
|
||
label: account.email; selected: FilesUi.selected-account-id == account.id;
|
||
clicked(scope) => { root.navigate("account-id", "" + account.id, "attachments"); }
|
||
}
|
||
Text { text: @tr("Uses the selected account’s saved credentials."); color: AppTheme.secondary; font-size: TypeScale.caption; }
|
||
VerticalBox {
|
||
alignment: start; padding: 0px; spacing: 8px;
|
||
ComboBox { height: 36px; vertical-stretch: 0; model: ["JMAP", "WebDAV"]; current-index: FilesUi.webdav ? 1 : 0; enabled: !FilesUi.busy; selected(value) => { FilesUi.webdav = self.current-index == 1; } }
|
||
LineEdit { text <=> FilesUi.endpoint; input-label: @tr("Storage URL"); placeholder-text: FilesUi.webdav ? "https://mail.example.com/dav/file/username/" : "https://mail.example.com"; enabled: !FilesUi.busy; horizontal-stretch: 1; }
|
||
Button { text: @tr("Connect"); primary: true; enabled: FilesUi.account > 0 && !FilesUi.busy; clicked => { root.connection-attempted = true; FilesUi.command("connect", FilesUi.endpoint, FilesUi.webdav ? "dav" : "jmap"); } }
|
||
}
|
||
}
|
||
}
|
||
Text { visible: root.connection-attempted; height: root.connection-attempted ? self.preferred-height : 0px; text: FilesUi.status; color: AppTheme.secondary; wrap: word-wrap; }
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if FilesUi.transfers-open : Rectangle {
|
||
background: AppTheme.surface;
|
||
VerticalBox {
|
||
padding: 16px; spacing: 12px;
|
||
HorizontalBox {Text {text:@tr("Transfers and operations");color:AppTheme.heading;font-size:TypeScale.title;} Button {text:@tr("Close");clicked=>{FilesUi.transfers-open=false;}}}
|
||
Text {text:@tr("Interrupted changes need review before retrying. Refresh storage and inspect the destination first.");wrap:word-wrap;color:AppTheme.secondary;}
|
||
ListView {
|
||
for operation in FilesUi.operations : VerticalBox {
|
||
padding: 8px; spacing: 8px;
|
||
Text {text:operation.action + " · " + operation.state + (operation.bytes!="" ? " · " + operation.bytes : "");color:AppTheme.heading;wrap:word-wrap;}
|
||
Text {text:operation.error;wrap:word-wrap;color:AppTheme.secondary;}
|
||
HorizontalBox {
|
||
if operation.state=="queued" : Button {text:@tr("Resume");enabled:!FilesUi.busy;clicked=>{FilesUi.command("retry-operation",operation.id,"");}}
|
||
if operation.state=="queued" || operation.state=="uncertain" : Button {text:@tr("Dismiss");enabled:!FilesUi.busy;clicked=>{FilesUi.command("cancel-operation",operation.id,"");}}
|
||
if operation.state=="uncertain" : Button {text:@tr("Reviewed as completed");enabled:!FilesUi.busy;clicked=>{FilesUi.command("resolve-operation",operation.id,"");}}
|
||
}
|
||
}
|
||
}
|
||
Text {text:FilesUi.status;wrap:word-wrap;color:AppTheme.secondary;}
|
||
}
|
||
}
|
||
}
|
||
}
|