mirror of
https://git.freesoftwareextremist.com/bloat
synced 2024-09-16 12:20:51 +00:00
Merge branch 'master' into absolute_fluoride
This commit is contained in:
commit
f55595ddb6
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
|||
bloat
|
||||
database
|
||||
bloat.gen.conf
|
||||
|
|
1
INSTALL
1
INSTALL
|
@ -20,6 +20,7 @@ bloat looks for a file named bloat.conf in the working directory and
|
|||
/etc/bloat in that order. You can also specify another file by using the -f
|
||||
flag. Comments in the config file describe what each config value does. For
|
||||
most cases, you only need to change the value of "client_website".
|
||||
# cp bloat.gen.conf /etc/bloat.conf
|
||||
# $EDITOR /etc/bloat.conf
|
||||
|
||||
4. Create database directory
|
||||
|
|
9
Makefile
9
Makefile
|
@ -18,6 +18,10 @@ all: bloat
|
|||
|
||||
bloat: $(SRC) $(TMPL)
|
||||
$(GO) build $(GOFLAGS) -o bloat main.go
|
||||
sed -e "s%=database%=/var/bloat%g" \
|
||||
-e "s%=templates%=$(SHAREPATH)/templates%g" \
|
||||
-e "s%=static%=$(SHAREPATH)/static%g" \
|
||||
< bloat.conf > bloat.gen.conf
|
||||
|
||||
install: bloat
|
||||
mkdir -p $(DESTDIR)$(BINPATH) \
|
||||
|
@ -29,10 +33,6 @@ install: bloat
|
|||
chmod 0644 $(DESTDIR)$(SHAREPATH)/templates/*
|
||||
cp -r static/* $(DESTDIR)$(SHAREPATH)/static
|
||||
chmod 0644 $(DESTDIR)$(SHAREPATH)/static/*
|
||||
sed -e "s%=database%=/var/bloat%g" \
|
||||
-e "s%=templates%=$(SHAREPATH)/templates%g" \
|
||||
-e "s%=static%=$(SHAREPATH)/static%g" \
|
||||
< bloat.conf > /etc/bloat.conf
|
||||
|
||||
uninstall:
|
||||
rm -f $(DESTDIR)$(BINPATH)/bloat
|
||||
|
@ -40,3 +40,4 @@ uninstall:
|
|||
|
||||
clean:
|
||||
rm -f bloat
|
||||
rm -f bloat.gen.conf
|
||||
|
|
|
@ -189,6 +189,7 @@ type Relationship struct {
|
|||
Following bool `json:"following"`
|
||||
FollowedBy bool `json:"followed_by"`
|
||||
Blocking bool `json:"blocking"`
|
||||
BlockedBy bool `json:"blocked_by"`
|
||||
Muting bool `json:"muting"`
|
||||
MutingNotifications bool `json:"muting_notifications"`
|
||||
Subscribing bool `json:"subscribing"`
|
||||
|
|
|
@ -90,7 +90,7 @@ func (c *Client) DeleteList(ctx context.Context, id string) error {
|
|||
func (c *Client) AddToList(ctx context.Context, list string, accounts ...string) error {
|
||||
params := url.Values{}
|
||||
for _, acct := range accounts {
|
||||
params.Add("account_ids", string(acct))
|
||||
params.Add("account_ids[]", string(acct))
|
||||
}
|
||||
|
||||
return c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/lists/%s/accounts", url.PathEscape(string(list))), params, nil, nil)
|
||||
|
@ -100,7 +100,7 @@ func (c *Client) AddToList(ctx context.Context, list string, accounts ...string)
|
|||
func (c *Client) RemoveFromList(ctx context.Context, list string, accounts ...string) error {
|
||||
params := url.Values{}
|
||||
for _, acct := range accounts {
|
||||
params.Add("account_ids", string(acct))
|
||||
params.Add("account_ids[]", string(acct))
|
||||
}
|
||||
|
||||
return c.doAPI(ctx, http.MethodDelete, fmt.Sprintf("/api/v1/lists/%s/accounts", url.PathEscape(string(list))), params, nil, nil)
|
||||
|
|
|
@ -56,7 +56,6 @@ type Status struct {
|
|||
MediaAttachments []Attachment `json:"media_attachments"`
|
||||
Mentions []Mention `json:"mentions"`
|
||||
Tags []Tag `json:"tags"`
|
||||
Card *Card `json:"card"`
|
||||
Application Application `json:"application"`
|
||||
Language string `json:"language"`
|
||||
Pinned interface{} `json:"pinned"`
|
||||
|
@ -77,22 +76,6 @@ type Context struct {
|
|||
Descendants []*Status `json:"descendants"`
|
||||
}
|
||||
|
||||
// Card hold information for mastodon card.
|
||||
type Card struct {
|
||||
URL string `json:"url"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Image string `json:"image"`
|
||||
Type string `json:"type"`
|
||||
AuthorName string `json:"author_name"`
|
||||
AuthorURL string `json:"author_url"`
|
||||
ProviderName string `json:"provider_name"`
|
||||
ProviderURL string `json:"provider_url"`
|
||||
HTML string `json:"html"`
|
||||
Width int64 `json:"width"`
|
||||
Height int64 `json:"height"`
|
||||
}
|
||||
|
||||
// GetFavourites return the favorite list of the current user.
|
||||
func (c *Client) GetFavourites(ctx context.Context, pg *Pagination) ([]*Status, error) {
|
||||
var statuses []*Status
|
||||
|
@ -123,16 +106,6 @@ func (c *Client) GetStatusContext(ctx context.Context, id string) (*Context, err
|
|||
return &context, nil
|
||||
}
|
||||
|
||||
// GetStatusCard return status specified by id.
|
||||
func (c *Client) GetStatusCard(ctx context.Context, id string) (*Card, error) {
|
||||
var card Card
|
||||
err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%s/card", id), nil, &card, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &card, nil
|
||||
}
|
||||
|
||||
// GetRebloggedBy returns the account list of the user who reblogged the toot of id.
|
||||
func (c *Client) GetRebloggedBy(ctx context.Context, id string, pg *Pagination) ([]*Account, error) {
|
||||
var accounts []*Account
|
||||
|
@ -301,7 +274,7 @@ func (c *Client) DeleteStatus(ctx context.Context, id string) error {
|
|||
}
|
||||
|
||||
// Search search content with query.
|
||||
func (c *Client) Search(ctx context.Context, q string, qType string, limit int, resolve bool, offset int, accountID string) (*Results, error) {
|
||||
func (c *Client) Search(ctx context.Context, q string, qType string, limit int, resolve bool, offset int, accountID string, following bool) (*Results, error) {
|
||||
var results Results
|
||||
params := url.Values{}
|
||||
params.Set("q", q)
|
||||
|
@ -309,6 +282,7 @@ func (c *Client) Search(ctx context.Context, q string, qType string, limit int,
|
|||
params.Set("limit", fmt.Sprint(limit))
|
||||
params.Set("resolve", fmt.Sprint(resolve))
|
||||
params.Set("offset", fmt.Sprint(offset))
|
||||
params.Set("following", fmt.Sprint(following))
|
||||
if len(accountID) > 0 {
|
||||
params.Set("account_id", accountID)
|
||||
}
|
||||
|
|
|
@ -62,6 +62,19 @@ type TimelineData struct {
|
|||
PrevLink string
|
||||
}
|
||||
|
||||
type ListsData struct {
|
||||
*CommonData
|
||||
Lists []*mastodon.List
|
||||
}
|
||||
|
||||
type ListData struct {
|
||||
*CommonData
|
||||
List *mastodon.List
|
||||
Accounts []*mastodon.Account
|
||||
Q string
|
||||
SearchAccounts []*mastodon.Account
|
||||
}
|
||||
|
||||
type ThreadData struct {
|
||||
*CommonData
|
||||
Statuses []*mastodon.Status
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
package renderer
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"io"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"bloat/mastodon"
|
||||
|
@ -19,6 +19,8 @@ const (
|
|||
NavPage = "nav.tmpl"
|
||||
RootPage = "root.tmpl"
|
||||
TimelinePage = "timeline.tmpl"
|
||||
ListsPage = "lists.tmpl"
|
||||
ListPage = "list.tmpl"
|
||||
ThreadPage = "thread.tmpl"
|
||||
QuickReplyPage = "quickreply.tmpl"
|
||||
StatusPopup = "status.tmpl"
|
||||
|
@ -53,10 +55,7 @@ func emojiFilter(content string, emojis []mastodon.Emoji) string {
|
|||
|
||||
var quoteRE = regexp.MustCompile("(?mU)(^|> *|\n)(>.*)(<br|$)")
|
||||
|
||||
func statusContentFilter(spoiler, content string, emojis []mastodon.Emoji, mentions []mastodon.Mention) string {
|
||||
if len(spoiler) > 0 {
|
||||
content = spoiler + "<br/>" + content
|
||||
}
|
||||
func statusContentFilter(content string, emojis []mastodon.Emoji, mentions []mastodon.Mention) string {
|
||||
content = quoteRE.ReplaceAllString(content, `$1<span class="quote">$2</span>$3`)
|
||||
var replacements []string
|
||||
for _, e := range emojis {
|
||||
|
@ -75,45 +74,41 @@ func displayInteractionCount(c int64) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func DurToStr(dur time.Duration) string {
|
||||
s := dur.Seconds()
|
||||
func durUnit(s int64) (dur int64, unit string) {
|
||||
if s < 60 {
|
||||
return strconv.Itoa(int(s)) + "s"
|
||||
if s < 0 {
|
||||
s = 0
|
||||
}
|
||||
return s, "s"
|
||||
}
|
||||
m := dur.Minutes()
|
||||
if m < 60*2 {
|
||||
return strconv.Itoa(int(m)) + "m"
|
||||
}
|
||||
h := dur.Hours()
|
||||
if h < 24*2 {
|
||||
return strconv.Itoa(int(h)) + "h"
|
||||
m := s / 60
|
||||
h := m / 60
|
||||
if h < 2 {
|
||||
return m, "m"
|
||||
}
|
||||
d := h / 24
|
||||
if d < 30*2 {
|
||||
return strconv.Itoa(int(d)) + "d"
|
||||
if d < 2 {
|
||||
return h, "h"
|
||||
}
|
||||
mo := d / 30
|
||||
if mo < 12*2 {
|
||||
return strconv.Itoa(int(mo)) + "mo"
|
||||
if mo < 2 {
|
||||
return d, "d"
|
||||
}
|
||||
y := mo / 12
|
||||
return strconv.Itoa(int(y)) + "y"
|
||||
y := d / 365
|
||||
if y < 2 {
|
||||
return mo, "mo"
|
||||
}
|
||||
return y, "y"
|
||||
}
|
||||
|
||||
func timeSince(t time.Time) string {
|
||||
d := time.Since(t)
|
||||
if d < 0 {
|
||||
d = 0
|
||||
}
|
||||
return DurToStr(d)
|
||||
d, u := durUnit(time.Now().Unix() - t.Unix())
|
||||
return strconv.FormatInt(d, 10) + u
|
||||
}
|
||||
|
||||
func timeUntil(t time.Time) string {
|
||||
d := time.Until(t)
|
||||
if d < 0 {
|
||||
d = 0
|
||||
}
|
||||
return DurToStr(d)
|
||||
d, u := durUnit(t.Unix() - time.Now().Unix())
|
||||
return strconv.FormatInt(d, 10) + u
|
||||
}
|
||||
|
||||
func formatTimeRFC3339(t time.Time) string {
|
||||
|
@ -128,6 +123,14 @@ func withContext(data interface{}, ctx *Context) TemplateData {
|
|||
return TemplateData{data, ctx}
|
||||
}
|
||||
|
||||
func raw(s string) template.HTML {
|
||||
return template.HTML(s)
|
||||
}
|
||||
|
||||
func rawCSS(s string) template.CSS {
|
||||
return template.CSS(s)
|
||||
}
|
||||
|
||||
type Renderer interface {
|
||||
Render(ctx *Context, writer io.Writer, page string, data interface{}) (err error)
|
||||
}
|
||||
|
@ -147,6 +150,9 @@ func NewRenderer(templateGlobPattern string) (r *renderer, err error) {
|
|||
"FormatTimeRFC3339": formatTimeRFC3339,
|
||||
"FormatTimeRFC822": formatTimeRFC822,
|
||||
"WithContext": withContext,
|
||||
"HTML": template.HTMLEscapeString,
|
||||
"Raw": raw,
|
||||
"RawCSS": rawCSS,
|
||||
}).ParseGlob(templateGlobPattern)
|
||||
if err != nil {
|
||||
return
|
||||
|
|
|
@ -163,8 +163,8 @@ func (s *service) NavPage(c *client) (err error) {
|
|||
return s.renderer.Render(c.rctx, c.w, renderer.NavPage, data)
|
||||
}
|
||||
|
||||
func (s *service) TimelinePage(c *client, tType string, instance string,
|
||||
maxID string, minID string) (err error) {
|
||||
func (s *service) TimelinePage(c *client, tType, instance, listId, maxID,
|
||||
minID string) (err error) {
|
||||
|
||||
var nextLink, prevLink, title string
|
||||
var statuses []*mastodon.Status
|
||||
|
@ -179,24 +179,46 @@ func (s *service) TimelinePage(c *client, tType string, instance string,
|
|||
return errInvalidArgument
|
||||
case "home":
|
||||
statuses, err = c.GetTimelineHome(c.ctx, &pg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
title = "Timeline"
|
||||
case "direct":
|
||||
statuses, err = c.GetTimelineDirect(c.ctx, &pg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
title = "Direct Timeline"
|
||||
case "local":
|
||||
statuses, err = c.GetTimelinePublic(c.ctx, true, "", &pg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
title = "Local Timeline"
|
||||
case "remote":
|
||||
if len(instance) > 0 {
|
||||
statuses, err = c.GetTimelinePublic(c.ctx, false, instance, &pg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
title = "Remote Timeline"
|
||||
case "twkn":
|
||||
statuses, err = c.GetTimelinePublic(c.ctx, false, "", &pg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
title = "The Whole Known Network"
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
case "list":
|
||||
statuses, err = c.GetTimelineList(c.ctx, listId, &pg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
list, err := c.GetList(c.ctx, listId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
title = "List Timeline - " + list.Title
|
||||
}
|
||||
|
||||
for i := range statuses {
|
||||
|
@ -211,6 +233,9 @@ func (s *service) TimelinePage(c *client, tType string, instance string,
|
|||
if len(instance) > 0 {
|
||||
v.Set("instance", instance)
|
||||
}
|
||||
if len(listId) > 0 {
|
||||
v.Set("list", listId)
|
||||
}
|
||||
prevLink = "/timeline/" + tType + "?" + v.Encode()
|
||||
}
|
||||
|
||||
|
@ -220,6 +245,9 @@ func (s *service) TimelinePage(c *client, tType string, instance string,
|
|||
if len(instance) > 0 {
|
||||
v.Set("instance", instance)
|
||||
}
|
||||
if len(listId) > 0 {
|
||||
v.Set("list", listId)
|
||||
}
|
||||
nextLink = "/timeline/" + tType + "?" + v.Encode()
|
||||
}
|
||||
|
||||
|
@ -252,6 +280,70 @@ func addToReplyMap(m map[string][]mastodon.ReplyInfo, key interface{},
|
|||
m[keyStr] = append(m[keyStr], mastodon.ReplyInfo{val, number})
|
||||
}
|
||||
|
||||
func (s *service) ListsPage(c *client) (err error) {
|
||||
lists, err := c.GetLists(c.ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
cdata := s.cdata(c, "Lists", 0, 0, "")
|
||||
data := renderer.ListsData{
|
||||
Lists: lists,
|
||||
CommonData: cdata,
|
||||
}
|
||||
return s.renderer.Render(c.rctx, c.w, renderer.ListsPage, data)
|
||||
}
|
||||
|
||||
func (s *service) AddList(c *client, title string) (err error) {
|
||||
_, err = c.CreateList(c.ctx, title)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *service) RemoveList(c *client, id string) (err error) {
|
||||
return c.DeleteList(c.ctx, id)
|
||||
}
|
||||
|
||||
func (s *service) RenameList(c *client, id, title string) (err error) {
|
||||
_, err = c.RenameList(c.ctx, id, title)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *service) ListPage(c *client, id string, q string) (err error) {
|
||||
list, err := c.GetList(c.ctx, id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
accounts, err := c.GetListAccounts(c.ctx, id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var searchAccounts []*mastodon.Account
|
||||
if len(q) > 0 {
|
||||
result, err := c.Search(c.ctx, q, "accounts", 20, true, 0, id, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
searchAccounts = result.Accounts
|
||||
}
|
||||
cdata := s.cdata(c, "List "+list.Title, 0, 0, "")
|
||||
data := renderer.ListData{
|
||||
List: list,
|
||||
Accounts: accounts,
|
||||
Q: q,
|
||||
SearchAccounts: searchAccounts,
|
||||
CommonData: cdata,
|
||||
}
|
||||
return s.renderer.Render(c.rctx, c.w, renderer.ListPage, data)
|
||||
}
|
||||
|
||||
func (s *service) ListAddUser(c *client, id string, uid string) (err error) {
|
||||
return c.AddToList(c.ctx, id, uid)
|
||||
}
|
||||
|
||||
func (s *service) ListRemoveUser(c *client, id string, uid string) (err error) {
|
||||
return c.RemoveFromList(c.ctx, id, uid)
|
||||
}
|
||||
|
||||
func (s *service) ThreadPage(c *client, id string, reply bool) (err error) {
|
||||
var pctx model.PostContext
|
||||
|
||||
|
@ -616,7 +708,7 @@ func (s *service) UserSearchPage(c *client,
|
|||
|
||||
var results *mastodon.Results
|
||||
if len(q) > 0 {
|
||||
results, err = c.Search(c.ctx, q, "statuses", 20, true, offset, id)
|
||||
results, err = c.Search(c.ctx, q, "statuses", 20, true, offset, id, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -627,7 +719,7 @@ func (s *service) UserSearchPage(c *client,
|
|||
if len(results.Statuses) == 20 {
|
||||
offset += 20
|
||||
nextLink = fmt.Sprintf("/usersearch/%s?q=%s&offset=%d", id,
|
||||
url.QueryEscape(q), offset)
|
||||
q, offset)
|
||||
}
|
||||
|
||||
if len(q) > 0 {
|
||||
|
@ -674,7 +766,7 @@ func (s *service) SearchPage(c *client,
|
|||
|
||||
var results *mastodon.Results
|
||||
if len(q) > 0 {
|
||||
results, err = c.Search(c.ctx, q, qType, 20, true, offset, "")
|
||||
results, err = c.Search(c.ctx, q, qType, 20, true, offset, "", false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -686,7 +778,7 @@ func (s *service) SearchPage(c *client,
|
|||
(qType == "statuses" && len(results.Statuses) == 20) {
|
||||
offset += 20
|
||||
nextLink = fmt.Sprintf("/search?q=%s&type=%s&offset=%d",
|
||||
url.QueryEscape(q), qType, offset)
|
||||
q, qType, offset)
|
||||
}
|
||||
|
||||
if len(q) > 0 {
|
||||
|
|
|
@ -169,9 +169,10 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
|||
tType, _ := mux.Vars(c.r)["type"]
|
||||
q := c.r.URL.Query()
|
||||
instance := q.Get("instance")
|
||||
list := q.Get("list")
|
||||
maxID := q.Get("max_id")
|
||||
minID := q.Get("min_id")
|
||||
return s.TimelinePage(c, tType, instance, maxID, minID)
|
||||
return s.TimelinePage(c, tType, instance, list, maxID, minID)
|
||||
}, SESSION, HTML)
|
||||
|
||||
defaultTimelinePage := handle(func(c *client) error {
|
||||
|
@ -606,6 +607,72 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
|||
return nil
|
||||
}, CSRF, HTML)
|
||||
|
||||
listsPage := handle(func(c *client) error {
|
||||
return s.ListsPage(c)
|
||||
}, SESSION, HTML)
|
||||
|
||||
addList := handle(func(c *client) error {
|
||||
title := c.r.FormValue("title")
|
||||
err := s.AddList(c, title)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
redirect(c, c.r.FormValue("referrer"))
|
||||
return nil
|
||||
}, CSRF, HTML)
|
||||
|
||||
removeList := handle(func(c *client) error {
|
||||
id, _ := mux.Vars(c.r)["id"]
|
||||
err := s.RemoveList(c, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
redirect(c, c.r.FormValue("referrer"))
|
||||
return nil
|
||||
}, CSRF, HTML)
|
||||
|
||||
renameList := handle(func(c *client) error {
|
||||
id, _ := mux.Vars(c.r)["id"]
|
||||
title := c.r.FormValue("title")
|
||||
err := s.RenameList(c, id, title)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
redirect(c, c.r.FormValue("referrer"))
|
||||
return nil
|
||||
}, CSRF, HTML)
|
||||
|
||||
listPage := handle(func(c *client) error {
|
||||
id, _ := mux.Vars(c.r)["id"]
|
||||
q := c.r.URL.Query()
|
||||
sq := q.Get("q")
|
||||
return s.ListPage(c, id, sq)
|
||||
}, SESSION, HTML)
|
||||
|
||||
listAddUser := handle(func(c *client) error {
|
||||
id, _ := mux.Vars(c.r)["id"]
|
||||
q := c.r.URL.Query()
|
||||
uid := q.Get("uid")
|
||||
err := s.ListAddUser(c, id, uid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
redirect(c, c.r.FormValue("referrer"))
|
||||
return nil
|
||||
}, CSRF, HTML)
|
||||
|
||||
listRemoveUser := handle(func(c *client) error {
|
||||
id, _ := mux.Vars(c.r)["id"]
|
||||
q := c.r.URL.Query()
|
||||
uid := q.Get("uid")
|
||||
err := s.ListRemoveUser(c, id, uid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
redirect(c, c.r.FormValue("referrer"))
|
||||
return nil
|
||||
}, CSRF, HTML)
|
||||
|
||||
signout := handle(func(c *client) error {
|
||||
s.Signout(c)
|
||||
setSessionCookie(c.w, "", 0)
|
||||
|
@ -699,6 +766,13 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
|||
r.HandleFunc("/unbookmark/{id}", unBookmark).Methods(http.MethodPost)
|
||||
r.HandleFunc("/filter", filter).Methods(http.MethodPost)
|
||||
r.HandleFunc("/unfilter/{id}", unFilter).Methods(http.MethodPost)
|
||||
r.HandleFunc("/lists", listsPage).Methods(http.MethodGet)
|
||||
r.HandleFunc("/list", addList).Methods(http.MethodPost)
|
||||
r.HandleFunc("/list/{id}", listPage).Methods(http.MethodGet)
|
||||
r.HandleFunc("/list/{id}/remove", removeList).Methods(http.MethodPost)
|
||||
r.HandleFunc("/list/{id}/rename", renameList).Methods(http.MethodPost)
|
||||
r.HandleFunc("/list/{id}/adduser", listAddUser).Methods(http.MethodPost)
|
||||
r.HandleFunc("/list/{id}/removeuser", listRemoveUser).Methods(http.MethodPost)
|
||||
r.HandleFunc("/signout", signout).Methods(http.MethodPost)
|
||||
r.HandleFunc("/fluoride/like/{id}", fLike).Methods(http.MethodPost)
|
||||
r.HandleFunc("/fluoride/unlike/{id}", fUnlike).Methods(http.MethodPost)
|
||||
|
|
|
@ -152,6 +152,7 @@ function replyToLinkLocal(a) {
|
|||
var copy = status.cloneNode(true);
|
||||
copy.id = "reply-to-popup";
|
||||
var ract = event.target.getBoundingClientRect();
|
||||
copy.style["max-width"] = (window.innerWidth - ract.left - 32) + "px";
|
||||
if (ract.top > window.innerHeight / 2) {
|
||||
copy.style.bottom = (window.innerHeight -
|
||||
window.scrollY - ract.top) + "px";
|
||||
|
@ -245,6 +246,7 @@ function handleReplyLink(a) {
|
|||
var copy = status.cloneNode(true);
|
||||
copy.id = "reply-popup";
|
||||
var ract = event.target.getBoundingClientRect();
|
||||
copy.style["max-width"] = (window.innerWidth - 98) + "px";
|
||||
if (ract.left > window.innerWidth / 2) {
|
||||
copy.style.right = (window.innerWidth -
|
||||
ract.right - 12) + "px";
|
||||
|
@ -334,6 +336,20 @@ function handleImgPreview(a) {
|
|||
}
|
||||
}
|
||||
|
||||
function onPaste(e) {
|
||||
if (!e.clipboardData.files)
|
||||
return;
|
||||
var fp = document.querySelector("#post-file-picker")
|
||||
var dt = new DataTransfer();
|
||||
for (var i = 0; i < fp.files.length; i++) {
|
||||
dt.items.add(fp.files[i]);
|
||||
}
|
||||
for (var i = 0; i < e.clipboardData.files.length; i++) {
|
||||
dt.items.add(e.clipboardData.files[i]);
|
||||
}
|
||||
fp.files = dt.files;
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
checkCSRFToken();
|
||||
checkAntiDopamineMode();
|
||||
|
@ -363,7 +379,7 @@ document.addEventListener("DOMContentLoaded", function() {
|
|||
}
|
||||
}
|
||||
|
||||
var links = document.querySelectorAll(".user-profile-decription a");
|
||||
var links = document.querySelectorAll(".user-profile-decription a, .user-fields a");
|
||||
for (var j = 0; j < links.length; j++) {
|
||||
links[j].target = "_blank";
|
||||
}
|
||||
|
@ -372,6 +388,10 @@ document.addEventListener("DOMContentLoaded", function() {
|
|||
for (var j = 0; j < links.length; j++) {
|
||||
handleImgPreview(links[j]);
|
||||
}
|
||||
|
||||
var pf = document.querySelector(".post-form")
|
||||
if (pf)
|
||||
pf.addEventListener("paste", onPaste);
|
||||
});
|
||||
|
||||
// @license-end
|
||||
|
|
|
@ -194,11 +194,14 @@ textarea {
|
|||
border-color: #777777;
|
||||
}
|
||||
|
||||
.notification-container.favourite .status-container,
|
||||
.notification-container.reblog .status-container {
|
||||
.notification-container .status-container {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.notification-container.mention .status-container {
|
||||
opacity: unset;
|
||||
}
|
||||
|
||||
.notification-info-text span {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
@ -277,7 +280,8 @@ textarea {
|
|||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.user-profile-decription {
|
||||
.user-profile-decription,
|
||||
.user-fields {
|
||||
overflow-wrap: break-word;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
@ -286,10 +290,22 @@ textarea {
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
.user-profile-decription img {
|
||||
height: auto;
|
||||
width: auto;
|
||||
max-height: 240px;
|
||||
max-width: 280px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.d-inline {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.p-0 {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.btn-link {
|
||||
border: none;
|
||||
outline: none;
|
||||
|
@ -354,10 +370,6 @@ a:hover,
|
|||
display: none;
|
||||
}
|
||||
|
||||
.post-form-field>* {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.emoji-item-container {
|
||||
width: 220px;
|
||||
display: inline-block;
|
||||
|
@ -422,9 +434,6 @@ img.emoji {
|
|||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.user-list-container {
|
||||
}
|
||||
|
||||
.user-list-item {
|
||||
overflow: auto;
|
||||
margin: 0 0 12px 0;
|
||||
|
@ -441,6 +450,10 @@ img.emoji {
|
|||
overflow: auto;
|
||||
}
|
||||
|
||||
.user-list-action {
|
||||
margin: 0 12px;
|
||||
}
|
||||
|
||||
#settings-form {
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
@ -449,10 +462,6 @@ img.emoji {
|
|||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.settings-form-field>* {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#settings-form button[type=submit] {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
|
|
@ -46,11 +46,11 @@
|
|||
<td> <kbd>6</kbd> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> Settings </td>
|
||||
<td> Lists </td>
|
||||
<td> <kbd>7</kbd> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> Signout </td>
|
||||
<td> Settings </td>
|
||||
<td> <kbd>8</kbd> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
{{range .Emojis}}
|
||||
<div class="emoji-item-container">
|
||||
<div class="emoji-item">
|
||||
<img class="emoji" src="{{.URL}}" alt="{{.ShortCode}}" height="32" />
|
||||
<img class="emoji" src="{{.URL}}" alt="{{.ShortCode}}" height="32" loading="lazy" />
|
||||
<span title=":{{.ShortCode}}:" class="emoji-shortcode">:{{.ShortCode}}:</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
{{if .RefreshInterval}}
|
||||
<meta http-equiv="refresh" content="{{.RefreshInterval}}">
|
||||
{{end}}
|
||||
<title> {{if gt .Count 0}}({{.Count}}){{end}} {{.Title | html}} </title>
|
||||
<title> {{if gt .Count 0}}({{.Count}}){{end}} {{.Title}} </title>
|
||||
<link rel="stylesheet" href="/static/style.css">
|
||||
{{if .CustomCSS}}
|
||||
<link rel="stylesheet" href="{{.CustomCSS}}">
|
||||
|
@ -26,7 +26,7 @@
|
|||
<script src="/static/fluoride.js"></script>
|
||||
{{end}}
|
||||
{{if $.Ctx.UserCSS}}
|
||||
<style>{{$.Ctx.UserCSS}}</style>
|
||||
<style>{{RawCSS $.Ctx.UserCSS}}</style>
|
||||
{{end}}
|
||||
</head>
|
||||
<body {{if $.Ctx.DarkMode}}class="dark"{{end}}>
|
||||
|
|
63
templates/list.tmpl
Normal file
63
templates/list.tmpl
Normal file
|
@ -0,0 +1,63 @@
|
|||
{{with .Data}}
|
||||
{{template "header.tmpl" (WithContext .CommonData $.Ctx)}}
|
||||
<div class="page-title"> List {{.List.Title}} </div>
|
||||
|
||||
<form action="/list/{{.List.ID}}/rename" method="POST">
|
||||
<input type="hidden" name="csrf_token" value="{{$.Ctx.CSRFToken}}">
|
||||
<input type="hidden" name="referrer" value="{{$.Ctx.Referrer}}">
|
||||
<input id="title" name="title" value="{{.List.Title}}">
|
||||
<button type="submit"> Rename </button>
|
||||
</form>
|
||||
|
||||
<div class="page-title"> Users </div>
|
||||
{{if .Accounts}}
|
||||
<table>
|
||||
{{range .Accounts}}
|
||||
<tr>
|
||||
<td class="p-0"> {{template "userlistitem.tmpl" (WithContext . $.Ctx)}} </td>
|
||||
<td class="p-0">
|
||||
<form class="user-list-action" action="/list/{{$.Data.List.ID}}/removeuser?uid={{.ID}}" method="POST">
|
||||
<input type="hidden" name="csrf_token" value="{{$.Ctx.CSRFToken}}">
|
||||
<input type="hidden" name="referrer" value="{{$.Ctx.Referrer}}">
|
||||
<button type="submit"> Remove </button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</table>
|
||||
{{else}}
|
||||
<div class="no-data-found">No data found</div>
|
||||
{{end}}
|
||||
|
||||
<div class="page-title"> Add user </div>
|
||||
<form class="search-form" action="/list/{{.List.ID}}" method="GET">
|
||||
<span class="post-form-field">
|
||||
<label for="query"> Query </label>
|
||||
<input id="query" name="q" value="{{.Q}}">
|
||||
</span>
|
||||
<button type="submit"> Search </button>
|
||||
</form>
|
||||
|
||||
{{if .Q}}
|
||||
{{if .SearchAccounts}}
|
||||
<table>
|
||||
{{range .SearchAccounts}}
|
||||
<tr>
|
||||
<td> {{template "userlistitem.tmpl" (WithContext . $.Ctx)}} </td>
|
||||
<td>
|
||||
<form class="user-list-action" action="/list/{{$.Data.List.ID}}/adduser?uid={{.ID}}" method="POST">
|
||||
<input type="hidden" name="csrf_token" value="{{$.Ctx.CSRFToken}}">
|
||||
<input type="hidden" name="referrer" value="{{$.Ctx.Referrer}}">
|
||||
<button type="submit"> Add </button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</table>
|
||||
{{else}}
|
||||
<div class="no-data-found">No data found</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{template "footer.tmpl"}}
|
||||
{{end}}
|
35
templates/lists.tmpl
Normal file
35
templates/lists.tmpl
Normal file
|
@ -0,0 +1,35 @@
|
|||
{{with .Data}}
|
||||
{{template "header.tmpl" (WithContext .CommonData $.Ctx)}}
|
||||
<div class="page-title"> Lists </div>
|
||||
|
||||
{{range .Lists}}
|
||||
<div>
|
||||
<a href="/timeline/list?list={{.ID}}"> {{.Title}} timeline </a>
|
||||
-
|
||||
<form class="d-inline" action="/list/{{.ID}}" method="GET">
|
||||
<button type="submit" class="btn-link"> edit </button>
|
||||
</form>
|
||||
-
|
||||
<form class="d-inline" action="/list/{{.ID}}/remove" method="POST">
|
||||
<input type="hidden" name="csrf_token" value="{{$.Ctx.CSRFToken}}">
|
||||
<input type="hidden" name="referrer" value="{{$.Ctx.Referrer}}">
|
||||
<button type="submit" class="btn-link"> delete </button>
|
||||
</form>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="no-data-found">No data found</div>
|
||||
{{end}}
|
||||
|
||||
<div class="page-title"> Add list </div>
|
||||
<form action="/list" method="POST">
|
||||
<input type="hidden" name="csrf_token" value="{{$.Ctx.CSRFToken}}">
|
||||
<input type="hidden" name="referrer" value="{{$.Ctx.Referrer}}">
|
||||
<span class="settings-form-field">
|
||||
<label for="title"> Title </label>
|
||||
<input id="title" name="title" required>
|
||||
</span>
|
||||
<button type="submit"> Add </button>
|
||||
</form>
|
||||
|
||||
{{template "footer.tmpl"}}
|
||||
{{end}}
|
|
@ -8,7 +8,7 @@
|
|||
</div>
|
||||
<div class="user-info-details-container">
|
||||
<div class="user-info-details-name">
|
||||
<bdi class="status-dname"> {{EmojiFilter (html .User.DisplayName) .User.Emojis}} </bdi>
|
||||
<bdi class="status-dname"> {{EmojiFilter (HTML .User.DisplayName) .User.Emojis | Raw}} </bdi>
|
||||
<a class="nav-link" href="/user/{{.User.ID}}" accesskey="0" title="User profile (0)">
|
||||
<span class="status-uname"> @{{.User.Acct}} </span>
|
||||
</a>
|
||||
|
@ -17,16 +17,17 @@
|
|||
<a class="nav-link" href="/timeline/home" accesskey="1" title="Home timeline (1)">home</a>
|
||||
<a class="nav-link" href="/timeline/direct" accesskey="2" title="Direct timeline (2)">direct</a>
|
||||
<a class="nav-link" href="/timeline/local" accesskey="3" title="Local timeline (3)">local</a>
|
||||
<a class="nav-link" href="/timeline/twkn" accesskey="5" title="The Whole Known Netwwork (4)">twkn</a>
|
||||
<a class="nav-link" href="/timeline/remote" accesskey="4" title="Remote timeline (5)">remote</a>
|
||||
<a class="nav-link" href="/timeline/twkn" accesskey="4" title="The Whole Known Netwwork (4)">twkn</a>
|
||||
<a class="nav-link" href="/timeline/remote" accesskey="5" title="Remote timeline (5)">remote</a>
|
||||
<a class="nav-link" href="/search" accesskey="6" title="Search (6)">search</a>
|
||||
</div>
|
||||
<div>
|
||||
<a class="nav-link" href="/settings" target="_top" accesskey="7" title="Settings (7)">settings</a>
|
||||
<a class="nav-link" href="/lists" accesskey="7" title="Lists (7)">lists</a>
|
||||
<a class="nav-link" href="/settings" target="_top" accesskey="8" title="Settings (8)">settings</a>
|
||||
<form class="signout" action="/signout" method="post" target="_top">
|
||||
<input type="hidden" name="csrf_token" value="{{$.Ctx.CSRFToken}}">
|
||||
<input type="hidden" name="referrer" value="{{$.Ctx.Referrer}}">
|
||||
<input type="submit" value="signout" class="btn-link nav-link" accesskey="8" title="Signout (8)">
|
||||
<input type="submit" value="signout" class="btn-link nav-link" title="Signout">
|
||||
</form>
|
||||
<a class="nav-link" href="/about" accesskey="9" title="About (9)">about</a>
|
||||
</div>
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
</div>
|
||||
<div class="notification-follow">
|
||||
<div class="notification-info-text">
|
||||
<bdi class="status-dname"> {{EmojiFilter (html .Account.DisplayName) .Account.Emojis}} </bdi>
|
||||
<bdi class="status-dname"> {{EmojiFilter (HTML .Account.DisplayName) .Account.Emojis | Raw}} </bdi>
|
||||
<span class="notification-text"> followed you -
|
||||
<time datetime="{{FormatTimeRFC3339 .CreatedAt}}" title="{{FormatTimeRFC822 .CreatedAt}}">{{TimeSince .CreatedAt}}</time>
|
||||
</span>
|
||||
|
@ -48,7 +48,7 @@
|
|||
</div>
|
||||
<div class="notification-follow">
|
||||
<div class="notification-info-text">
|
||||
<bdi class="status-dname"> {{EmojiFilter (html .Account.DisplayName) .Account.Emojis}} </bdi>
|
||||
<bdi class="status-dname"> {{EmojiFilter (HTML .Account.DisplayName) .Account.Emojis | Raw}} </bdi>
|
||||
<span class="notification-text"> wants to follow you -
|
||||
<time datetime="{{FormatTimeRFC3339 .CreatedAt}}" title="{{FormatTimeRFC822 .CreatedAt}}">{{TimeSince .CreatedAt}}</time>
|
||||
</span>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
{{if .ReplyContext}}
|
||||
<input type="hidden" name="reply_to_id" value="{{.ReplyContext.InReplyToID}}" />
|
||||
<input type="hidden" name="quickreply" value="{{.ReplyContext.QuickReply}}" />
|
||||
<label for="post-content" class="post-form-title"> Reply to {{.ReplyContext.InReplyToName}} </label>
|
||||
<label for="post-content" class="post-form-title"> Reply to @{{.ReplyContext.InReplyToName}} </label>
|
||||
{{else}}
|
||||
<label for="post-content" class="post-form-title"> New post </label>
|
||||
{{end}}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
</div>
|
||||
<div class="user-list-name">
|
||||
<div>
|
||||
<div class="status-dname"> {{EmojiFilter (html .DisplayName) .Emojis}} </div>
|
||||
<div class="status-dname"> {{EmojiFilter (HTML .DisplayName) .Emojis | Raw}} </div>
|
||||
<a class="img-link" href="/user/{{.ID}}">
|
||||
<div class="status-uname"> @{{.Acct}} </div>
|
||||
</a>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<form class="search-form" action="/search" method="GET">
|
||||
<span class="post-form-field">
|
||||
<label for="query"> Query </label>
|
||||
<input id="query" name="q" value="{{.Q | html}}">
|
||||
<input id="query" name="q" value="{{.Q}}">
|
||||
</span>
|
||||
<span class="post-form-field">
|
||||
<label for="type"> Type </label>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<a class="img-link" href="/user/{{.Account.ID}}">
|
||||
<img class="status-profile-img" src="{{.Account.Avatar}}" title="@{{.Account.Acct}}" alt="avatar" height="24" />
|
||||
</a>
|
||||
<bdi class="status-dname"> {{EmojiFilter (html .Account.DisplayName) .Account.Emojis}} </bdi>
|
||||
<bdi class="status-dname"> {{EmojiFilter (HTML .Account.DisplayName) .Account.Emojis | Raw}} </bdi>
|
||||
<a href="/user/{{.Account.ID}}">
|
||||
<span class="status-uname"> @{{.Account.Acct}} </span>
|
||||
</a>
|
||||
|
@ -23,7 +23,7 @@
|
|||
</div>
|
||||
<div class="status">
|
||||
<div class="status-name">
|
||||
<bdi class="status-dname"> {{EmojiFilter (html .Account.DisplayName) .Account.Emojis}} </bdi>
|
||||
<bdi class="status-dname"> {{EmojiFilter (HTML .Account.DisplayName) .Account.Emojis | Raw}} </bdi>
|
||||
<a href="/user/{{.Account.ID}}">
|
||||
<span class="status-uname"> @{{.Account.Acct}} </span>
|
||||
</a>
|
||||
|
@ -91,7 +91,10 @@
|
|||
{{end}}
|
||||
</div>
|
||||
{{if (or .Content .SpoilerText)}}
|
||||
<div class="status-content"> {{StatusContentFilter (html .SpoilerText) .Content .Emojis .Mentions}} </div>
|
||||
<div class="status-content">
|
||||
{{if .SpoilerText}}{{EmojiFilter (HTML .SpoilerText) .Emojis | Raw}}<br/>{{end}}
|
||||
{{StatusContentFilter .Content .Emojis .Mentions | Raw}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .MediaAttachments}}
|
||||
<div class="status-media-container">
|
||||
|
@ -100,7 +103,7 @@
|
|||
{{if eq .Type "image"}}
|
||||
{{if $.Ctx.HideAttachments}}
|
||||
<a href="{{.URL}}" target="_blank">
|
||||
{{if .Description}}[{{.Description}}]{{else}}[image]{{end}}
|
||||
[image{{if $s.Sensitive}}/nsfw{{end}}{{if .Description}}: {{.Description}}{{end}}]
|
||||
</a>
|
||||
{{else}}
|
||||
<a class="img-link" href="{{.URL}}" target="_blank" title="{{.Description}}">
|
||||
|
@ -114,7 +117,7 @@
|
|||
{{else if eq .Type "audio"}}
|
||||
{{if $.Ctx.HideAttachments}}
|
||||
<a href="{{.URL}}" target="_blank">
|
||||
{{if .Description}}[{{.Description}}]{{else}}[audio]{{end}}
|
||||
[audio{{if $s.Sensitive}}/nsfw{{end}}{{if .Description}}: {{.Description}}{{end}}]
|
||||
</a>
|
||||
{{else}}
|
||||
<audio class="status-audio" controls title="{{.Description}}">
|
||||
|
@ -126,7 +129,7 @@
|
|||
{{else if eq .Type "video"}}
|
||||
{{if $.Ctx.HideAttachments}}
|
||||
<a href="{{.URL}}" target="_blank">
|
||||
{{if .Description}}[{{.Description}}]{{else}}[video]{{end}}
|
||||
[video{{if $s.Sensitive}}/nsfw{{end}}{{if .Description}}: {{.Description}}{{end}}]
|
||||
</a>
|
||||
{{else}}
|
||||
<div class="status-video-container" title="{{.Description}}">
|
||||
|
@ -142,7 +145,7 @@
|
|||
|
||||
{{else}}
|
||||
<a href="{{.URL}}" target="_blank">
|
||||
{{if .Description}}[{{.Description}}]{{else}}[attachment]{{end}}
|
||||
[attachment{{if $s.Sensitive}}/nsfw{{end}}{{if .Description}}: {{.Description}}{{end}}]
|
||||
</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
@ -156,12 +159,12 @@
|
|||
{{range $i, $o := .Poll.Options}}
|
||||
<div class="poll-option">
|
||||
{{if (or $s.Poll.Expired $s.Poll.Voted)}}
|
||||
<div> {{EmojiFilter (html $o.Title) $s.Emojis}} - {{$o.VotesCount}} votes </div>
|
||||
<div> {{EmojiFilter (HTML $o.Title) $s.Emojis | Raw}} - {{$o.VotesCount}} votes </div>
|
||||
{{else}}
|
||||
<input type="{{if $s.Poll.Multiple}}checkbox{{else}}radio{{end}}" name="choices"
|
||||
id="poll-{{$s.ID}}-{{$i}}" value="{{$i}}">
|
||||
<label for="poll-{{$s.ID}}-{{$i}}">
|
||||
{{EmojiFilter (html $o.Title) $s.Emojis}}
|
||||
{{EmojiFilter (HTML $o.Title) $s.Emojis | Raw}}
|
||||
</label>
|
||||
{{end}}
|
||||
</div>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
</div>
|
||||
<div class="user-profile-details-container">
|
||||
<div>
|
||||
<bdi class="status-dname"> {{EmojiFilter (html .User.DisplayName) .User.Emojis}} </bdi>
|
||||
<bdi class="status-dname"> {{EmojiFilter (HTML .User.DisplayName) .User.Emojis | Raw}} </bdi>
|
||||
<span class="status-uname"> @{{.User.Acct}} </span>
|
||||
<a class="remote-link" href="{{.User.URL}}" target="_blank" title="remote profile">
|
||||
source
|
||||
|
@ -20,6 +20,7 @@
|
|||
{{if not .IsCurrent}}
|
||||
<div>
|
||||
<span> {{if .User.Pleroma.Relationship.FollowedBy}} follows you - {{end}} </span>
|
||||
{{if .User.Pleroma.Relationship.BlockedBy}} blocks you - {{end}}
|
||||
{{if .User.Pleroma.Relationship.Following}}
|
||||
<form class="d-inline" action="/unfollow/{{.User.ID}}" method="post">
|
||||
<input type="hidden" name="csrf_token" value="{{$.Ctx.CSRFToken}}">
|
||||
|
@ -129,11 +130,15 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="user-profile-decription">
|
||||
{{EmojiFilter .User.Note .User.Emojis}}
|
||||
{{EmojiFilter .User.Note .User.Emojis | Raw}}
|
||||
</div>
|
||||
{{if .User.Fields}}{{range .User.Fields}}
|
||||
<div>{{.Name}} - {{.Value}}</div>
|
||||
{{end}}{{end}}
|
||||
{{if .User.Fields}}
|
||||
<div class="user-fields">
|
||||
{{range .User.Fields}}
|
||||
<div>{{.Name}} - {{.Value | Raw}}</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,19 +1,7 @@
|
|||
{{with .Data}}
|
||||
<div>
|
||||
{{range .}}
|
||||
<div class="user-list-item">
|
||||
<div class="user-list-profile-img">
|
||||
<a class="img-link" href="/user/{{.ID}}">
|
||||
<img class="status-profile-img" src="{{.Avatar}}" title="@{{.Acct}}" alt="avatar" height="48" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="user-list-name">
|
||||
<div class="status-dname"> {{EmojiFilter (html .DisplayName) .Emojis}} </div>
|
||||
<a class="img-link" href="/user/{{.ID}}">
|
||||
<div class="status-uname"> @{{.Acct}} </div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{{template "userlistitem.tmpl" (WithContext . $.Ctx)}}
|
||||
{{else}}
|
||||
<div class="no-data-found">No data found</div>
|
||||
{{end}}
|
||||
|
|
15
templates/userlistitem.tmpl
Normal file
15
templates/userlistitem.tmpl
Normal file
|
@ -0,0 +1,15 @@
|
|||
{{with .Data}}
|
||||
<div class="user-list-item">
|
||||
<div class="user-list-profile-img">
|
||||
<a class="img-link" href="/user/{{.ID}}">
|
||||
<img class="status-profile-img" src="{{.Avatar}}" title="@{{.Acct}}" alt="avatar" height="48" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="user-list-name">
|
||||
<div class="status-dname"> {{EmojiFilter (HTML .DisplayName) .Emojis | Raw}} </div>
|
||||
<a class="img-link" href="/user/{{.ID}}">
|
||||
<div class="status-uname"> @{{.Acct}} </div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
|
@ -1,11 +1,11 @@
|
|||
{{with .Data}}
|
||||
{{template "header.tmpl" (WithContext .CommonData $.Ctx)}}
|
||||
<div class="page-title"> Search {{EmojiFilter (html .User.DisplayName) .User.Emojis}}'s statuses </div>
|
||||
<div class="page-title"> Search {{EmojiFilter (HTML .User.DisplayName) .User.Emojis | Raw}}'s statuses </div>
|
||||
|
||||
<form class="search-form" action="/usersearch/{{.User.ID}}" method="GET">
|
||||
<span class="post-form-field>
|
||||
<span class="post-form-field">
|
||||
<label for="query"> Query </label>
|
||||
<input id="query" name="q" value="{{.Q | html}}">
|
||||
<input id="query" name="q" value="{{.Q}}">
|
||||
</span>
|
||||
<button type="submit"> Search </button>
|
||||
</form>
|
||||
|
|
Loading…
Reference in a new issue