diff options
| -rw-r--r-- | app.tsx | 21 | ||||
| -rw-r--r-- | scss/popdowns/updates.scss | 9 | ||||
| -rw-r--r-- | src/modules/popdowns/updates.tsx | 48 | ||||
| -rw-r--r-- | src/services/palette.ts | 240 | ||||
| -rw-r--r-- | src/services/updates.ts | 16 |
5 files changed, 312 insertions, 22 deletions
@@ -5,24 +5,17 @@ import Osds from "@/modules/osds"; import Popdowns from "@/modules/popdowns"; import Session from "@/modules/session"; import Monitors from "@/services/monitors"; +import Palette from "@/services/palette"; import Players from "@/services/players"; 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 { initConfig, updateConfig } from "config"; export const loadStyleAsync = async () => { - let schemeColours; - if (GLib.file_test(`${STATE}/scheme/current.txt`, GLib.FileTest.EXISTS)) { - const currentScheme = await readFileAsync(`${STATE}/scheme/current.txt`); - 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`); + const schemeColours = Object.entries(Palette.get_default().colours) + .map(([name, hex]) => `$${name}: ${hex};`) + .join("\n"); await writeFileAsync(`${SRC}/scss/scheme/_index.scss`, schemeColours); App.apply_css(await execAsync(`sass ${SRC}/style.scss`), true); }; @@ -32,8 +25,10 @@ App.start({ icons: "assets/icons", main() { const now = Date.now(); + loadStyleAsync().catch(console.error); - monitorFile(`${STATE}/scheme/current.txt`, () => loadStyleAsync().catch(console.error)); + Palette.get_default().connect("notify::colours", () => loadStyleAsync().catch(console.error)); + initConfig(); <Launcher />; diff --git a/scss/popdowns/updates.scss b/scss/popdowns/updates.scss index f63b50e..7ab661f 100644 --- a/scss/popdowns/updates.scss +++ b/scss/popdowns/updates.scss @@ -9,7 +9,7 @@ $-accent: scheme.$blue; @include lib.popdown-window($-accent); min-width: lib.s(550); - min-height: lib.s(450); + min-height: lib.s(800); .wrapper { @include lib.element-decel; @@ -32,10 +32,15 @@ $-accent: scheme.$blue; font-size: lib.s(16); } - .list { + .list, + .news { color: scheme.$text; margin-left: lib.s(12); } + + .news { + margin-bottom: lib.s(5); + } } } } diff --git a/src/modules/popdowns/updates.tsx b/src/modules/popdowns/updates.tsx index 9e98f66..fd98671 100644 --- a/src/modules/popdowns/updates.tsx +++ b/src/modules/popdowns/updates.tsx @@ -1,3 +1,4 @@ +import Palette from "@/services/palette"; import Updates, { Repo as IRepo, Update as IUpdate } from "@/services/updates"; import { MenuItem } from "@/utils/widgets"; import PopdownWindow from "@/widgets/popdownwindow"; @@ -35,8 +36,8 @@ const Update = (update: IUpdate) => { ); }; -const Repo = (repo: IRepo) => { - const expanded = Variable(false); +const Repo = ({ repo, first }: { repo: IRepo; first?: boolean }) => { + const expanded = Variable(first); return ( <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 = () => ( <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> ); diff --git a/src/services/palette.ts b/src/services/palette.ts new file mode 100644 index 0000000..b925d50 --- /dev/null +++ b/src/services/palette.ts @@ -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()); + } +} diff --git a/src/services/updates.ts b/src/services/updates.ts index 06e86b1..dc82080 100644 --- a/src/services/updates.ts +++ b/src/services/updates.ts @@ -23,6 +23,7 @@ export interface Data { cached?: boolean; repos: Repo[]; errors: string[]; + news: string; } @register({ GTypeName: "Updates" }) @@ -38,7 +39,7 @@ export default class Updates extends GObject.Object { #timeout?: GLib.Source; #loading = false; - #data: Data = { cached: true, repos: [], errors: [] }; + #data: Data = { cached: true, repos: [], errors: [], news: "" }; @property(Boolean) 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); } + @property(String) + get news() { + return this.#data.news; + } + async #updateFromCache() { this.#data = JSON.parse(await readFileAsync(this.#cachePath)); this.notify("update-data"); this.notify("list"); this.notify("num-updates"); + this.notify("news"); } async getRepo(repo: string) { @@ -93,9 +100,9 @@ export default class Updates extends GObject.Object { this.notify("loading"); // Get new updates - Promise.allSettled([execAsync("checkupdates"), execAsync("yay -Qua")]) - .then(async ([pacman, yay]) => { - const data: Data = { repos: [], errors: [] }; + Promise.allSettled([execAsync("checkupdates"), execAsync("yay -Qua"), execAsync("yay -Pw")]) + .then(async ([pacman, yay, news]) => { + const data: Data = { repos: [], errors: [], news: news.status === "fulfilled" ? news.value : "" }; // Pacman updates (checkupdates) if (pacman.status === "fulfilled") { @@ -145,6 +152,7 @@ export default class Updates extends GObject.Object { this.notify("update-data"); this.notify("list"); this.notify("num-updates"); + this.notify("news"); } this.#loading = false; |