diff options
Diffstat (limited to 'src/modules')
| -rw-r--r-- | src/modules/bar.tsx | 29 | ||||
| -rw-r--r-- | src/modules/updates.tsx | 102 |
2 files changed, 118 insertions, 13 deletions
diff --git a/src/modules/bar.tsx b/src/modules/bar.tsx index aeb6e42..cf08634 100644 --- a/src/modules/bar.tsx +++ b/src/modules/bar.tsx @@ -221,14 +221,7 @@ const Network = () => ( network.wifi.scan(); execAsync( "uwsm app -- foot -T nmtui fish -c 'sleep .1; set -e COLORTERM; TERM=xterm-old nmtui connect'" - ).catch(err => { - // Idk why but foot always throws this error when it opens - if ( - err.message !== - "warn: wayland.c:1619: compositor does not implement the XDG toplevel icon protocol\nwarn: terminal.c:1973: slave exited with signal 1 (Hangup)" - ) - console.error(err); - }); + ).catch(() => {}); // Ignore errors }); }} setup={self => { @@ -372,8 +365,16 @@ const StatusIcons = () => ( ); const PkgUpdates = () => ( - <box - className="module updates" + <button + onClick={(self, event) => { + if (event.button === Astal.MouseButton.PRIMARY) { + const popup = App.get_window("updates") as PopupWindow | null; + if (popup) { + if (popup.visible) popup.hide(); + else popup.popup_at_widget(self, event); + } + } + }} setup={self => setupCustomTooltip( self, @@ -381,9 +382,11 @@ const PkgUpdates = () => ( ) } > - <label className="icon" label="download" /> - <label label={bind(Updates.get_default(), "numUpdates").as(String)} /> - </box> + <box className="module pkg-updates"> + <label className="icon" label="download" /> + <label label={bind(Updates.get_default(), "numUpdates").as(String)} /> + </box> + </button> ); const Unread = () => { diff --git a/src/modules/updates.tsx b/src/modules/updates.tsx new file mode 100644 index 0000000..0a8cbea --- /dev/null +++ b/src/modules/updates.tsx @@ -0,0 +1,102 @@ +import { bind, execAsync, Variable } from "astal"; +import { App, Astal, Gtk } from "astal/gtk3"; +import Updates, { Repo as IRepo, Update as IUpdate } from "../services/updates"; +import { MenuItem } from "../utils/widgets"; +import PopupWindow from "../widgets/popupwindow"; + +const constructItem = (label: string, exec: string, quiet = true) => + new MenuItem({ + label, + onActivate() { + App.get_window("updates")?.hide(); + execAsync(exec).catch(e => !quiet && console.error(e)); + }, + }); + +const Update = (update: IUpdate) => { + const menu = new Gtk.Menu(); + menu.append(constructItem("Open info in browser", `xdg-open '${update.url}'`, false)); + menu.append(constructItem("Open info in terminal", `uwsm app -- foot -H pacman -Qi ${update.name}`)); + menu.append(constructItem("Reinstall", `uwsm app -T -- yay -S ${update.name}`)); + menu.append(constructItem("Remove with dependencies", `uwsm app -T -- yay -Rns ${update.name}`)); + + return ( + <button + onClick={(_, event) => event.button === Astal.MouseButton.SECONDARY && menu.popup_at_pointer(null)} + onDestroy={() => menu.destroy()} + > + <label + truncate + xalign={0} + label={`${update.name} (${update.version.old} -> ${update.version.new})\n ${update.description}`} + /> + </button> + ); +}; + +const Repo = (repo: IRepo) => { + const expanded = Variable(false); + + return ( + <box vertical className="repo"> + <button className="wrapper" cursor="pointer" onClicked={() => expanded.set(!expanded.get())}> + <box className="header"> + <label className="icon" label={repo.icon} /> + <label label={`${repo.name} (${repo.updates.length})`} /> + <box hexpand /> + <label className="icon" label={bind(expanded).as(e => (e ? "expand_less" : "expand_more"))} /> + </box> + </button> + <revealer + revealChild={bind(expanded)} + transitionType={Gtk.RevealerTransitionType.SLIDE_DOWN} + transitionDuration={200} + > + <box vertical className="list"> + {repo.updates.map(Update)} + </box> + </revealer> + </box> + ); +}; + +const List = () => ( + <box vertical valign={Gtk.Align.START} className="repos"> + {bind(Updates.get_default(), "updateData").as(d => d.repos.map(Repo))} + </box> +); + +export default () => ( + <PopupWindow name="updates"> + <box vertical className="updates"> + <box className="header"> + <label label={bind(Updates.get_default(), "numUpdates").as(n => `${n} update${n === 1 ? "" : "s"}`)} /> + <box hexpand /> + <button + cursor="pointer" + onClicked={() => + execAsync("uwsm app -T -- yay") + .then(() => Updates.get_default().getUpdates()) + // Ignore errors + .catch(() => {}) + } + label="Update all" + /> + <button cursor="pointer" onClicked={() => Updates.get_default().getUpdates()} label="Reload" /> + </box> + <stack + transitionType={Gtk.StackTransitionType.CROSSFADE} + transitionDuration={150} + shown={bind(Updates.get_default(), "numUpdates").as(n => (n > 0 ? "list" : "empty"))} + > + <box vertical valign={Gtk.Align.CENTER} name="empty"> + <label className="icon" label="deployed_code_history" /> + <label label="All packages up to date!" /> + </box> + <scrollable expand hscroll={Gtk.PolicyType.NEVER} name="list"> + <List /> + </scrollable> + </stack> + </box> + </PopupWindow> +); |