package calls import ( "amadan.net/oficina/home/connect" "bytes" "embed" "io/fs" "testing/fstest" "amadan.net/oficina/estilo.git/base" "amadan.net/oficina/estilo.git/callui" "amadan.net/oficina/estilo.git/chrome" "amadan.net/oficina/estilo.git/credits" estilomenu "amadan.net/oficina/estilo.git/menu" "amadan.net/oficina/estilo.git/sky" "html/template" "net/http" "amadan.net/rastrillo/rastrillo" "amadan.net/rastrillo/rastrillo/ui" "amadan.net/oficina/calls/internal/calls/icons" ) //go:embed templates static var appFS embed.FS var assets = rastrillo.NewAssets(callAssets()) func callAssets() fs.FS { // estilo's stylesheets and chrome.js come straight from the estilo // this module pins, so they cannot drift from it: base.css (links), // menu.css (every floating panel), chrome.css (the bar, the account // menu and the sign-in door), sky.css (the sky behind the door), // callui.css (the call's own pieces). files := fstest.MapFS{ "static/estilo/base.css": &fstest.MapFile{Data: base.CSS()}, "static/estilo/menu.css": &fstest.MapFile{Data: estilomenu.CSS()}, "static/estilo/chrome.css": &fstest.MapFile{Data: chrome.CSS()}, "static/estilo/chrome.js": &fstest.MapFile{Data: chrome.JS()}, "static/estilo/sky.css": &fstest.MapFile{Data: sky.CSS()}, "static/callui.css": &fstest.MapFile{Data: callui.CSS()}, } if err := fs.WalkDir(appFS, "static", func(path string, entry fs.DirEntry, err error) error { if err != nil { return err } if entry.IsDir() { return nil } data, err := appFS.ReadFile(path) if err != nil { return err } files[path] = &fstest.MapFile{Data: data} return nil }); err != nil { panic(err) } return files } var pageNames = []string{"index", "signin", "signedout", "errors", "welcome", "link_once", "confirm", "join", "room", "settings", "agent_once", "home", "native", "share", "credits", "guest", "meeting_settings"} var pages = map[string]*template.Template{} func init() { for _, name := range pageNames { pages[name] = template.Must(buildPage(name).ParseFS(credits.Templates(), "templates/credits.html")) if name == "signin" { // The Keymail continuation's meta refresh (home-oauth-head). pages[name] = template.Must(pages[name].ParseFS(connect.Templates(), "templates/oauth.html")) } if name == "home" { pages[name] = template.Must(pages[name].ParseFS(connect.Templates(), "templates/home_link.html")) } } } // buildPage parses rastrillo's ui partials first, then estilo's chrome // and menu templates (the bar, the account menu, the error page body and // the sign-in door, which call rastrillo's field and callout partials), // then this app's own partials, then the layout and the page, so a page // can call confirm-form, field, empty-state and the rest by name with a // dict — and chat-panel, the one partial Calls owns, the same way. // // templates/partials/ is parsed for every page rather than per page // that uses it: a partial is defined-but-unused for free, and the // alternative is a second list beside pageNames that has to be kept in // step with the first. func buildPage(name string) *template.Template { t := template.New("layout"). Funcs(ui.Funcs(ui.WithIcons(icons.Icon, icons.Assets))). Funcs(template.FuncMap{"asset": assets.Path}) t = template.Must(t.ParseFS(ui.Templates(), "*.html")) t = template.Must(t.ParseFS(chrome.Templates(), "templates/*.html")) t = template.Must(t.ParseFS(estilomenu.Templates(), "templates/*.html")) t = template.Must(t.ParseFS(appFS, "templates/partials/*.html")) return template.Must(t.ParseFS(appFS, "templates/layout.html", "templates/"+name+".html")) } // render writes a page. A template failure is logged by nobody and // answered in the plainest way left: nothing has been written yet, so // the status can still say so. func render(w http.ResponseWriter, name string, data any) { var buf bytes.Buffer if err := pages[name].ExecuteTemplate(&buf, "layout", data); err != nil { http.Error(w, "something went wrong", http.StatusInternalServerError) return } if w.Header().Get("Content-Type") == "" { w.Header().Set("Content-Type", "text/html; charset=utf-8") } buf.WriteTo(w) }