kde-widget/com.umorist47.meowrelaygui/contents/ui/main.qml
2026-02-23 05:28:35 +03:00

1091 lines
48 KiB
QML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import org.kde.kirigami as Kirigami
import org.kde.plasma.core as PlasmaCore
import org.kde.plasma.components as PlasmaComponents
import org.kde.plasma.extras as PlasmaExtras
import org.kde.plasma.plasmoid
PlasmoidItem {
id: root
Plasmoid.title: "MeowRelay"
// compactRepresentation extra content
property var iflag: getFlag(current)
property var iflag_extra: getFlag(current) + current
// extra
hideOnWindowDeactivate: !plasmoid.configuration.isPinned
property var scrollbartype: Plasmoid.configuration.SBdisplay ? ScrollBar.AsNeeded : ScrollBar.AlwaysOff
//me/profile
property var profileData: null
property alias profileDataAlias: root.profileData
property string user: profileData.user
property bool is_admin: profileData.user_data.is_admin
property bool share: profileData.user_data.share
property bool ebalka: profileData.ebalka
property string current_device: profileData.current_device
property string current: profileData.current
property string provider: profileData.provider
property string ip: profileData.ip
property var features: profileData.features
property var devices: profileData.devices
//routes
property var routesData: null
//stats
property var statsData: null
property double uptime: statsData.uptime
property var servers: statsData.servers
// tabs json responses
ListModel { id: routeModel }
ListModel { id: statsModel }
ListModel { id: profileModel }
ListModel { id: devicesModel }
ListModel { id: featuresModel }
// buttons blocker (antispam)
property int isLoadingItems: 0
property bool isLoading: false // колясочные технологии, по итогу юзается токо isLoadingItems
property bool isBlocked: false
property bool shareBlocked: false
property bool ebalkaBlocked: false
property bool refreshBlocked: false
property bool countriesBlocked: false
property bool devicesBlocked: false
property bool featuresBlocked: false
onIsLoadingItemsChanged: {
if (isLoadingItems > 0) {
isLoading = true
}
else if (isLoadingItems < 0) {
isLoading = false
isLoadingItems = 0
}
else {
isLoading = false
}
}
// flags
function getFlag(code) {
if (!code || code.length !== 2) return '❔';
let upper = code.toUpperCase();
let c1 = upper.charCodeAt(0) + 127397;
let c2 = upper.charCodeAt(1) + 127397;
return String.fromCodePoint(c1, c2);
}
// traffic
function formatTraffic(bytes) {
if (bytes === 0) return "0 B";
var units = ["B", "KB", "MB", "GB", "TB"];
var i = Math.floor(Math.log(bytes) / Math.log(1024));
var size = (bytes / Math.pow(1024, i)).toFixed(1);
return parseFloat(size) + " " + units[i];
}
Timer {
id: meowRelayWatchdog
interval: 10000
repeat: false
onTriggered: {
console.error("MeowRelay unreachable!") //ваша няшка умерла
routeModel.clear()
routeModel.append({"code":"ERR","name":"Error, MeowRelay unreachable :<","provider":"NONE"})
routesData = [{"code":"ERR","name":"Error, MeowRelay unreachable :<","provider":"NONE"}]
profileData = {"provider":"NONE"}
statsData = null
isBlocked = false
isLoadingItems = 0
countriesBlocked = true
shareBlocked = true
ebalkaBlocked = true
devicesBlocked = true
featuresBlocked = true
Plasmoid.configuration.OnErrorRefresh ? (
meowRelayWatchdog.start(),
isLoadingItems += Plasmoid.configuration.BIrefresh,
//isBlocked = true,
superWorker.sendMessage({ "action": "fetch_routes" }),
superWorker.sendMessage({ "action": "fetch_profile" }),
superWorker.sendMessage({ "action": "fetch_stats" }),
fetchRoutesTimer.restart(),
fetchProfileTimer.restart(),
fetchStatsTimer.restart()
) : null
}
}
Timer {
id: meowRelayWatchdogStats
interval: 10000
repeat: false
onTriggered: {
console.error("MeowRelay unreachable!") //ваша няшка умерла
refreshBlocked = false
}
}
Timer {
id: meowRelayPing
interval: Plasmoid.configuration.AutoPingInt * 1000
running: Plasmoid.configuration.AutoPing
repeat: false
onTriggered: {
superWorker.sendMessage({ "action": "ping" })
console.log("MeowRelay ping pong")
}
}
// раб в отдельном потоке чтобы кде не фризило
WorkerScript {
id: superWorker
source: "worker.mjs"
property var handlers: ({
"fetch_routes": ({ data }) => {
routeModel.clear();
routesData = data;
for (let i = 0; i < data.length; i++) {
routeModel.append(data[i]);
};
if (Plasmoid.configuration.BIrefresh || Plasmoid.configuration.BIautoupdate) {
isLoadingItems -= 1
};
refreshBlocked = false;
},
"fetch_stats": ({ data }) => {
statsModel.clear();
statsData = data;
var serverArray = [];
for (var Name in servers) {
if (servers.hasOwnProperty(Name)) {
var item = servers[Name];
item.name = Name;
item.pingString = item.ping.join(" ");
item.trafficString = item.traffic.join(" ");
item.speedString = item.speed.join(" ");
serverArray.push(item);
}
}
serverArray.sort(function(a, b) {
return b.connected - a.connected;
});
for (let i = 0; i < serverArray.length; i++) {
statsModel.append(serverArray[i]);
};
if (Plasmoid.configuration.BIrefresh || Plasmoid.configuration.BIautoupdate) {
isLoadingItems -= 1
};
meowRelayWatchdogStats.stop();
refreshBlocked = false;
},
"fetch_profile": ({ data }) => {
profileModel.clear();
devicesModel.clear();
featuresModel.clear();
profileData = data;
for (let i = 0; i < data.length; i++) {
profileModel.append(data[i]);
}
for (let i = 0; i < data.devices.length; i++) {
devicesModel.append(data.devices[i])
}
for (let i = 0; i < data.features.length; i++) {
featuresModel.append(data.features[i])
}
if (Plasmoid.configuration.BIrefresh || Plasmoid.configuration.BIautoupdate) {
isLoadingItems -= 1
}
meowRelayWatchdog.stop();
isBlocked = false;
refreshBlocked = false;
countriesBlocked = false;
shareBlocked = false;
ebalkaBlocked = false;
devicesBlocked = false;
featuresBlocked = false;
},
"post_country": ({ status }) => {
if (status === 200) {
superWorker.sendMessage({ "action": "fetch_profile" })
//done
}
else {
superWorker.sendMessage({ "actions": "fetch_routes" })
//error
};
if (Plasmoid.configuration.BIpostcountry) {
isLoadingItems -= 1
}
},
"post_share": ({ status }) => {
if (status === 200) {
superWorker.sendMessage({ "action": "fetch_profile" })
//done
}
else {
superWorker.sendMessage({ "action": "fetch_profile" })
// share = !share
//error
};
if (Plasmoid.configuration.BIpostshare) {
isLoadingItems -= 1
}
shareBlocked = false;
},
"post_ebalka": ({ status, ip }) => {
if (status === 200 && ip != null) {
superWorker.sendMessage({ "action": "fetch_profile" })
//done
}
else {
superWorker.sendMessage({ "action": "fetch_profile" }) // кофе
//error
};
if (Plasmoid.configuration.BIpostebalka) {
isLoadingItems -= 1
}
if (Plasmoid.configuration.BIdevicesupdate) {
isLoadingItems -= 1
}
ebalkaBlocked = false;
devicesBlocked = false;
},
"post_name": ({ status }) => {
if (status === 200) {
superWorker.sendMessage({ "action": "fetch_profile" })
//done
}
else {
//error
};
if (Plasmoid.configuration.BIdevicesupdate) {
isLoadingItems -= 1
}
devicesBlocked = false;
},
"post_feature": ({ status }) => {
superWorker.sendMessage({ "action": "fetch_profile" });
if (Plasmoid.configuration.BIfeaturesupdate) {
isLoadingItems -= 1
}
featuresBlocked = false;
},
"post_idconnect": ({ status }) => {
superWorker.sendMessage({ "action": "fetch_profile" });
superWorker.sendMessage({ "action": "fetch_routes" });
if (Plasmoid.configuration.BIpostcountry) {
isLoadingItems -= 1
}
}
})
onMessage: (msg) => {
if (handlers[msg.action]) {
handlers[msg.action](msg);
}
}
}
// init fetch
Component.onCompleted: {
isLoadingItems += Plasmoid.configuration.BIinit
isBlocked = true
meowRelayWatchdog.start()
superWorker.sendMessage({ "action": "fetch_routes" })
superWorker.sendMessage({ "action": "fetch_stats" })
superWorker.sendMessage({ "action": "fetch_profile" })
fetchRoutesTimer.start()
fetchStatsTimer.start()
fetchProfileTimer.start()
meowRelayPing.start()
}
// autoupdate fetch
Timer {
id: fetchRoutesTimer
interval: (Plasmoid.configuration.RoutesUpInt * 60000)
running: Plasmoid.configuration.RoutesAutoUp
repeat: true
triggeredOnStart: false
onTriggered: {
refreshBlocked = true
isLoadingItems += Plasmoid.configuration.BIautoupdate
superWorker.sendMessage({ "action": "fetch_routes" })
}
}
Timer {
id: fetchStatsTimer
interval: (Plasmoid.configuration.StatsUpInt * 60000)
running: Plasmoid.configuration.StatsAutoUp
repeat: true
triggeredOnStart: false
onTriggered: {
refreshBlocked = true
isLoadingItems += Plasmoid.configuration.BIautoupdate
meowRelayWatchdogStats.start()
superWorker.sendMessage({ "action": "fetch_stats" })
}
}
Timer {
id: fetchProfileTimer
interval: (Plasmoid.configuration.ProfileUpInt * 60000)
running: Plasmoid.configuration.ProfileAutoUp
repeat: true
triggeredOnStart: false
onTriggered: {
refreshBlocked = true
isLoadingItems += Plasmoid.configuration.BIautoupdate
meowRelayWatchdog.start()
superWorker.sendMessage({ "action": "fetch_profile" })
}
}
compactRepresentation: Item {
Layout.minimumWidth: govnoLayout.implicitWidth
Layout.minimumHeight: Kirigami.Units.iconSizes.medium
Layout.maximumWidth: govnoLayout.implicitWidth
Layout.maximumHeight: Layout.minimumHeight
Layout.preferredWidth: govnoLayout.implicitWidth
Layout.preferredHeight: Layout.minimumHeight
MouseArea {
id: mouseArea
//property bool wasExpanded
Accessible.name: Plasmoid.title
Accessible.role: Accessible.Button
//Accessible.description: desc
//onPressed: wasExpanded = root.expanded
//onClicked: root.expanded = !wasExpanded
onClicked: Plasmoid.activated();
Keys.onPressed: {
switch (event.key) {
case Qt.Key_Space:
case Qt.Key_Enter:
case Qt.Key_Return:
case Qt.Key_Select:
Plasmoid.activated();
break;
}
}
// иконки не будет без этой залупы
anchors.fill: parent
anchors.leftMargin: 0
activeFocusOnTab: true
hoverEnabled: true
Component {
id: meowIconComponent
Kirigami.Icon {
Layout.preferredWidth: Kirigami.Units.gridUnit * 1.5
Layout.preferredHeight: Kirigami.Units.gridUnit * 1.5
Layout.alignment: Qt.AlignVCenter
source: Qt.resolvedUrl("../images/dogfood.svg")
isMask: true
active: mouseArea.containsMouse
}
}
Component {
id: genericLabelComponent
Label {
property string labelText: "yaebalrot"
text: labelText === "yaebalrot" ? "" : labelText
color: text === "error" ? "red" : Kirigami.Theme.textColor
font.pointSize: Kirigami.Theme.mediumFont.pointSize
verticalAlignment: Text.AlignVCenter
}
}
property var configArray: Plasmoid.configuration.ExtraLayout === true ? Plasmoid.configuration.StringLayoutPlacement.split(" ") : ["meow_icon"]
RowLayout {
id: govnoLayout
anchors.fill: parent
spacing: Kirigami.Units.smallSpacing
Repeater {
model: mouseArea.configArray
Loader {
id: govnoebanoe
// Logic to choose WHICH component to spawn
sourceComponent: (modelData === "meow_icon") ? meowIconComponent : genericLabelComponent
// Logic to pass DATA to that component
Binding {
target: govnoebanoe.item // The Label we just created
property: "labelText" // The property on the Label
value: root[modelData] // The live variable in root
when: govnoebanoe.status === Loader.Ready && modelData !== "meow_icon"
}
Layout.fillHeight: true
}
}
}
// Kirigami.Icon {
// anchors.fill: parent
// source: Qt.resolvedUrl("../images/dogfood.svg")
// isMask: true
// active: mouseArea.containsMouse
// }
}
}
fullRepresentation: PlasmaExtras.Representation {
//id: fullRoot
Layout.minimumWidth: Kirigami.Units.gridUnit * 25
Layout.minimumHeight: Kirigami.Units.gridUnit * 30
Layout.preferredWidth: Kirigami.Units.gridUnit * 25
Layout.preferredHeight: Kirigami.Units.gridUnit * 30
//spacing: 0
header: PlasmaExtras.PlasmoidHeading {
contentItem: ColumnLayout {
spacing: Kirigami.Units.smallSpacing
// row 1 (name)
RowLayout {
Layout.fillWidth: true
PlasmaExtras.Heading {
text: " " + plasmoid.title
level: 1
Layout.fillWidth: false
elide: Text.ElideRight
}
Rectangle {
height: Kirigami.Units.gridUnit
Layout.alignment: AlignVCenter
width: 1
color: PlasmaCore.Theme.dividerColor
opacity: 0.7
}
PlasmaExtras.Heading {
text: (user ? user : "unknown")
opacity: 0.7
level: 1
Layout.fillWidth: true
elide: Text.ElideRight
}
// config button
PlasmaComponents.ToolButton {
id: configButton
icon.name: "configure"
display: ToolButton.IconOnly
onClicked: plasmoid.internalAction("configure").trigger()
ToolTip.text: i18n("Configure")
ToolTip.visible: hovered
ToolTip.delay: Kirigami.Units.toolTipDelay
KeyNavigation.right: pinButton
}
// pin button
PlasmaComponents.ToolButton {
id: pinButton
icon.name: "window-pin"
display: ToolButton.IconOnly
checkable: true
checked: plasmoid.configuration.isPinned
down: checked
onToggled: plasmoid.configuration.isPinned = checked
ToolTip.text: i18n("Keep Open")
ToolTip.visible: hovered
ToolTip.delay: Kirigami.Units.toolTipDelay
KeyNavigation.left: configButton
visible: plasmoid.formFactor === 2 || plasmoid.formFactor === 3
enabled: visible
}
}
// row 2 (tabs)
PlasmaComponents.TabBar {
id: mainTabBar
Layout.fillWidth: true
Repeater {
model: ["Route", "Devices", "Stats", "Features"]
delegate: PlasmaComponents.TabButton {
text: modelData
}
}
}
}
}
// Footer
footer: PlasmaExtras.PlasmoidHeading {
id: footerItem
ColumnLayout {
anchors.fill: parent
PlasmaComponents.ProgressBar {
indeterminate: true
//visible: isLoading
visible: false
Layout.fillWidth: true
}
RowLayout {
spacing: Kirigami.Units.smallSpacing
PlasmaComponents.Label {
text: "Up:"
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
PlasmaComponents.Label {
text: (Math.floor(uptime / 86400)) + "d " + (Math.floor((uptime % 86400) / 3600)) + "h " + (Math.floor(((uptime % 86400) % 3600) / 60)) + "m"
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
Item {
Layout.fillWidth: true
}
PlasmaComponents.Label {
text: getFlag(current) + current
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
PlasmaComponents.Label {
text: provider
opacity: 0.7
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
Rectangle {
height: Kirigami.Units.gridUnit
Layout.alignment: AlignVCenter
width: 1
color: PlasmaCore.Theme.dividerColor
opacity: 0.2
}
PlasmaComponents.Label {
text: "Device:"
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
PlasmaComponents.Label {
text: current_device
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
Rectangle {
height: Kirigami.Units.gridUnit
Layout.alignment: AlignVCenter
width: 1
color: PlasmaCore.Theme.dividerColor
opacity: 0.2
}
PlasmaComponents.Label {
text: "Ip:"
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
PlasmaComponents.Label {
text: ip
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
}
RowLayout {
PlasmaComponents.Button {
text: "Refresh"
icon.name: "view-refresh"
onClicked: {
meowRelayWatchdog.running ? meowRelayWatchdog.stop() : null
meowRelayWatchdog.start()
isLoadingItems += Plasmoid.configuration.BIrefresh
isBlocked = true
superWorker.sendMessage({ "action": "fetch_routes" })
superWorker.sendMessage({ "action": "fetch_profile" })
superWorker.sendMessage({ "action": "fetch_stats" })
fetchRoutesTimer.restart()
fetchProfileTimer.restart()
fetchStatsTimer.restart()
}
enabled: !(isBlocked || refreshBlocked)
Layout.fillWidth: false
}
PlasmaComponents.BusyIndicator {
running: isLoadingItems //isLoading
implicitWidth: 28
implicitHeight: 28
}
Item {
Layout.fillWidth: true
}
PlasmaComponents.Switch {
text: "Share"
checked: share
enabled: !(isBlocked || shareBlocked)
onClicked: {
isLoadingItems += Plasmoid.configuration.BIpostshare
shareBlocked = true
superWorker.sendMessage({ "action": "post_share", "bool": !share })
share = !share
}
}
Rectangle {
height: Kirigami.Units.gridUnit
Layout.alignment: AlignVCenter
width: 1
color: PlasmaCore.Theme.dividerColor
opacity: 0.2
}
PlasmaComponents.Switch {
text: "80% loss"
checked: ebalka
enabled: !(isBlocked || ebalkaBlocked)
onClicked: {
isLoadingItems += Plasmoid.configuration.BIpostebalka
ebalkaBlocked = true
superWorker.sendMessage({ "action": "post_ebalka", "bool": !ebalka, "ip": null })
ebalka = !ebalka
}
}
}
}
}
// Content
contentItem: Item {
StackLayout {
id: tabStack
anchors.fill: parent
currentIndex: mainTabBar.currentIndex
// Tab 1 Routes
PlasmaComponents.ScrollView {
id: routeScroll
anchors.fill: parent
ScrollBar.vertical.policy: scrollbartype
Column {
id: listContainer
width: routeScroll.availableWidth
spacing: 0
// Countries
Repeater {
model: routeModel
delegate: PlasmaComponents.RadioDelegate {
width: listContainer.width
height: Kirigami.Units.gridUnit * 3
enabled: !countriesBlocked
checked: (model.code === current && model.provider === provider)
contentItem: RowLayout {
spacing: Kirigami.Units.largeSpacing
PlasmaComponents.Label {
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
wrapMode: Text.Wrap
font.pixelSize: 24
text: getFlag(model.code)
}
ColumnLayout {
spacing: 0
Layout.fillWidth: true
PlasmaComponents.Label {
text: model.name
font.bold: true
Layout.fillWidth: true
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
PlasmaComponents.Label {
text: model.provider
font.pointSize: Kirigami.Theme.smallFont.pointSize
opacity: 0.7
Layout.fillWidth: true
verticalAlignment: Text.AlignVCenter
}
}
}
onClicked: {
isLoadingItems += Plasmoid.configuration.BIpostcountry
superWorker.sendMessage({ "action": "post_country", "code": model.code, "provider": model.provider })
}
}
}
}
}
PlasmaComponents.ScrollView {
id: devicesScroll
anchors.fill: parent
ScrollBar.vertical.policy: scrollbartype
Column {
id: deviceListContainer
width: devicesScroll.availableWidth
spacing: Kirigami.Units.smallSpacing
Item {
width: deviceListContainer.width
height: Kirigami.Units.smallSpacing/2
}
// devices
Repeater {
model: devicesModel
delegate: PlasmaComponents.Button {
id: devicesButton
width: deviceListContainer.width
height: Kirigami.Units.gridUnit * 4
contentItem: RowLayout {
x: Kirigami.Units.largeSpacing
y: Kirigami.Units.largeSpacing
width: devicesButton.availableWidth
spacing: Kirigami.Units.smallSpacing
ColumnLayout {
spacing: 0
RowLayout {
Layout.fillWidth: true
PlasmaComponents.Label {
text: model.name
font.bold: true
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
PlasmaComponents.Label {
text: "(LOSS 80%)"
color: "#f38ba8"
visible: model.ebalka
Layout.fillWidth: true
}
}
RowLayout {
PlasmaComponents.Label {
text: model.ip
font.pointSize: Kirigami.Theme.smallFont.pointSize
opacity: 0.7
verticalAlignment: Text.AlignVCenter
}
PlasmaComponents.Label {
text: "↓"
color: "#00ff00"
}
PlasmaComponents.Label {
text: formatTraffic(model.rx)
font.pointSize: Kirigami.Theme.smallFont.pointSize
verticalAlignment: Text.AlignVCenter
}
PlasmaComponents.Label {
text: "↑"
color: "#00aaff"
}
PlasmaComponents.Label {
text: formatTraffic(model.tx)
font.pointSize: Kirigami.Theme.smallFont.pointSize
verticalAlignment: Text.AlignVCenter
}
}
RowLayout {
PlasmaComponents.Label {
text: getFlag(model.country)
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
PlasmaComponents.Label {
text: model.country + " " + model.provider
opacity: 0.7
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
PlasmaComponents.Label {
text: (model.online ? "online" : "offline")
color: (model.online ? "#a6e3a1" : "#f38ba8")
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
}
}
Item {
Layout.fillWidth: true
}
ColumnLayout {
RowLayout {
Item {
Layout.fillWidth: true
}
PlasmaComponents.Switch {
text: "80% loss"
checked: model.ebalka
enabled: !(isBlocked || devicesBlocked)
onClicked: {
isLoadingItems += Plasmoid.configuration.BIdevicesupdate
devicesBlocked = true
superWorker.sendMessage({ "action": "post_ebalka", "bool": !model.ebalka, "ip": model.ip })
// ebalka = !ebalka
}
}
}
RowLayout {
Item {
Layout.fillWidth: true
}
PlasmaComponents.Button {
id: menuButton
text: "Rename"
PlasmaComponents.Menu {
id: inputMenu
padding: Kirigami.Units.largeSpacing
function confirmAction() {
superWorker.sendMessage({ "action": "post_name", "ip": model.ip, "name": textInput.text })
inputMenu.close();
textInput.text = "";
}
ColumnLayout {
spacing: Kirigami.Units.smallSpacing
RowLayout {
PlasmaComponents.Label {
text: "Enter new name:"
font.pixelSize: Kirigami.Units.gridUnit * 0.8
}
}
RowLayout {
PlasmaComponents.TextField {
id: textInput
placeholderText: (model.name).split(" ").slice(2).join(" ")
focus: true
onAccepted: inputMenu.confirmAction()
}
PlasmaComponents.Button {
icon.name: "dialog-ok-apply"
onClicked: inputMenu.confirmAction()
}
}
}
}
onClicked: {
inputMenu.open();
textInput.forceActiveFocus();
}
}
}
}
}
//onClicked: {
//}
}
}
Item {
width: deviceListContainer.width
height: Kirigami.Units.smallSpacing/2
}
}
}
PlasmaComponents.ScrollView {
id: statsScroll
anchors.fill: parent
ScrollBar.vertical.policy: scrollbartype
Column {
id: statsListContainer
width: statsScroll.availableWidth
spacing: Kirigami.Units.smallSpacing
Item {
width: statsListContainer.width
height: Kirigami.Units.smallSpacing/2
}
// stats
Repeater {
model: statsModel
delegate: PlasmaComponents.Button {
id: statsButton
width: statsListContainer.width
height: Kirigami.Units.gridUnit * 4
enabled: !(isBlocked || countriesBlocked)
contentItem: RowLayout {
x: Kirigami.Units.largeSpacing
y: Kirigami.Units.largeSpacing
width: statsButton.availableWidth
spacing: Kirigami.Units.smallSpacing
ColumnLayout {
spacing: 0
RowLayout {
Layout.fillWidth: true
PlasmaComponents.Label {
text: getFlag(model.country)
elide: Text.ElideRight
font.pixelSize: 16
verticalAlignment: Text.AlignVCenter
}
PlasmaComponents.Label {
text: model.name
font.bold: true
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
}
PlasmaComponents.Label {
text: "Up: " + (Math.floor(model.uptime / 86400)) + "d " + (Math.floor((model.uptime % 86400) / 3600)) + "h " + (Math.floor(((model.uptime % 86400) % 3600) / 60)) + "m"
}
}
Item {
Layout.fillWidth: true
}
ColumnLayout {
spacing: 0
RowLayout {
Item {
Layout.fillWidth: true
}
PlasmaComponents.Label {
text: model.connected + " peers (online: " + model.online + ")"
color: "#a6e3a1"
font.bold: true
}
}
RowLayout {
Item {
Layout.fillWidth: true
}
PlasmaComponents.Label {
text: "↓"
color: "#00ff00"
}
PlasmaComponents.Label {
text: formatTraffic(model.trafficString.split(" ")[0])
}
PlasmaComponents.Label {
text: "(" + formatTraffic(model.speedString.split(" ")[0]) + ")"
opacity: 0.7
}
PlasmaComponents.Label {
text: "↑"
color: "#00aaff"
}
PlasmaComponents.Label {
text: formatTraffic(model.trafficString.split(" ")[1])
}
PlasmaComponents.Label {
text: "(" + formatTraffic(model.speedString.split(" ")[1]) + ")"
opacity: 0.7
}
}
RowLayout {
Item {
Layout.fillWidth: true
}
PlasmaComponents.Label {
text: model.pingString.split(" ").join(", ") + " ms"
color: "#a6e3a1"
font.bold: true
}
}
}
}
onClicked: {
isLoadingItems += Plasmoid.configuration.BIpostcountry
superWorker.sendMessage({ "action": "post_idconnect", "iface": model.name })
}
}
}
Item {
width: statsListContainer.width
height: Kirigami.Units.smallSpacing/2
}
}
}
PlasmaComponents.ScrollView {
id: featuresScroll
anchors.fill: parent
ScrollBar.vertical.policy: scrollbartype
Column {
id: featuresListContainer
width: featuresScroll.availableWidth
spacing: Kirigami.Units.smallSpacing
Item {
width: featuresListContainer.width
height: Kirigami.Units.smallSpacing/2
}
// features
Repeater {
model: featuresModel
delegate: PlasmaComponents.Button {
id: featuresButton
width: featuresListContainer.width
height: Kirigami.Units.gridUnit * 4
contentItem: RowLayout {
x: Kirigami.Units.largeSpacing
y: Kirigami.Units.largeSpacing
width: featuresButton.availableWidth
spacing: Kirigami.Units.smallSpacing
ColumnLayout {
spacing: 2
Layout.fillWidth: true
PlasmaComponents.Label {
text: model.name
elide: Text.ElideRight
//font.pixelSize: 16
verticalAlignment: Text.AlignVCenter
}
PlasmaComponents.Label {
text: model.desc
wrapMode: Text.Wrap
opacity: 0.7
//font.bold: true
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
Layout.fillWidth: true
}
}
PlasmaComponents.Switch {
checked: model.value
enabled: !(isBlocked || featuresBlocked)
onClicked: {
isLoadingItems += Plasmoid.configuration.BIfeaturesupdate
//featuresBlocked = true
superWorker.sendMessage({ "action": "post_feature", "name": model.name, "value": !model.value })
}
}
}
//onClicked: {
//}
}
}
Item {
width: featuresListContainer.width
height: Kirigami.Units.smallSpacing/2
}
}
}
// Placeholder for other tabs
// PlasmaExtras.PlaceholderMessage { text: "Chairy Support" } // да в пизду крч нахуй надо
}
}
}
}