import QtQuick 2.0 import Sailfish.Silica 1.0 Page { id: page property var bookData: ({}) property string coverSource: "" property bool downloading: false property string downloadError: "" SilicaFlickable { anchors.fill: parent contentHeight: column.height + Theme.paddingLarge Column { id: column width: parent.width PageHeader { title: bookData.title } Image { id: cover anchors.horizontalCenter: parent.horizontalCenter width: Math.min(parent.width * 0.45, 260) height: width * 1.5 fillMode: Image.PreserveAspectFit source: page.coverSource visible: source.length > 0 } Label { x: Theme.horizontalPageMargin width: parent.width - 2 * Theme.horizontalPageMargin text: bookData.authors color: Theme.secondaryColor wrapMode: Text.Wrap visible: text.length > 0 } Label { x: Theme.horizontalPageMargin width: parent.width - 2 * Theme.horizontalPageMargin text: bookData.summary wrapMode: Text.Wrap font.pixelSize: Theme.fontSizeSmall visible: text.length > 0 } SectionHeader { text: "Formati" } Repeater { model: bookData.formats delegate: ListItem { id: formatItem contentHeight: Theme.itemSizeMedium enabled: !page.downloading Label { anchors.left: parent.left anchors.leftMargin: Theme.horizontalPageMargin anchors.verticalCenter: parent.verticalCenter text: modelData.format + (modelData.size > 0 ? " (" + formatSize(modelData.size) + ")" : "") color: formatItem.highlighted ? Theme.highlightColor : Theme.primaryColor } onClicked: startDownload(modelData) } } Label { x: Theme.horizontalPageMargin width: parent.width - 2 * Theme.horizontalPageMargin text: page.downloadError color: Theme.errorColor wrapMode: Text.Wrap visible: text.length > 0 } Item { width: 1; height: Theme.paddingLarge } } } Column { anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom visible: page.downloading height: Theme.itemSizeMedium ProgressBar { id: progressBar anchors.left: parent.left anchors.right: parent.right value: 0 minimumValue: 0 maximumValue: 1 } Label { anchors.horizontalCenter: parent.horizontalCenter text: "Download in corso…" color: Theme.secondaryColor font.pixelSize: Theme.fontSizeSmall } } Notices { id: notices } function formatSize(bytes) { if (bytes <= 0) return "" if (bytes < 1048576) return Math.round(bytes / 1024) + " KB" return (bytes / 1048576).toFixed(1) + " MB" } function resolveUrl(u) { if (u.indexOf("http://") === 0 || u.indexOf("https://") === 0) return u var base = appSettings.baseUrl // il server dietro sottopath (es. /calibre) emette link che lo includono già: // evita di raddoppiarlo var basePath = base.replace(/^https?:\/\/[^\/]+/, "") if (basePath.length > 1 && u.indexOf(basePath) === 0) return base + u.substring(basePath.length) return base + (u.charAt(0) === "/" ? u : "/" + u) } function sanitizeFileName(s) { return s.replace(/[\\/:*?"<>|]/g, "_").replace(/\s+/g, " ").trim() } function startDownload(format) { if (page.downloading) return var ext = format.format.toLowerCase() var dest = appSettings.downloadDir + "/" + sanitizeFileName(bookData.title) + "." + ext page.downloading = true page.downloadError = "" progressBar.value = 0 progressBar.maximumValue = format.size > 0 ? format.size : 1 downloader.download(resolveUrl(format.url), dest) } Component.onCompleted: { if (bookData.coverUrl && bookData.coverUrl.length > 0) apiClient.fetchImage(bookData.coverUrl, "detail:" + bookData.id) } Connections { target: apiClient onImageReady: { if (id === "detail:" + bookData.id) page.coverSource = localPath } } Connections { target: downloader onProgress: { if (!page.downloading) return progressBar.value = received if (total > 0) progressBar.maximumValue = total } onFinished: { page.downloading = false if (ok) { page.downloadError = "" notices.show("Salvato in " + path) } else { page.downloadError = "Errore: " + error } } } }