From 7eb985b616c12aed261fbef74a47c5a928c03e61 Mon Sep 17 00:00:00 2001 From: Ellie Huxtable Date: Mon, 15 Jul 2024 19:12:01 +0100 Subject: feat(gui): add runbook list, ability to create and delete, sql storage (#2282) * wip * saving works :)) * functioning delete button * persist selection properly --- ui/src/state/runbooks/runbook.ts | 124 +++++++++++++++++++++++++++++++++++++++ ui/src/state/store.ts | 17 ++++++ 2 files changed, 141 insertions(+) create mode 100644 ui/src/state/runbooks/runbook.ts (limited to 'ui/src/state') diff --git a/ui/src/state/runbooks/runbook.ts b/ui/src/state/runbooks/runbook.ts new file mode 100644 index 00000000..8555f4ea --- /dev/null +++ b/ui/src/state/runbooks/runbook.ts @@ -0,0 +1,124 @@ +import Database from "@tauri-apps/plugin-sql"; +import { uuidv7 } from "uuidv7"; + +export default class Runbook { + id: string; + created: Date; + updated: Date; + + private _name: string; + private _content: string; + + set name(value: string) { + this.updated = new Date(); + this._name = value; + } + + set content(value: string) { + this.updated = new Date(); + this._content = value; + } + + get content() { + return this._content; + } + + get name() { + return this._name; + } + + constructor( + id: string, + name: string, + content: string, + created: Date, + updated: Date, + ) { + this.id = id; + this._name = name; + this._content = content; + this.created = created; + this.updated = updated; + } + + /// Create a new Runbook, and automatically generate an ID. + public static async create(): Promise { + let now = new Date(); + + // Initialize with the same value for created/updated, to avoid needing null. + let runbook = new Runbook(uuidv7(), "", "", now, now); + await runbook.save(); + + return runbook; + } + + public static async load(id: String): Promise { + const db = await Database.load("sqlite:runbooks.db"); + + let res = await db.select("select * from runbooks where id = $1", [ + id, + ]); + + if (res.length == 0) return null; + + let rb = res[0]; + + return new Runbook( + rb.id, + rb.name, + rb.content, + new Date(rb.created / 1000000), + new Date(rb.updated / 1000000), + ); + } + + static async all(): Promise { + const db = await Database.load("sqlite:runbooks.db"); + + let res = await db.select( + "select * from runbooks order by updated desc", + ); + + return res.map((i) => { + return new Runbook( + i.id, + i.name, + i.content, + new Date(i.created / 1000000), + new Date(i.updated / 1000000), + ); + }); + } + + public async save() { + const db = await Database.load("sqlite:runbooks.db"); + + await db.execute( + `insert into runbooks(id, name, content, created, updated) + values ($1, $2, $3, $4, $5) + + on conflict(id) do update + set + name=$2, + content=$3, + updated=$5`, + + // getTime returns a timestamp as unix milliseconds + // we won't need or use the resolution here, but elsewhere Atuin stores timestamps in sqlite as nanoseconds since epoch + // let's do that across the board to avoid mistakes + [ + this.id, + this._name, + this._content, + this.created.getTime() * 1000000, + this.updated.getTime() * 1000000, + ], + ); + } + + public static async delete(id: string) { + const db = await Database.load("sqlite:runbooks.db"); + + await db.execute("delete from runbooks where id=$1", [id]); + } +} diff --git a/ui/src/state/store.ts b/ui/src/state/store.ts index 1e835cbd..cde2da17 100644 --- a/ui/src/state/store.ts +++ b/ui/src/state/store.ts @@ -18,6 +18,7 @@ import { import { invoke } from "@tauri-apps/api/core"; import { sessionToken, settings } from "./client"; import { getWeekInfo } from "@/lib/utils"; +import Runbook from "./runbooks/runbook"; // I'll probs want to slice this up at some point, but for now a // big blobby lump of state is fine. @@ -30,14 +31,19 @@ interface AtuinState { shellHistory: ShellHistory[]; calendar: any[]; weekStart: number; + runbooks: Runbook[]; + currentRunbook: String | null; refreshHomeInfo: () => void; refreshCalendar: () => void; refreshAliases: () => void; refreshVars: () => void; refreshUser: () => void; + refreshRunbooks: () => void; refreshShellHistory: (query?: string) => void; historyNextPage: (query?: string) => void; + + setCurrentRunbook: (id: String) => void; } let state = (set: any, get: any): AtuinState => ({ @@ -47,6 +53,8 @@ let state = (set: any, get: any): AtuinState => ({ vars: [], shellHistory: [], calendar: [], + runbooks: [], + currentRunbook: "", weekStart: getWeekInfo().firstDay, @@ -68,6 +76,11 @@ let state = (set: any, get: any): AtuinState => ({ }); }, + refreshRunbooks: async () => { + let runbooks = await Runbook.all(); + set({ runbooks }); + }, + refreshShellHistory: (query?: string) => { if (query) { invoke("search", { query: query }) @@ -141,6 +154,10 @@ let state = (set: any, get: any): AtuinState => ({ }); } }, + + setCurrentRunbook: (id: String) => { + set({ currentRunbook: id }); + }, }); export const useStore = create()( -- cgit v1.3.1