diff --git a/api.go b/api.go index 726577f..390a290 100644 --- a/api.go +++ b/api.go @@ -2,7 +2,6 @@ package main import ( "encoding/json" - "fmt" "net/http" "strconv" "strings" @@ -17,20 +16,15 @@ func registerAPI(mux *http.ServeMux, sqlite *sqliteStore, logger *zap.Logger) { return } - ids, err := sqlite.listAllPublicIDs(r.Context()) + summaries, err := sqlite.listAll(r.Context()) if err != nil { logger.Error("list flashcards", zap.Error(err)) http.Error(w, `{"error":"internal error"}`, http.StatusInternalServerError) return } - urls := make([]string, 0, len(ids)) - for _, id := range ids { - urls = append(urls, fmt.Sprintf("/api/v1/flashcards/%d", id)) - } - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(map[string][]string{"flashcards": urls}) + json.NewEncoder(w).Encode(map[string][]flashcardSummary{"flashcards": summaries}) }) mux.HandleFunc("/api/v1/flashcards/", func(w http.ResponseWriter, r *http.Request) { diff --git a/main.go b/main.go index 39d084b..0f8726d 100644 --- a/main.go +++ b/main.go @@ -2,10 +2,8 @@ package main import ( "context" - "embed" "encoding/json" "fmt" - "io/fs" "net/http" "os" "os/signal" @@ -21,9 +19,6 @@ import ( "go.uber.org/zap" ) -//go:embed public/* -var publicFiles embed.FS - var version = "0.1.0" func main() { @@ -77,8 +72,7 @@ func main() { }) mux.Handle("/mcp", authMiddleware(token, streamableServer)) registerAPI(mux, sqliteStore, logger) - publicFS, _ := fs.Sub(publicFiles, "public") - mux.Handle("/", http.FileServer(http.FS(publicFS))) + mux.Handle("/", publicHandler()) httpServer := &http.Server{ Addr: ":" + port, diff --git a/public/app.js b/public/app.js index ea78f3a..a6e747d 100644 --- a/public/app.js +++ b/public/app.js @@ -1,77 +1,111 @@ -import m from 'https://unpkg.com/mithril@2/mithril.esm.js'; +import m from "https://esm.sh/mithril@2.3.8"; -const API = '/api/v1/flashcards'; +const API = "/api/v1/flashcards"; + +const Card = { + view: function (v) { + return m("li", v.attrs.card.id); + }, +}; const List = { oninit: async function (v) { - v.state.ids = []; + v.state.cards = []; v.state.loading = true; - try { - const res = await fetch(API); - const data = await res.json(); - v.state.ids = (data.flashcards || []).map(u => u.split('/').pop()); - } catch { /* ignore */ } + const res = await fetch(API); + const data = await res.json(); + v.state.cards = data.flashcards || []; v.state.loading = false; + m.redraw(); }, view: function (v) { - if (v.state.loading) return m('.loading', m('i.nes-icon.nes-logo.is-small')); - return [ - m('.header', [ - m('h1', 'St\u2011Pierre Flashcards'), - m('span.nes-badge', m('span', String(v.state.ids.length))), - ]), - v.state.ids.length === 0 && m('p', 'No flashcards yet.'), - v.state.ids.map(id => m('.card-preview.nes-container.is-rounded.with-title', { - onclick: () => m.route.set('/flashcard/' + id), - }, [ - m('p.title', '#' + id), - m('p', 'Click to view details \u2192'), - ])), - ]; + if (v.state.loading) { + return m("h1", "loading"); + } + const cards = m( + ".cards-list", + v.state.cards.map((c) => m(Card, { card: c })), + ); + return m("ol", cards); }, }; const Detail = { oninit: async function (v) { - v.state.entry = null; + v.state.flashcard = null; v.state.loading = true; - try { - const res = await fetch(API + '/' + v.attrs.id); - if (res.ok) v.state.entry = await res.json(); - } catch { /* ignore */ } + v.state.flashcard = await findFlashCardByID(v.attrs.id); v.state.loading = false; + m.redraw(); }, view: function (v) { - if (v.state.loading) return m('.loading', m('i.nes-icon.nes-logo.is-small')); + if (v.state.loading) + return m(".loading", m("i.nes-icon.nes-logo.is-small")); const e = v.state.entry; - if (!e) return [ - m('.back', m('button.nes-btn', { onclick: () => m.route.set('/') }, '\u2B05 Back')), - m('p', 'Flashcard not found.'), - ]; + if (!e) + return [ + m( + ".back", + m( + "button.nes-btn", + { onclick: () => m.route.set("/") }, + "\u2B05 Back", + ), + ), + m("p", "Flashcard not found."), + ]; return [ - m('.back', m('button.nes-btn.is-warning', { onclick: () => m.route.set('/') }, '\u2B05 Back')), - m('.detail-card.nes-container.is-rounded.with-title', [ - m('p.title', '#' + e.public_id + ' v' + e.version), - m('.field', [ m('.field-label', 'Note'), m('p', e.note) ]), - e.host ? m('.field', [ m('.field-label', 'Host'), m('p', e.host) ]) : null, - e.user ? m('.field', [ m('.field-label', 'User'), m('p', e.user) ]) : null, - e.context ? m('.field', [ m('.field-label', 'Context'), m('p', e.context) ]) : null, - e.repos?.length ? m('.field', [ - m('.field-label', 'Repos'), - m('div', e.repos.map(r => m('span.nes-badge', m('span', r)))), - ]) : null, - e.tags?.length ? m('.field', [ - m('.field-label', 'Tags'), - m('div', e.tags.map(t => m('span.nes-badge', m('span', t)))), - ]) : null, - m('.field', [ m('.field-label', 'Created'), m('p', e.created) ]), - e.delete_timestamp ? m('.field', [ m('.field-label', 'Deleted'), m('p', e.delete_timestamp) ]) : null, + m( + ".back", + m( + "button.nes-btn.is-warning", + { onclick: () => m.route.set("/") }, + "\u2B05 Back", + ), + ), + m(".detail-card.nes-container.is-rounded.with-title", [ + m("p.title", "#" + e.public_id + " v" + e.version), + m(".field", [m(".field-label", "Note"), m("p", e.note)]), + e.host + ? m(".field", [m(".field-label", "Host"), m("p", e.host)]) + : null, + e.user + ? m(".field", [m(".field-label", "User"), m("p", e.user)]) + : null, + e.context + ? m(".field", [m(".field-label", "Context"), m("p", e.context)]) + : null, + e.repos?.length + ? m(".field", [ + m(".field-label", "Repos"), + m( + "div", + e.repos.map((r) => m("span.nes-badge", m("span", r))), + ), + ]) + : null, + e.tags?.length + ? m(".field", [ + m(".field-label", "Tags"), + m( + "div", + e.tags.map((t) => m("span.nes-badge", m("span", t))), + ), + ]) + : null, + m(".field", [m(".field-label", "Created"), m("p", e.created)]), + e.delete_timestamp + ? m(".field", [ + m(".field-label", "Deleted"), + m("p", e.delete_timestamp), + ]) + : null, ]), ]; }, }; -m.route(document.getElementById('app'), '/', { - '/': List, - '/flashcard/:id': Detail, +m.route(document.getElementById("app"), "/", { + "/": List, + "/flashcard/:id": Detail, }); diff --git a/public/index.html b/public/index.html index 6fe7d9f..5e6ac2d 100644 --- a/public/index.html +++ b/public/index.html @@ -1,31 +1,13 @@ - + -
- - -