417 lines
15 KiB
JavaScript
417 lines
15 KiB
JavaScript
import App from 'resource:///com/github/Aylur/ags/app.js';
|
|
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
import Network from "resource:///com/github/Aylur/ags/service/network.js";
|
|
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
|
const { Box, Button, Entry, Icon, Label, Revealer, Scrollable, Slider, Stack, Overlay } = Widget;
|
|
const { execAsync, exec } = Utils;
|
|
import { MaterialIcon } from '../../.commonwidgets/materialicon.js';
|
|
import { setupCursorHover } from '../../.widgetutils/cursorhover.js';
|
|
import { ConfigToggle } from '../../.commonwidgets/configwidgets.js';
|
|
|
|
const MATERIAL_SYMBOL_SIGNAL_STRENGTH = {
|
|
'network-wireless-signal-excellent-symbolic': "signal_wifi_4_bar",
|
|
'network-wireless-signal-good-symbolic': "network_wifi_3_bar",
|
|
'network-wireless-signal-ok-symbolic': "network_wifi_2_bar",
|
|
'network-wireless-signal-weak-symbolic': "network_wifi_1_bar",
|
|
'network-wireless-signal-none-symbolic': "signal_wifi_0_bar",
|
|
}
|
|
|
|
let connectAttempt = '';
|
|
let networkAuth = null;
|
|
let networkAuthSSID = null;
|
|
|
|
const WifiNetwork = (accessPoint) => {
|
|
const networkStrength = MaterialIcon(MATERIAL_SYMBOL_SIGNAL_STRENGTH[accessPoint.iconName], 'hugerass')
|
|
const networkName = Box({
|
|
vertical: true,
|
|
children: [
|
|
Label({
|
|
hpack: 'start',
|
|
label: accessPoint.ssid
|
|
}),
|
|
accessPoint.active ? Label({
|
|
hpack: 'start',
|
|
className: 'txt-smaller txt-subtext',
|
|
label: getString("Selected"),
|
|
}) : null,
|
|
]
|
|
});
|
|
return Button({
|
|
onClicked: accessPoint.active ? () => { } : () => {
|
|
connectAttempt = accessPoint.ssid;
|
|
networkAuthSSID.label = `${getString('Connecting to')}: ${connectAttempt}`;
|
|
|
|
// Check if the SSID is stored
|
|
execAsync(['nmcli', '-g', 'NAME', 'connection', 'show'])
|
|
.then((savedConnections) => {
|
|
const savedSSIDs = savedConnections.split('\n');
|
|
|
|
if (!savedSSIDs.includes(connectAttempt)) { // SSID not saved: show password input
|
|
if (networkAuth) {
|
|
networkAuth.revealChild = true;
|
|
}
|
|
} else { // If SSID is saved, hide password input
|
|
if (networkAuth) {
|
|
networkAuth.revealChild = false;
|
|
}
|
|
// Connect
|
|
execAsync(['nmcli', 'device', 'wifi', 'connect', connectAttempt])
|
|
.catch(print);
|
|
}
|
|
})
|
|
.catch(print);
|
|
|
|
},
|
|
child: Box({
|
|
className: 'sidebar-wifinetworks-network spacing-h-10',
|
|
children: [
|
|
networkStrength,
|
|
networkName,
|
|
Box({ hexpand: true }),
|
|
accessPoint.active ? MaterialIcon('check', 'large') : null,
|
|
],
|
|
}),
|
|
setup: accessPoint.active ? () => { } : setupCursorHover,
|
|
})
|
|
}
|
|
|
|
const NetResource = (icon, command) => {
|
|
const resourceLabel = Label({
|
|
className: `txt-smaller txt-subtext`,
|
|
});
|
|
const widget = Button({
|
|
child: Box({
|
|
hpack: 'start',
|
|
className: `spacing-h-4`,
|
|
children: [
|
|
MaterialIcon(icon, 'very-small'),
|
|
resourceLabel,
|
|
],
|
|
setup: (self) => self.poll(2000, () => execAsync(['bash', '-c', command])
|
|
.then((output) => {
|
|
resourceLabel.label = output;
|
|
}).catch(print))
|
|
,
|
|
})
|
|
});
|
|
return widget;
|
|
}
|
|
|
|
const CurrentNetwork = () => {
|
|
let passwordVisible = false;
|
|
let authLock = false;
|
|
let timeoutId = null;
|
|
|
|
const bottomSeparator = Box({
|
|
className: 'separator-line',
|
|
});
|
|
const networkName = Box({
|
|
vertical: true,
|
|
hexpand: true,
|
|
children: [
|
|
Label({
|
|
hpack: 'start',
|
|
className: 'txt-smaller txt-subtext',
|
|
label: getString("Current network"),
|
|
}),
|
|
Label({
|
|
hpack: 'start',
|
|
label: Network.wifi?.ssid,
|
|
setup: (self) => self.hook(Network, (self) => {
|
|
if (authLock) return;
|
|
self.label = Network.wifi?.ssid;
|
|
}),
|
|
}),
|
|
]
|
|
});
|
|
const networkBandwidth = Box({
|
|
vertical: true,
|
|
hexpand: true,
|
|
hpack: 'end',
|
|
className: 'sidebar-wifinetworks-bandwidth',
|
|
children: [
|
|
NetResource('arrow_warm_up', `${App.configDir}/scripts/network_scripts/network_bandwidth.py sent`),
|
|
NetResource('arrow_cool_down', `${App.configDir}/scripts/network_scripts/network_bandwidth.py recv`),
|
|
]
|
|
});
|
|
// const networkStatus = Box({
|
|
// children: [Label({
|
|
// vpack: 'center',
|
|
// className: 'txt-subtext',
|
|
// setup: (self) => self.hook(Network, (self) => {
|
|
// if (authLock) return;
|
|
// self.label = Network.wifi.state;
|
|
// }),
|
|
// })]
|
|
// });
|
|
networkAuthSSID = Label({
|
|
className: 'margin-left-5',
|
|
hpack: 'start',
|
|
hexpand: true,
|
|
label: '',
|
|
});
|
|
const cancelAuthButton = Button({
|
|
className: 'txt sidebar-wifinetworks-network-button',
|
|
label: getString('Cancel'),
|
|
hpack: 'end',
|
|
onClicked: () => {
|
|
passwordVisible = false;
|
|
authEntry.visibility = false;
|
|
networkAuth.revealChild = false;
|
|
authFailed.revealChild = false;
|
|
networkAuthSSID.label = '';
|
|
networkName.children[1].label = Network.wifi?.ssid;
|
|
authEntry.text = '';
|
|
},
|
|
setup: setupCursorHover,
|
|
});
|
|
const authHeader = Box({
|
|
vertical: false,
|
|
hpack: 'fill',
|
|
spacing: 10,
|
|
children: [
|
|
networkAuthSSID,
|
|
cancelAuthButton
|
|
]
|
|
});
|
|
const authVisible = Button({
|
|
vpack: 'center',
|
|
child: MaterialIcon('visibility', 'large'),
|
|
className: 'txt sidebar-wifinetworks-auth-visible',
|
|
onClicked: (self) => {
|
|
passwordVisible = !passwordVisible;
|
|
authEntry.visibility = passwordVisible;
|
|
self.child.label = passwordVisible ? 'visibility_off' : 'visibility';
|
|
},
|
|
setup: setupCursorHover,
|
|
});
|
|
const authFailed = Revealer({
|
|
revealChild: false,
|
|
child: Label({
|
|
className: 'txt txt-italic txt-subtext',
|
|
label: getString('Authentication failed'),
|
|
}),
|
|
})
|
|
const authEntry = Entry({
|
|
className: 'sidebar-wifinetworks-auth-entry',
|
|
visibility: false,
|
|
hexpand: true,
|
|
onAccept: (self) => {
|
|
authLock = false;
|
|
// Delete SSID connection before attempting to reconnect
|
|
execAsync(['nmcli', 'connection', 'delete', connectAttempt])
|
|
.catch(() => { }); // Ignore error if SSID not found
|
|
|
|
execAsync(['nmcli', 'device', 'wifi', 'connect', connectAttempt, 'password', self.text])
|
|
.then(() => {
|
|
connectAttempt = ''; // Reset SSID after successful connection
|
|
networkAuth.revealChild = false; // Hide input if successful
|
|
authFailed.revealChild = false; // Hide failed message if successful
|
|
self.text = ''; // Empty input for retry
|
|
passwordVisible = false;
|
|
authEntry.visibility = false;
|
|
})
|
|
.catch(() => {
|
|
// Connection failed, show password input again
|
|
networkAuth.revealChild = true;
|
|
authFailed.revealChild = true;
|
|
});
|
|
},
|
|
placeholderText: getString('Enter network password'),
|
|
});
|
|
const authBox = Box({
|
|
className: 'sidebar-wifinetworks-auth-box',
|
|
children: [
|
|
authEntry,
|
|
authVisible,
|
|
]
|
|
});
|
|
const forgetButton = Button({
|
|
label: getString('Forget'),
|
|
hexpand: true,
|
|
className: 'txt sidebar-wifinetworks-network-button',
|
|
onClicked: () => {
|
|
execAsync(['nmcli', '-t', '-f', 'ACTIVE,NAME', 'connection', 'show'])
|
|
.then(output => {
|
|
const activeSSID = output
|
|
.split('\n')
|
|
.find(line => line.startsWith('yes:'))
|
|
?.split(':')[1];
|
|
|
|
if (activeSSID) {
|
|
execAsync(['nmcli', 'connection', 'delete', activeSSID])
|
|
.catch(err => Utils.execAsync(['notify-send',
|
|
"Network",
|
|
`Failed to forget network - Hold to copy\n${err}`,
|
|
'-a', 'ags',
|
|
]).catch(print));
|
|
}
|
|
})
|
|
.catch();
|
|
},
|
|
setup: setupCursorHover,
|
|
});
|
|
const propertiesButton = Button({
|
|
label: getString('Properties'),
|
|
className: 'txt sidebar-wifinetworks-network-button',
|
|
hexpand: true,
|
|
onClicked: () => {
|
|
Utils.execAsync('nmcli -t -f uuid connection show --active').then(uuid => {
|
|
if (uuid.trim()) {
|
|
Utils.execAsync(`nm-connection-editor --edit ${uuid.trim()}`);
|
|
}
|
|
closeEverything();
|
|
}).catch(err => Utils.execAsync(['notify-send',
|
|
"Network",
|
|
`Failed to get connection UUID - Hold to copy\n${err}`,
|
|
'-a', 'ags',
|
|
]).catch(print));
|
|
},
|
|
setup: setupCursorHover,
|
|
});
|
|
const networkProp = Revealer({
|
|
transition: 'slide_down',
|
|
transitionDuration: userOptions.animations.durationLarge,
|
|
child: Box({
|
|
className: 'spacing-h-10',
|
|
homogeneous: true,
|
|
children: [
|
|
propertiesButton,
|
|
forgetButton,
|
|
],
|
|
setup: setupCursorHover,
|
|
}),
|
|
setup: (self) => self.hook(Network, (self) => {
|
|
if (Network.wifi?.ssid === '') self.revealChild = false;
|
|
else self.revealChild = true;
|
|
}),
|
|
});
|
|
networkAuth = Revealer({
|
|
transition: 'slide_down',
|
|
transitionDuration: userOptions.animations.durationLarge,
|
|
child: Box({
|
|
className: 'margin-top-10 spacing-v-5',
|
|
vertical: true,
|
|
children: [
|
|
authHeader,
|
|
authBox,
|
|
authFailed,
|
|
]
|
|
}),
|
|
setup: (self) => self.hook(Network, (self) => {
|
|
execAsync(['nmcli', '-g', 'NAME', 'connection', 'show'])
|
|
.then((savedConnections) => {
|
|
const savedSSIDs = savedConnections.split('\n');
|
|
if (Network.wifi.state == 'failed' ||
|
|
(Network.wifi.state == 'need_auth' && !savedSSIDs.includes(Network.wifi.ssid))) {
|
|
authLock = true;
|
|
connectAttempt = Network.wifi.ssid;
|
|
self.revealChild = true;
|
|
if (timeoutId) {
|
|
clearTimeout(timeoutId);
|
|
}
|
|
timeoutId = setTimeout(() => {
|
|
authLock = false;
|
|
passwordVisible = false;
|
|
authEntry.visibility = false;
|
|
self.revealChild = false;
|
|
authFailed.revealChild = false;
|
|
Network.wifi.state = 'activated';
|
|
}, 60000); // 60 seconds timeout
|
|
}
|
|
}
|
|
).catch(print);
|
|
}),
|
|
});
|
|
const actualContent = Box({
|
|
vertical: true,
|
|
className: 'spacing-v-10',
|
|
children: [
|
|
Box({
|
|
className: 'sidebar-wifinetworks-network',
|
|
vertical: true,
|
|
children: [
|
|
Box({
|
|
className: 'spacing-h-10 margin-bottom-10',
|
|
children: [
|
|
MaterialIcon('language', 'hugerass'),
|
|
networkName,
|
|
networkBandwidth,
|
|
// networkStatus,
|
|
]
|
|
}),
|
|
networkProp,
|
|
networkAuth
|
|
]
|
|
}),
|
|
bottomSeparator,
|
|
]
|
|
});
|
|
return Box({
|
|
vertical: true,
|
|
children: [Revealer({
|
|
transition: 'slide_down',
|
|
transitionDuration: userOptions.animations.durationLarge,
|
|
revealChild: Network.wifi,
|
|
child: actualContent,
|
|
})]
|
|
})
|
|
}
|
|
|
|
export default (props) => {
|
|
const networkList = Box({
|
|
vertical: true,
|
|
className: 'spacing-v-10',
|
|
children: [Overlay({
|
|
passThrough: true,
|
|
child: Scrollable({
|
|
vexpand: true,
|
|
child: Box({
|
|
attribute: {
|
|
'updateNetworks': (self) => {
|
|
const accessPoints = Network.wifi?.access_points || [];
|
|
self.children = Object.values(accessPoints.reduce((a, accessPoint) => {
|
|
// Only keep max strength networks by ssid
|
|
if (!a[accessPoint.ssid] || a[accessPoint.ssid].strength < accessPoint.strength) {
|
|
a[accessPoint.ssid] = accessPoint;
|
|
a[accessPoint.ssid].active |= accessPoint.active;
|
|
}
|
|
|
|
return a;
|
|
}, {})).map(n => WifiNetwork(n));
|
|
},
|
|
},
|
|
vertical: true,
|
|
className: 'spacing-v-5 sidebar-centermodules-scrollgradient-bottom-contentmargin',
|
|
setup: (self) => self.hook(Network, self.attribute.updateNetworks),
|
|
}),
|
|
}),
|
|
overlays: [Box({
|
|
className: 'sidebar-centermodules-scrollgradient-bottom'
|
|
})]
|
|
})]
|
|
});
|
|
const bottomBar = Box({
|
|
homogeneous: true,
|
|
children: [Button({
|
|
hpack: 'center',
|
|
className: 'txt-small txt sidebar-centermodules-bottombar-button',
|
|
onClicked: () => {
|
|
execAsync(['bash', '-c', userOptions.apps.network]).catch(print);
|
|
closeEverything();
|
|
},
|
|
label: getString('More'),
|
|
setup: setupCursorHover,
|
|
})],
|
|
})
|
|
return Box({
|
|
...props,
|
|
className: 'spacing-v-10',
|
|
vertical: true,
|
|
children: [
|
|
CurrentNetwork(),
|
|
networkList,
|
|
bottomBar,
|
|
]
|
|
});
|
|
} |