updates: show arch news
This commit is contained in:
parent
15110e5f7c
commit
01626b70f1
5 changed files with 312 additions and 22 deletions
21
app.tsx
21
app.tsx
|
|
@ -5,24 +5,17 @@ import Osds from "@/modules/osds";
|
||||||
import Popdowns from "@/modules/popdowns";
|
import Popdowns from "@/modules/popdowns";
|
||||||
import Session from "@/modules/session";
|
import Session from "@/modules/session";
|
||||||
import Monitors from "@/services/monitors";
|
import Monitors from "@/services/monitors";
|
||||||
|
import Palette from "@/services/palette";
|
||||||
import Players from "@/services/players";
|
import Players from "@/services/players";
|
||||||
import type PopupWindow from "@/widgets/popupwindow";
|
import type PopupWindow from "@/widgets/popupwindow";
|
||||||
import { execAsync, GLib, monitorFile, readFileAsync, writeFileAsync } from "astal";
|
import { execAsync, writeFileAsync } from "astal";
|
||||||
import { App } from "astal/gtk3";
|
import { App } from "astal/gtk3";
|
||||||
import { initConfig, updateConfig } from "config";
|
import { initConfig, updateConfig } from "config";
|
||||||
|
|
||||||
export const loadStyleAsync = async () => {
|
export const loadStyleAsync = async () => {
|
||||||
let schemeColours;
|
const schemeColours = Object.entries(Palette.get_default().colours)
|
||||||
if (GLib.file_test(`${STATE}/scheme/current.txt`, GLib.FileTest.EXISTS)) {
|
.map(([name, hex]) => `$${name}: ${hex};`)
|
||||||
const currentScheme = await readFileAsync(`${STATE}/scheme/current.txt`);
|
.join("\n");
|
||||||
schemeColours = currentScheme
|
|
||||||
.split("\n")
|
|
||||||
.map(l => {
|
|
||||||
const [name, hex] = l.split(" ");
|
|
||||||
return `$${name}: #${hex};`;
|
|
||||||
})
|
|
||||||
.join("\n");
|
|
||||||
} else schemeColours = await readFileAsync(`${SRC}/scss/scheme/_default.scss`);
|
|
||||||
await writeFileAsync(`${SRC}/scss/scheme/_index.scss`, schemeColours);
|
await writeFileAsync(`${SRC}/scss/scheme/_index.scss`, schemeColours);
|
||||||
App.apply_css(await execAsync(`sass ${SRC}/style.scss`), true);
|
App.apply_css(await execAsync(`sass ${SRC}/style.scss`), true);
|
||||||
};
|
};
|
||||||
|
|
@ -32,8 +25,10 @@ App.start({
|
||||||
icons: "assets/icons",
|
icons: "assets/icons",
|
||||||
main() {
|
main() {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
loadStyleAsync().catch(console.error);
|
loadStyleAsync().catch(console.error);
|
||||||
monitorFile(`${STATE}/scheme/current.txt`, () => loadStyleAsync().catch(console.error));
|
Palette.get_default().connect("notify::colours", () => loadStyleAsync().catch(console.error));
|
||||||
|
|
||||||
initConfig();
|
initConfig();
|
||||||
|
|
||||||
<Launcher />;
|
<Launcher />;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ $-accent: scheme.$blue;
|
||||||
@include lib.popdown-window($-accent);
|
@include lib.popdown-window($-accent);
|
||||||
|
|
||||||
min-width: lib.s(550);
|
min-width: lib.s(550);
|
||||||
min-height: lib.s(450);
|
min-height: lib.s(800);
|
||||||
|
|
||||||
.wrapper {
|
.wrapper {
|
||||||
@include lib.element-decel;
|
@include lib.element-decel;
|
||||||
|
|
@ -32,10 +32,15 @@ $-accent: scheme.$blue;
|
||||||
font-size: lib.s(16);
|
font-size: lib.s(16);
|
||||||
}
|
}
|
||||||
|
|
||||||
.list {
|
.list,
|
||||||
|
.news {
|
||||||
color: scheme.$text;
|
color: scheme.$text;
|
||||||
margin-left: lib.s(12);
|
margin-left: lib.s(12);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.news {
|
||||||
|
margin-bottom: lib.s(5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import Palette from "@/services/palette";
|
||||||
import Updates, { Repo as IRepo, Update as IUpdate } from "@/services/updates";
|
import Updates, { Repo as IRepo, Update as IUpdate } from "@/services/updates";
|
||||||
import { MenuItem } from "@/utils/widgets";
|
import { MenuItem } from "@/utils/widgets";
|
||||||
import PopdownWindow from "@/widgets/popdownwindow";
|
import PopdownWindow from "@/widgets/popdownwindow";
|
||||||
|
|
@ -35,8 +36,8 @@ const Update = (update: IUpdate) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Repo = (repo: IRepo) => {
|
const Repo = ({ repo, first }: { repo: IRepo; first?: boolean }) => {
|
||||||
const expanded = Variable(false);
|
const expanded = Variable(first);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<box vertical className="repo">
|
<box vertical className="repo">
|
||||||
|
|
@ -61,9 +62,50 @@ const Repo = (repo: IRepo) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const News = ({ news }: { news: string }) => {
|
||||||
|
const expanded = Variable(true);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<box vertical className="repo">
|
||||||
|
<button className="wrapper" cursor="pointer" onClicked={() => expanded.set(!expanded.get())}>
|
||||||
|
<box className="header">
|
||||||
|
<label className="icon" label="newspaper" />
|
||||||
|
<label label="News" />
|
||||||
|
<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}
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
wrap
|
||||||
|
useMarkup
|
||||||
|
xalign={0}
|
||||||
|
className="news"
|
||||||
|
label={bind(Palette.get_default(), "teal").as(c =>
|
||||||
|
news
|
||||||
|
.slice(0, news.lastIndexOf("\n")) // Remove last line cause it contains an unopened \x1b[0m
|
||||||
|
.replace(/^([0-9]{4}-[0-9]{2}-[0-9]{2} .+)$/gm, "<b>$1</b>") // Make titles bold
|
||||||
|
// Replace color codes with html spans
|
||||||
|
.replaceAll("\x1b[36m", `<span foreground="${c}">`)
|
||||||
|
.replaceAll("\x1b[0m", "</span>")
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</revealer>
|
||||||
|
</box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const List = () => (
|
const List = () => (
|
||||||
<box vertical valign={Gtk.Align.START} className="repos">
|
<box vertical valign={Gtk.Align.START} className="repos">
|
||||||
{bind(Updates.get_default(), "updateData").as(d => d.repos.map(Repo))}
|
{bind(Updates.get_default(), "updateData").as(d =>
|
||||||
|
d.news
|
||||||
|
? [<News news={d.news} />, ...d.repos.map(r => <Repo repo={r} />)]
|
||||||
|
: d.repos.map((r, i) => <Repo repo={r} first={i === 0} />)
|
||||||
|
)}
|
||||||
</box>
|
</box>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
240
src/services/palette.ts
Normal file
240
src/services/palette.ts
Normal file
|
|
@ -0,0 +1,240 @@
|
||||||
|
import { GLib, GObject, monitorFile, property, readFile, register } from "astal";
|
||||||
|
|
||||||
|
export type Hex = `#${string}`;
|
||||||
|
|
||||||
|
export interface IPalette {
|
||||||
|
rosewater: Hex;
|
||||||
|
flamingo: Hex;
|
||||||
|
pink: Hex;
|
||||||
|
mauve: Hex;
|
||||||
|
red: Hex;
|
||||||
|
maroon: Hex;
|
||||||
|
peach: Hex;
|
||||||
|
yellow: Hex;
|
||||||
|
green: Hex;
|
||||||
|
teal: Hex;
|
||||||
|
sky: Hex;
|
||||||
|
sapphire: Hex;
|
||||||
|
blue: Hex;
|
||||||
|
lavender: Hex;
|
||||||
|
text: Hex;
|
||||||
|
subtext1: Hex;
|
||||||
|
subtext0: Hex;
|
||||||
|
overlay2: Hex;
|
||||||
|
overlay1: Hex;
|
||||||
|
overlay0: Hex;
|
||||||
|
surface2: Hex;
|
||||||
|
surface1: Hex;
|
||||||
|
surface0: Hex;
|
||||||
|
base: Hex;
|
||||||
|
mantle: Hex;
|
||||||
|
crust: Hex;
|
||||||
|
accent: Hex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@register({ GTypeName: "Palette" })
|
||||||
|
export default class Palette extends GObject.Object {
|
||||||
|
static instance: Palette;
|
||||||
|
static get_default() {
|
||||||
|
if (!this.instance) this.instance = new Palette();
|
||||||
|
|
||||||
|
return this.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
#colours!: IPalette;
|
||||||
|
|
||||||
|
@property(Object)
|
||||||
|
get colours() {
|
||||||
|
return this.#colours;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get rosewater() {
|
||||||
|
return this.#colours.rosewater;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get flamingo() {
|
||||||
|
return this.#colours.flamingo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get pink() {
|
||||||
|
return this.#colours.pink;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get mauve() {
|
||||||
|
return this.#colours.mauve;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get red() {
|
||||||
|
return this.#colours.red;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get maroon() {
|
||||||
|
return this.#colours.maroon;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get peach() {
|
||||||
|
return this.#colours.peach;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get yellow() {
|
||||||
|
return this.#colours.yellow;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get green() {
|
||||||
|
return this.#colours.green;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get teal() {
|
||||||
|
return this.#colours.teal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get sky() {
|
||||||
|
return this.#colours.sky;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get sapphire() {
|
||||||
|
return this.#colours.sapphire;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get blue() {
|
||||||
|
return this.#colours.blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get lavender() {
|
||||||
|
return this.#colours.lavender;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get text() {
|
||||||
|
return this.#colours.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get subtext1() {
|
||||||
|
return this.#colours.subtext1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get subtext0() {
|
||||||
|
return this.#colours.subtext0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get overlay2() {
|
||||||
|
return this.#colours.overlay2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get overlay1() {
|
||||||
|
return this.#colours.overlay1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get overlay0() {
|
||||||
|
return this.#colours.overlay0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get surface2() {
|
||||||
|
return this.#colours.surface2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get surface1() {
|
||||||
|
return this.#colours.surface1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get surface0() {
|
||||||
|
return this.#colours.surface0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get base() {
|
||||||
|
return this.#colours.base;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get mantle() {
|
||||||
|
return this.#colours.mantle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get crust() {
|
||||||
|
return this.#colours.crust;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get accent() {
|
||||||
|
return this.#colours.accent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notify() {
|
||||||
|
this.notify("colours");
|
||||||
|
this.notify("rosewater");
|
||||||
|
this.notify("flamingo");
|
||||||
|
this.notify("pink");
|
||||||
|
this.notify("mauve");
|
||||||
|
this.notify("red");
|
||||||
|
this.notify("maroon");
|
||||||
|
this.notify("peach");
|
||||||
|
this.notify("yellow");
|
||||||
|
this.notify("green");
|
||||||
|
this.notify("teal");
|
||||||
|
this.notify("sky");
|
||||||
|
this.notify("sapphire");
|
||||||
|
this.notify("blue");
|
||||||
|
this.notify("lavender");
|
||||||
|
this.notify("text");
|
||||||
|
this.notify("subtext1");
|
||||||
|
this.notify("subtext0");
|
||||||
|
this.notify("overlay2");
|
||||||
|
this.notify("overlay1");
|
||||||
|
this.notify("overlay0");
|
||||||
|
this.notify("surface2");
|
||||||
|
this.notify("surface1");
|
||||||
|
this.notify("surface0");
|
||||||
|
this.notify("base");
|
||||||
|
this.notify("mantle");
|
||||||
|
this.notify("crust");
|
||||||
|
this.notify("accent");
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
let schemeColours;
|
||||||
|
if (GLib.file_test(`${STATE}/scheme/current.txt`, GLib.FileTest.EXISTS)) {
|
||||||
|
const currentScheme = readFile(`${STATE}/scheme/current.txt`);
|
||||||
|
schemeColours = currentScheme.split("\n").map(l => l.split(" "));
|
||||||
|
} else
|
||||||
|
schemeColours = readFile(`${SRC}/scss/scheme/_default.scss`)
|
||||||
|
.split("\n")
|
||||||
|
.map(l => {
|
||||||
|
const [name, hex] = l.split(":");
|
||||||
|
return [name.slice(1), hex.trim().slice(1, -1)];
|
||||||
|
});
|
||||||
|
|
||||||
|
this.#colours = schemeColours.reduce((acc, [name, hex]) => ({ ...acc, [name]: `#${hex}` }), {} as IPalette);
|
||||||
|
this.#notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.update();
|
||||||
|
monitorFile(`${STATE}/scheme/current.txt`, () => this.update());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -23,6 +23,7 @@ export interface Data {
|
||||||
cached?: boolean;
|
cached?: boolean;
|
||||||
repos: Repo[];
|
repos: Repo[];
|
||||||
errors: string[];
|
errors: string[];
|
||||||
|
news: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@register({ GTypeName: "Updates" })
|
@register({ GTypeName: "Updates" })
|
||||||
|
|
@ -38,7 +39,7 @@ export default class Updates extends GObject.Object {
|
||||||
|
|
||||||
#timeout?: GLib.Source;
|
#timeout?: GLib.Source;
|
||||||
#loading = false;
|
#loading = false;
|
||||||
#data: Data = { cached: true, repos: [], errors: [] };
|
#data: Data = { cached: true, repos: [], errors: [], news: "" };
|
||||||
|
|
||||||
@property(Boolean)
|
@property(Boolean)
|
||||||
get loading() {
|
get loading() {
|
||||||
|
|
@ -60,11 +61,17 @@ export default class Updates extends GObject.Object {
|
||||||
return this.#data.repos.reduce((acc, repo) => acc + repo.updates.length, 0);
|
return this.#data.repos.reduce((acc, repo) => acc + repo.updates.length, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
get news() {
|
||||||
|
return this.#data.news;
|
||||||
|
}
|
||||||
|
|
||||||
async #updateFromCache() {
|
async #updateFromCache() {
|
||||||
this.#data = JSON.parse(await readFileAsync(this.#cachePath));
|
this.#data = JSON.parse(await readFileAsync(this.#cachePath));
|
||||||
this.notify("update-data");
|
this.notify("update-data");
|
||||||
this.notify("list");
|
this.notify("list");
|
||||||
this.notify("num-updates");
|
this.notify("num-updates");
|
||||||
|
this.notify("news");
|
||||||
}
|
}
|
||||||
|
|
||||||
async getRepo(repo: string) {
|
async getRepo(repo: string) {
|
||||||
|
|
@ -93,9 +100,9 @@ export default class Updates extends GObject.Object {
|
||||||
this.notify("loading");
|
this.notify("loading");
|
||||||
|
|
||||||
// Get new updates
|
// Get new updates
|
||||||
Promise.allSettled([execAsync("checkupdates"), execAsync("yay -Qua")])
|
Promise.allSettled([execAsync("checkupdates"), execAsync("yay -Qua"), execAsync("yay -Pw")])
|
||||||
.then(async ([pacman, yay]) => {
|
.then(async ([pacman, yay, news]) => {
|
||||||
const data: Data = { repos: [], errors: [] };
|
const data: Data = { repos: [], errors: [], news: news.status === "fulfilled" ? news.value : "" };
|
||||||
|
|
||||||
// Pacman updates (checkupdates)
|
// Pacman updates (checkupdates)
|
||||||
if (pacman.status === "fulfilled") {
|
if (pacman.status === "fulfilled") {
|
||||||
|
|
@ -145,6 +152,7 @@ export default class Updates extends GObject.Object {
|
||||||
this.notify("update-data");
|
this.notify("update-data");
|
||||||
this.notify("list");
|
this.notify("list");
|
||||||
this.notify("num-updates");
|
this.notify("num-updates");
|
||||||
|
this.notify("news");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.#loading = false;
|
this.#loading = false;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue