diff --git a/assets/style.css b/assets/style.css index 950a69e..218d15d 100644 --- a/assets/style.css +++ b/assets/style.css @@ -15,6 +15,19 @@ section { row-gap: 1em; } +.status-username { + font-weight: bold; + margin-bottom: .5em; +} + +.status-content { + margin: 0 1em; +} + +.status { + margin-bottom: 1em; +} + @media (min-width: 650px) { .cols { grid-template-columns: repeat(2, 1fr); diff --git a/go.mod b/go.mod index dd86997..d845af6 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,11 @@ module status go 1.16 require ( + github.com/fogleman/gg v1.3.0 // indirect + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/gorilla/mux v1.8.0 github.com/gorilla/sessions v1.2.1 github.com/lib/pq v1.10.4 golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 + golang.org/x/image v0.0.0-20211028202545-6944b10bf410 // indirect ) diff --git a/go.sum b/go.sum index 709d15c..9f70a0b 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,7 @@ +github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= @@ -8,6 +12,8 @@ github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 h1:/pEO3GD/ABYAjuakUS6xSEmmlyVS4kxBNkeA9tLJiTI= golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ= +golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/web/handler/common.go b/web/handler/common.go index 692b426..f2f8e55 100644 --- a/web/handler/common.go +++ b/web/handler/common.go @@ -9,14 +9,14 @@ var TplCommonMap = map[string]string{ - status cafe + status ☕ {{ template "head" . }}
-

status.cafe

+

status ☕

{{ template "content" . }} diff --git a/web/handler/handler.go b/web/handler/handler.go index 84871c1..7326314 100644 --- a/web/handler/handler.go +++ b/web/handler/handler.go @@ -77,6 +77,7 @@ func New(cfg *config.Config, sess *session.Session, data *storage.Storage) (http router.HandleFunc("/users/{user}", h.showUserView).Methods(http.MethodGet) router.HandleFunc("/users/{user}/status.json", h.showUserStatusView).Methods(http.MethodGet) + router.HandleFunc("/users/{user}/status.png", h.showUserStatusImageView).Methods(http.MethodGet) router.PathPrefix("/assets/").Handler( http.StripPrefix("/assets/", http.FileServer( diff --git a/web/handler/html.go b/web/handler/html.go index 4157482..3786650 100644 --- a/web/handler/html.go +++ b/web/handler/html.go @@ -88,15 +88,18 @@ Are you sure you you want to delete the following status?
+

Update status with bookmarklet

Drag to your bookmarks to update from anywhere: Set status

+

Display status on your homepage

+

todo

Status stream

{{ range .statuses }}
-
{{ .User }}, {{ .TimeAgo }}
-

{{ .Content }}

+
{{ .User }}'s status, updated {{ .TimeAgo }}
+

{{ .Content }}

{{ end }}
diff --git a/web/handler/html/common/layout.html b/web/handler/html/common/layout.html index 48a31ec..f783b06 100644 --- a/web/handler/html/common/layout.html +++ b/web/handler/html/common/layout.html @@ -4,14 +4,14 @@ - status cafe + status ☕ {{ template "head" . }}
-

status.cafe

+

status ☕

{{ template "content" . }} diff --git a/web/handler/html/index.html b/web/handler/html/index.html index 91fec5b..f8ec326 100644 --- a/web/handler/html/index.html +++ b/web/handler/html/index.html @@ -14,15 +14,18 @@
+

Update status with bookmarklet

Drag to your bookmarks to update from anywhere: Set status

+

Display status on your homepage

+

todo

Status stream

{{ range .statuses }}
-
{{ .User }}, {{ .TimeAgo }}
-

{{ .Content }}

+
{{ .User }}'s status, updated {{ .TimeAgo }}
+

{{ .Content }}

{{ end }}
diff --git a/web/handler/user_view.go b/web/handler/user_view.go index e915c72..75ebf15 100644 --- a/web/handler/user_view.go +++ b/web/handler/user_view.go @@ -2,7 +2,17 @@ package handler import ( "encoding/json" + "github.com/fogleman/gg" + "github.com/golang/freetype" + "github.com/golang/freetype/truetype" "github.com/gorilla/mux" + "golang.org/x/image/font" + "golang.org/x/image/font/gofont/goregular" + "golang.org/x/image/math/fixed" + "image" + "image/color" + "image/draw" + //"image/png" "net/http" ) @@ -26,7 +36,7 @@ func (h *Handler) showUserView(w http.ResponseWriter, r *http.Request) { type statusjson struct { Author string `json:"author"` Content string `json:"content"` - TimeAgo string `json:"time_ago"` + TimeAgo string `json:"timeAgo"` } func (h *Handler) showUserStatusView(w http.ResponseWriter, r *http.Request) { @@ -50,3 +60,88 @@ func (h *Handler) showUserStatusView(w http.ResponseWriter, r *http.Request) { } json.NewEncoder(w).Encode(res) } + +func drawText(canvas *image.RGBA, text string) error { + var ( + fgColor image.Image + fontFace *truetype.Font + err error + fontSize = 12.0 + ) + fgColor = image.White + fontFace, err = freetype.ParseFont(goregular.TTF) + fontDrawer := &font.Drawer{ + Dst: canvas, + Src: fgColor, + Face: truetype.NewFace(fontFace, &truetype.Options{ + Size: fontSize, + Hinting: font.HintingFull, + }), + } + //textBounds, _ := fontDrawer.BoundString(text) + xPosition := fixed.I(20) + //textHeight := textBounds.Max.Y - textBounds.Min.Y + yPosition := fixed.I(20) + fontDrawer.Dot = fixed.Point26_6{ + X: xPosition, + Y: yPosition, + } + fontDrawer.DrawString(text) + return err +} + +func createAvatar(text string) *image.RGBA { + bgColor := color.RGBA{ + R: 255, + G: 20, + B: 100, + A: 255, + } + background := image.NewRGBA(image.Rect(0, 0, 200, 100)) + draw.Draw(background, background.Bounds(), &image.Uniform{C: bgColor}, + image.Point{}, draw.Src) + drawText(background, text) + return background +} + +func (h *Handler) showUserStatusImageView(w http.ResponseWriter, r *http.Request) { + user := mux.Vars(r)["user"] + if !h.storage.UserExists(user) { + notFound(w) + return + } + statuses, err := h.storage.StatusByUsername(mux.Vars(r)["user"], 1, 0) + if err != nil { + serverError(w, err) + return + } + //w.Header().Set("Access-Control-Allow-Origin", "*") + //w.Header().Set("Content-Type", "application/json") + var text string + if len(statuses) > 0 { + text = statuses[0].Content + } + + background := "#ffffff" + foreground := "#000000" + + if c := r.URL.Query().Get("background"); c != "" { + background = "#" + c + } + + if c := r.URL.Query().Get("foreground"); c != "" { + foreground = "#" + c + } + + width := 350 + dc := gg.NewContext(width, 80) + dc.SetHexColor(background) + dc.Clear() + dc.SetHexColor(foreground) + dc.DrawStringWrapped(text, 19, 16, 0, 0, float64(width-(2*16)), 1.5, gg.AlignLeft) + dc.EncodePNG(w) + //dc.SavePNG("out.png") + + //avatar := createAvatar(text) + //png.Encode(w, avatar) +}