7.7 KiB · text History 9cdb602
import QtQuick
import QtQuick.Layouts
import org.kde.plasma.components as PlasmaComponents3
import org.kde.kirigami as Kirigami

import "Status.js" as Status

// One running job on the wall. What fits stays: a card stacked four high
// drops its log, then its step bar and details, before its name and
// clock — the order the web wall drops them in.
Rectangle {
    id: cardItem

    required property var card
    property double now: Date.now()

    readonly property var job: card.job || {}
    readonly property var run: card.run || {}
    readonly property var steps: job.steps || []
    readonly property real gu: Kirigami.Units.gridUnit
    readonly property bool roomForLog: height >= gu * 13
    readonly property bool roomForMeta: height >= gu * 8
    readonly property bool roomForSteps: height >= gu * 6
    // Too short for two lines: just the job and its clock.
    readonly property bool tiny: height < gu * 3

    radius: Kirigami.Units.cornerRadius
    color: Kirigami.Theme.alternateBackgroundColor
    border.width: 1
    border.color: Qt.alpha(Kirigami.Theme.neutralTextColor, 0.35)
    clip: true

    // A light sweeping along the top edge: this one is moving.
    Rectangle {
        id: shimmerTrack
        anchors { top: parent.top; left: parent.left; right: parent.right; margins: 1 }
        height: 2
        color: Qt.alpha(Kirigami.Theme.neutralTextColor, 0.25)
        clip: true

        Rectangle {
            width: parent.width * 0.4
            height: parent.height
            gradient: Gradient {
                orientation: Gradient.Horizontal
                GradientStop { position: 0; color: "transparent" }
                GradientStop { position: 0.5; color: Kirigami.Theme.neutralTextColor }
                GradientStop { position: 1; color: "transparent" }
            }
            NumberAnimation on x {
                from: -shimmerTrack.width * 0.4
                to: shimmerTrack.width
                duration: 1800
                loops: Animation.Infinite
            }
        }
    }

    ColumnLayout {
        anchors.fill: parent
        anchors.margins: Kirigami.Units.smallSpacing * 2
        anchors.topMargin: Kirigami.Units.smallSpacing * 2 + 2
        spacing: Kirigami.Units.smallSpacing

        RowLayout {
            Layout.fillWidth: true
            PlasmaComponents3.Label {
                Layout.fillWidth: true
                elide: Text.ElideRight
                font.pointSize: cardItem.tiny ? Kirigami.Theme.defaultFont.pointSize
                                              : Kirigami.Theme.smallFont.pointSize
                font.bold: cardItem.tiny
                opacity: cardItem.tiny ? 1 : 0.7
                text: cardItem.tiny ? Status.jobLabel(cardItem.job)
                                    : (cardItem.card.repository || {}).full_name || ""
            }
            PlasmaComponents3.Label {
                font.family: "monospace"
                font.bold: true
                color: Kirigami.Theme.neutralTextColor
                text: Status.clock(cardItem.job.started_at, cardItem.now)
            }
        }

        PlasmaComponents3.Label {
            visible: !cardItem.tiny
            Layout.fillWidth: true
            elide: Text.ElideRight
            font.bold: true
            text: Status.jobLabel(cardItem.job)
        }

        PlasmaComponents3.Label {
            visible: cardItem.roomForMeta
            Layout.fillWidth: true
            elide: Text.ElideRight
            font: Kirigami.Theme.smallFont
            opacity: 0.7
            text: ["#" + cardItem.run.number,
                   Status.runTitle(cardItem.run),
                   [Status.shortRef(cardItem.run.head_ref), Status.shortSha(cardItem.run.head_sha)]
                       .filter(function(s) { return s !== "" }).join("@")]
                  .filter(function(s) { return s !== "" }).join(" · ")
        }

        // A segment per step, coloured by how it went.
        RowLayout {
            visible: cardItem.roomForSteps && cardItem.steps.length > 0
            Layout.fillWidth: true
            // Its segments fill its height; it mustn't fill the card's.
            Layout.fillHeight: false
            Layout.preferredHeight: 4
            Layout.maximumHeight: 4
            spacing: 2
            Repeater {
                model: cardItem.steps
                Rectangle {
                    required property var modelData
                    readonly property string status: Status.effective(modelData.status, modelData.conclusion)
                    Layout.fillWidth: true
                    Layout.fillHeight: true
                    radius: 2
                    color: status === "running" || status === "in_progress"
                           ? Kirigami.Theme.neutralTextColor
                           : Status.isFinished(status) ? Status.color(status, Kirigami.Theme)
                                                       : Qt.alpha(Kirigami.Theme.disabledTextColor, 0.3)
                }
            }
        }

        PlasmaComponents3.Label {
            readonly property var step: Status.currentStep(cardItem.steps)
            visible: cardItem.roomForSteps && step !== null
            Layout.fillWidth: true
            elide: Text.ElideRight
            font: Kirigami.Theme.smallFont
            text: step ? (step.name || ("Step " + step.number)) : ""
        }

        // The newest lines, bottom-aligned; older ones run off the top
        // under a fade.
        Item {
            id: logBox
            visible: cardItem.roomForLog
            Layout.fillWidth: true
            Layout.fillHeight: true
            clip: true

            readonly property real lineHeight: logText.font.pixelSize * 1.3
            readonly property int fits: Math.ceil(height / Math.max(1, lineHeight)) + 1

            Text {
                id: logText
                anchors { left: parent.left; right: parent.right; bottom: parent.bottom }
                font.family: "monospace"
                font.pixelSize: Math.round(Kirigami.Theme.smallFont.pixelSize > 0
                                           ? Kirigami.Theme.smallFont.pixelSize * 0.95 : 10)
                lineHeight: 1.3
                lineHeightMode: Text.ProportionalHeight
                wrapMode: Text.NoWrap
                elide: Text.ElideNone
                textFormat: Text.PlainText
                color: Kirigami.Theme.textColor
                opacity: 0.85
                text: (cardItem.card.log || []).slice(-logBox.fits).join("\n")
            }

            Rectangle {
                anchors { left: parent.left; right: parent.right; top: parent.top }
                height: parent.height * 0.3
                gradient: Gradient {
                    GradientStop { position: 0; color: cardItem.color }
                    GradientStop { position: 1; color: Qt.alpha(cardItem.color, 0) }
                }
            }
        }

        Item { visible: !cardItem.roomForLog; Layout.fillHeight: true }

        RowLayout {
            visible: cardItem.roomForMeta
            Layout.fillWidth: true
            spacing: Kirigami.Units.smallSpacing
            Kirigami.Icon {
                source: "cpu"
                Layout.preferredWidth: Kirigami.Units.iconSizes.small
                Layout.preferredHeight: Kirigami.Units.iconSizes.small
                opacity: 0.6
            }
            PlasmaComponents3.Label {
                Layout.fillWidth: true
                elide: Text.ElideRight
                font: Kirigami.Theme.smallFont
                opacity: 0.6
                text: [(cardItem.card.runner || {}).name || "runner", (cardItem.card.runner || {}).architecture || ""]
                      .filter(function(s) { return s !== "" }).join(" · ")
            }
        }
    }
}