Adding faces

This commit is contained in:
m15o 2021-11-30 21:50:10 +01:00
parent 95dacca678
commit fa5e12c0f7
16 changed files with 311 additions and 67 deletions

View file

@ -3,12 +3,14 @@ body {
margin: 0 auto; margin: 0 auto;
padding: 1em; padding: 1em;
font-family: Verdana; font-family: Verdana;
background-color: azure;
color: midnightblue;
} }
section { /*section {*/
padding: 1em; /* padding: 1em;*/
background-color: lightblue; /* background-color: lightblue;*/
} /*}*/
.flash { .flash {
background-color: lightgreen; background-color: lightgreen;
@ -50,13 +52,13 @@ dd {
margin-bottom: 1em; margin-bottom: 1em;
} }
nav { /*nav {*/
margin-bottom: 1em; /* margin-bottom: 1em;*/
} /*}*/
h1, h2 { /*h1, h2 {*/
margin-top: 0; /* !*margin-top: 0;*!*/
} /*}*/
.tools { .tools {
list-style-position: inside; list-style-position: inside;
@ -65,11 +67,12 @@ h1, h2 {
@media (min-width: 650px) { @media (min-width: 650px) {
.cols { .cols {
grid-template-columns: repeat(2, 1fr); grid-template-columns: 1fr 2fr;
grid-gap: 1em; grid-gap: 2em;
} }
} }
.radio { display: inline-block }
.field { margin-bottom: 1rem; max-width: 500px; } .field { margin-bottom: 1rem; max-width: 500px; }
.field > label { margin-bottom: .25rem; } .field > label { margin-bottom: .25rem; }
.field > * { display: block; width: 100%; box-sizing: border-box; } .field > * { display: block; width: 100%; box-sizing: border-box; }

View file

@ -10,6 +10,7 @@ type Status struct {
Id int64 Id int64
User string User string
Content string Content string
Face string
CreatedAt time.Time CreatedAt time.Time
} }

View file

@ -7,7 +7,7 @@ import (
"strconv" "strconv"
) )
const schemaVersion = 7 const schemaVersion = 8
func Migrate(db *sql.DB) { func Migrate(db *sql.DB) {
var currentVersion int var currentVersion int

View file

@ -40,4 +40,6 @@ alter column about TYPE TEXT;`,
add column picture varchar(500) not null DEFAULT '';`, add column picture varchar(500) not null DEFAULT '';`,
"schema_version_7": `alter table users "schema_version_7": `alter table users
add column email varchar(500) not null DEFAULT '';`, add column email varchar(500) not null DEFAULT '';`,
"schema_version_8": `alter table statuses
add column face varchar(1) not null DEFAULT '🙂';`,
} }

View file

@ -0,0 +1,2 @@
alter table statuses
add column face varchar(1) not null DEFAULT '🙂';

View file

@ -15,7 +15,7 @@ type statusQueryBuilder struct {
} }
func (p statusQueryBuilder) build() string { func (p statusQueryBuilder) build() string {
query := []string{`SELECT id, author, content, created_at from statuses`} query := []string{`SELECT id, author, content, created_at, face from statuses`}
if p.where != "" { if p.where != "" {
query = append(query, `WHERE`, p.where) query = append(query, `WHERE`, p.where)
} }
@ -31,7 +31,7 @@ func (p statusQueryBuilder) build() string {
func (s *Storage) populateStatus(rows *sql.Rows) (model.Status, error) { func (s *Storage) populateStatus(rows *sql.Rows) (model.Status, error) {
var status model.Status var status model.Status
err := rows.Scan(&status.Id, &status.User, &status.Content, &status.CreatedAt) err := rows.Scan(&status.Id, &status.User, &status.Content, &status.CreatedAt, &status.Face)
if err != nil { if err != nil {
return status, err return status, err
} }
@ -45,8 +45,8 @@ func (s *Storage) CreateStatus(status model.Status) error {
if err != nil { if err != nil {
return err return err
} }
if err := tx.QueryRowContext(ctx, `INSERT INTO statuses (author, content) VALUES ($1, $2) RETURNING id`, if err := tx.QueryRowContext(ctx, `INSERT INTO statuses (author, content, face) VALUES ($1, $2, $3) RETURNING id`,
status.User, status.Content).Scan(&statusId); err != nil { status.User, status.Content, status.Face).Scan(&statusId); err != nil {
tx.Rollback() tx.Rollback()
return err return err
} }
@ -121,7 +121,8 @@ func (s *Storage) LatestStatuses() ([]model.Status, error) {
statuses.id, statuses.id,
users.name, users.name,
statuses.content, statuses.content,
statuses.created_at statuses.created_at,
statuses.face
from from
users users
inner join statuses inner join statuses
@ -143,11 +144,11 @@ func (s *Storage) LatestStatuses() ([]model.Status, error) {
} }
func (s *Storage) UpdateStatus(status model.Status) error { func (s *Storage) UpdateStatus(status model.Status) error {
stmt, err := s.db.Prepare(`UPDATE statuses SET content = $1 WHERE id = $2 and author = $3;`) stmt, err := s.db.Prepare(`UPDATE statuses SET content = $1, face = $2 WHERE id = $3 and author = $4;`)
if err != nil { if err != nil {
return err return err
} }
_, err = stmt.Exec(status.Content, status.Id, status.User) _, err = stmt.Exec(status.Content, status.Face, status.Id, status.User)
return err return err
} }
@ -186,7 +187,13 @@ func (s *Storage) DeleteStatus(id int64, author string) error {
tx.Rollback() tx.Rollback()
return err return err
} }
} else {
if _, err := tx.ExecContext(ctx, `UPDATE users set status_id=$1 where name=$2`, latestId, author); err != nil {
tx.Rollback()
return err
} }
}
err = tx.Commit() err = tx.Commit()
return err return err
} }

View file

@ -42,7 +42,7 @@ var TplCommonMap = map[string]string{
{{ define "head" }}{{ end }} {{ define "head" }}{{ end }}
`, `,
"status": `{{ define "status" }} "status": `{{ define "status" }}
<div class="status-username"><a href="/users/{{ .User }}">{{ .User }}</a>, {{ .TimeAgo }}</div> <div class="status-username"><a href="/users/{{ .User }}">{{ .User }}</a> {{ .Face }} {{ .TimeAgo }}</div>
<p class="status-content">{{ .Content }}</p> <p class="status-content">{{ .Content }}</p>
{{ end }}`, {{ end }}`,
} }

View file

@ -8,6 +8,7 @@ import (
type StatusForm struct { type StatusForm struct {
Id int64 Id int64
Content string Content string
Face string
Error string Error string
} }
@ -17,5 +18,6 @@ func NewStatusForm(r *http.Request) *StatusForm {
return &StatusForm{ return &StatusForm{
Id: id, Id: id,
Content: r.FormValue("content"), Content: r.FormValue("content"),
Face: r.FormValue("face"),
} }
} }

View file

@ -63,6 +63,19 @@ var TplMap = map[string]string{
</body> </body>
</html>`, </html>`,
"current_status": `{{ define "content" }} "current_status": `{{ define "content" }}
<style>
#statuscafe {
padding: .5em;
background-color: lightblue;
border: 1px solid;
}
#statuscafe-username {
margin-bottom: .5em;
}
#statuscafe-content {
margin: 0 1em 0.5em 1em;
}
</style>
<div class="cols"> <div class="cols">
<section> <section>
<h1>Current Status Widget</h1> <h1>Current Status Widget</h1>
@ -79,7 +92,21 @@ var TplMap = map[string]string{
</table> </table>
</form> </form>
<p>Past this code into your HTML file:</p> <p>Past this code into your HTML file:</p>
<textarea id="code-html" style="width: 100%; height: 75px;"></textarea> <textarea style="width: 100%; height: 75px;"><script src="https://status.cafe/current-status.js?name={{ .name }}"></script></textarea>
<p>Past this code into your CSS file:</p>
<textarea style="width: 100%; height: 75px;">#statuscafe {
padding: .5em;
background-color: lightblue;
border: 1px solid;
}
#statuscafe-username {
margin-bottom: .5em;
}
#statuscafe-content {
margin: 0 1em 0.5em 1em;
}
</textarea>
<p>Make it your own! The CSS above is only an example. Tweak it so that it integrates well with your sites's colors.</p>
</section> </section>
<section> <section>
<h2>Preview</h2> <h2>Preview</h2>
@ -90,19 +117,6 @@ var TplMap = map[string]string{
{{ end }} {{ end }}
</section> </section>
</div> </div>
<script>
function getHTML(name) {
return '<s'+'cript src="https://status.cafe/current-status.js?name=' + name + '"></s'+'cript>'
}
function preview() {
const name = document.getElementById("name").value
if (name) {
const code = getHTML(document.getElementById("name").value)
document.getElementById("code-html").value = code
}
}
preview()
</script>
{{ end }}`, {{ end }}`,
"edit_status": `{{ define "content" }} "edit_status": `{{ define "content" }}
<section> <section>
@ -112,11 +126,59 @@ var TplMap = map[string]string{
{{ end }} {{ end }}
{{ template "flash" .flash }} {{ template "flash" .flash }}
<form action="/edit?id={{ .status.Id }}" method="post"> <form action="/edit?id={{ .status.Id }}" method="post">
<div class="field"> <div style="margin-bottom: 1em">
<div class="radio">
<input type="radio" id="face1" name="face" value="🙂" checked>
<label for="face1">🙂</label>
</div>
<div class="radio">
<input type="radio" id="face2" name="face" value="😎">
<label for="face2">😎</label>
</div>
<div class="radio">
<input type="radio" id="face3" name="face" value="😛">
<label for="face3">😛</label>
</div>
<div class="radio">
<input type="radio" id="face4" name="face" value="🥰">
<label for="face4">🥰</label>
</div>
<div class="radio">
<input type="radio" id="face5" name="face" value="👽">
<label for="face5">👽</label>
</div>
<div class="radio">
<input type="radio" id="face6" name="face" value="😱">
<label for="face6">😱</label>
</div>
<div class="radio">
<input type="radio" id="face7" name="face" value="🤗">
<label for="face7">🤗</label>
</div>
<div class="radio">
<input type="radio" id="face8" name="face" value="😯">
<label for="face8">😯</label>
</div>
<div class="radio">
<input type="radio" id="face9" name="face" value="🤒">
<label for="face9">🤒</label>
</div>
<div class="radio">
<input type="radio" id="face10" name="face" value="😡">
<label for="face10">😡</label>
</div>
<div class="radio">
<input type="radio" id="face11" name="face" value="🥺">
<label for="face11">🥺</label>
</div>
<div class="radio">
<input type="radio" id="face12" name="face" value="🥳">
<label for="face12">🥳</label>
</div>
</div>
<div class="field"> <div class="field">
<textarea name="content" placeholder="What's new?" required style="width: 100%; box-sizing: border-box;" autofocus>{{ .status.Content }}</textarea> <textarea name="content" placeholder="What's new?" required style="width: 100%; box-sizing: border-box;" autofocus>{{ .status.Content }}</textarea>
</div> </div>
</div>
<input type="submit" value="Submit"> <input type="submit" value="Submit">
</form> </form>
</section> </section>
@ -130,15 +192,65 @@ var TplMap = map[string]string{
{{ end }} {{ end }}
{{ template "flash" .flash }} {{ template "flash" .flash }}
<form action="/add" method="post"> <form action="/add" method="post">
<div style="margin-bottom: 1em">
<div class="radio">
<input type="radio" id="face1" name="face" value="🙂" checked>
<label for="face1">🙂</label>
</div>
<div class="radio">
<input type="radio" id="face2" name="face" value="😎">
<label for="face2">😎</label>
</div>
<div class="radio">
<input type="radio" id="face3" name="face" value="😛">
<label for="face3">😛</label>
</div>
<div class="radio">
<input type="radio" id="face4" name="face" value="🥰">
<label for="face4">🥰</label>
</div>
<div class="radio">
<input type="radio" id="face5" name="face" value="👽">
<label for="face5">👽</label>
</div>
<div class="radio">
<input type="radio" id="face6" name="face" value="😱">
<label for="face6">😱</label>
</div>
<div class="radio">
<input type="radio" id="face7" name="face" value="🤗">
<label for="face7">🤗</label>
</div>
<div class="radio">
<input type="radio" id="face8" name="face" value="😯">
<label for="face8">😯</label>
</div>
<div class="radio">
<input type="radio" id="face9" name="face" value="🤒">
<label for="face9">🤒</label>
</div>
<div class="radio">
<input type="radio" id="face10" name="face" value="😡">
<label for="face10">😡</label>
</div>
<div class="radio">
<input type="radio" id="face11" name="face" value="🥺">
<label for="face11">🥺</label>
</div>
<div class="radio">
<input type="radio" id="face12" name="face" value="🥳">
<label for="face12">🥳</label>
</div>
</div>
<div class="field"> <div class="field">
<textarea name="content" maxlength="140" placeholder="What's new?" required style="width: 100%; box-sizing: border-box; height: 100px;" autofocus></textarea> <textarea name="content" maxlength="140" placeholder="What's new?" required style="width: 100%; box-sizing: border-box; height: 100px;" autofocus></textarea>
</div> </div>
<input type="submit" value="Submit"> <input type="submit" value="Submit">
<ul class="tools">
<li>Install the <a href="/about/status-updater">status updater</a> bookmarklet</li>
<li>Add the <a href="/current-status">current status widget</a> to your homepage</li>
</ul>
</form> </form>
<ul class="tools">
<li><a href="/about/status-updater">status updater</a> bookmarklet</li>
<li><a href="/current-status">status widget</a> for your homepage</li>
</ul>
</section> </section>
<section> <section>
<h2>Status stream</h2> <h2>Status stream</h2>
@ -149,7 +261,8 @@ var TplMap = map[string]string{
{{ end }} {{ end }}
</section> </section>
</div> </div>
{{ end }}`, {{ end }}
`,
"intro": `{{ define "content" }} "intro": `{{ define "content" }}
<section> <section>
<h1>Introduction</h1> <h1>Introduction</h1>

View file

@ -1,4 +1,4 @@
{{ define "status" }} {{ define "status" }}
<div class="status-username"><a href="/users/{{ .User }}">{{ .User }}</a>, {{ .TimeAgo }}</div> <div class="status-username"><a href="/users/{{ .User }}">{{ .User }}</a> {{ .Face }} {{ .TimeAgo }}</div>
<p class="status-content">{{ .Content }}</p> <p class="status-content">{{ .Content }}</p>
{{ end }} {{ end }}

View file

@ -1,4 +1,17 @@
{{ define "content" }} {{ define "content" }}
<style>
#statuscafe {
padding: .5em;
background-color: lightblue;
border: 1px solid;
}
#statuscafe-username {
margin-bottom: .5em;
}
#statuscafe-content {
margin: 0 1em 0.5em 1em;
}
</style>
<div class="cols"> <div class="cols">
<section> <section>
<h1>Current Status Widget</h1> <h1>Current Status Widget</h1>
@ -15,7 +28,21 @@
</table> </table>
</form> </form>
<p>Past this code into your HTML file:</p> <p>Past this code into your HTML file:</p>
<textarea id="code-html" style="width: 100%; height: 75px;"></textarea> <textarea style="width: 100%; height: 75px;"><script src="https://status.cafe/current-status.js?name={{ .name }}"></script></textarea>
<p>Past this code into your CSS file:</p>
<textarea style="width: 100%; height: 75px;">#statuscafe {
padding: .5em;
background-color: lightblue;
border: 1px solid;
}
#statuscafe-username {
margin-bottom: .5em;
}
#statuscafe-content {
margin: 0 1em 0.5em 1em;
}
</textarea>
<p>Make it your own! The CSS above is only an example. Tweak it so that it integrates well with your sites's colors.</p>
</section> </section>
<section> <section>
<h2>Preview</h2> <h2>Preview</h2>
@ -26,17 +53,4 @@
{{ end }} {{ end }}
</section> </section>
</div> </div>
<script>
function getHTML(name) {
return '<s'+'cript src="https://status.cafe/current-status.js?name=' + name + '"></s'+'cript>'
}
function preview() {
const name = document.getElementById("name").value
if (name) {
const code = getHTML(document.getElementById("name").value)
document.getElementById("code-html").value = code
}
}
preview()
</script>
{{ end }} {{ end }}

View file

@ -6,11 +6,59 @@
{{ end }} {{ end }}
{{ template "flash" .flash }} {{ template "flash" .flash }}
<form action="/edit?id={{ .status.Id }}" method="post"> <form action="/edit?id={{ .status.Id }}" method="post">
<div class="field"> <div style="margin-bottom: 1em">
<div class="radio">
<input type="radio" id="face1" name="face" value="🙂" checked>
<label for="face1">🙂</label>
</div>
<div class="radio">
<input type="radio" id="face2" name="face" value="😎">
<label for="face2">😎</label>
</div>
<div class="radio">
<input type="radio" id="face3" name="face" value="😛">
<label for="face3">😛</label>
</div>
<div class="radio">
<input type="radio" id="face4" name="face" value="🥰">
<label for="face4">🥰</label>
</div>
<div class="radio">
<input type="radio" id="face5" name="face" value="👽">
<label for="face5">👽</label>
</div>
<div class="radio">
<input type="radio" id="face6" name="face" value="😱">
<label for="face6">😱</label>
</div>
<div class="radio">
<input type="radio" id="face7" name="face" value="🤗">
<label for="face7">🤗</label>
</div>
<div class="radio">
<input type="radio" id="face8" name="face" value="😯">
<label for="face8">😯</label>
</div>
<div class="radio">
<input type="radio" id="face9" name="face" value="🤒">
<label for="face9">🤒</label>
</div>
<div class="radio">
<input type="radio" id="face10" name="face" value="😡">
<label for="face10">😡</label>
</div>
<div class="radio">
<input type="radio" id="face11" name="face" value="🥺">
<label for="face11">🥺</label>
</div>
<div class="radio">
<input type="radio" id="face12" name="face" value="🥳">
<label for="face12">🥳</label>
</div>
</div>
<div class="field"> <div class="field">
<textarea name="content" placeholder="What's new?" required style="width: 100%; box-sizing: border-box;" autofocus>{{ .status.Content }}</textarea> <textarea name="content" placeholder="What's new?" required style="width: 100%; box-sizing: border-box;" autofocus>{{ .status.Content }}</textarea>
</div> </div>
</div>
<input type="submit" value="Submit"> <input type="submit" value="Submit">
</form> </form>
</section> </section>

View file

@ -7,15 +7,65 @@
{{ end }} {{ end }}
{{ template "flash" .flash }} {{ template "flash" .flash }}
<form action="/add" method="post"> <form action="/add" method="post">
<div style="margin-bottom: 1em">
<div class="radio">
<input type="radio" id="face1" name="face" value="🙂" checked>
<label for="face1">🙂</label>
</div>
<div class="radio">
<input type="radio" id="face2" name="face" value="😎">
<label for="face2">😎</label>
</div>
<div class="radio">
<input type="radio" id="face3" name="face" value="😛">
<label for="face3">😛</label>
</div>
<div class="radio">
<input type="radio" id="face4" name="face" value="🥰">
<label for="face4">🥰</label>
</div>
<div class="radio">
<input type="radio" id="face5" name="face" value="👽">
<label for="face5">👽</label>
</div>
<div class="radio">
<input type="radio" id="face6" name="face" value="😱">
<label for="face6">😱</label>
</div>
<div class="radio">
<input type="radio" id="face7" name="face" value="🤗">
<label for="face7">🤗</label>
</div>
<div class="radio">
<input type="radio" id="face8" name="face" value="😯">
<label for="face8">😯</label>
</div>
<div class="radio">
<input type="radio" id="face9" name="face" value="🤒">
<label for="face9">🤒</label>
</div>
<div class="radio">
<input type="radio" id="face10" name="face" value="😡">
<label for="face10">😡</label>
</div>
<div class="radio">
<input type="radio" id="face11" name="face" value="🥺">
<label for="face11">🥺</label>
</div>
<div class="radio">
<input type="radio" id="face12" name="face" value="🥳">
<label for="face12">🥳</label>
</div>
</div>
<div class="field"> <div class="field">
<textarea name="content" maxlength="140" placeholder="What's new?" required style="width: 100%; box-sizing: border-box; height: 100px;" autofocus></textarea> <textarea name="content" maxlength="140" placeholder="What's new?" required style="width: 100%; box-sizing: border-box; height: 100px;" autofocus></textarea>
</div> </div>
<input type="submit" value="Submit"> <input type="submit" value="Submit">
<ul class="tools">
<li>Install the <a href="/about/status-updater">status updater</a> bookmarklet</li>
<li>Add the <a href="/current-status">current status widget</a> to your homepage</li>
</ul>
</form> </form>
<ul class="tools">
<li><a href="/about/status-updater">status updater</a> bookmarklet</li>
<li><a href="/current-status">status widget</a> for your homepage</li>
</ul>
</section> </section>
<section> <section>
<h2>Status stream</h2> <h2>Status stream</h2>

View file

@ -16,6 +16,7 @@ func (h *Handler) saveStatus(w http.ResponseWriter, r *http.Request) {
status := model.Status{ status := model.Status{
User: user, User: user,
Content: f.Content, Content: f.Content,
Face: f.Face,
} }
if err := status.Validate(); err != nil { if err := status.Validate(); err != nil {
f.Error = err.Error() f.Error = err.Error()

View file

@ -30,6 +30,7 @@ func (h *Handler) updateStatus(w http.ResponseWriter, r *http.Request) {
} }
f := form.NewStatusForm(r) f := form.NewStatusForm(r)
status.Content = f.Content status.Content = f.Content
status.Face = f.Face
if err := status.Validate(); err != nil { if err := status.Validate(); err != nil {
f.Error = err.Error() f.Error = err.Error()
h.renderLayout(w, "edit_post", map[string]interface{}{ h.renderLayout(w, "edit_post", map[string]interface{}{

View file

@ -15,7 +15,7 @@ fetch("https://status.cafe/users/` + name + `/status.json")
document.getElementById("statuscafe-content").innerHTML = "No status yet." document.getElementById("statuscafe-content").innerHTML = "No status yet."
return return
} }
document.getElementById("statuscafe-username").innerHTML = '<a href="https://status.cafe/users/` + name + `">' + r.author + '</a>\'s status, updated ' + r.timeAgo document.getElementById("statuscafe-username").innerHTML = '<a href="https://status.cafe/users/` + name + `">' + r.author + '</a>, ' + r.timeAgo
document.getElementById("statuscafe-content").innerHTML = r.content document.getElementById("statuscafe-content").innerHTML = r.content
}) })
`)) `))