mirror of
https://git.phreedom.club/localhost_frssoft/bloat.git
synced 2024-11-29 07:01:28 +00:00
Compare commits
3 commits
6a233f6cb2
...
4c0c1c40a8
Author | SHA1 | Date | |
---|---|---|---|
localhost_frssoft | 4c0c1c40a8 | ||
localhost_frssoft | 8dcd6cfdd7 | ||
localhost_frssoft | 14898fea07 |
4
README
4
README
|
@ -12,6 +12,10 @@ Changes (localhost_custom fork):
|
||||||
- visible edited post time
|
- visible edited post time
|
||||||
- visible quoted post (status in status)
|
- visible quoted post (status in status)
|
||||||
- visible profile banner in spoiler
|
- visible profile banner in spoiler
|
||||||
|
- add schedule status
|
||||||
|
- add language input form
|
||||||
|
- add expiry status
|
||||||
|
- add support for send poll (hardcoded to 20 options)
|
||||||
- hide boosts in spoiler
|
- hide boosts in spoiler
|
||||||
- hide NSFW content and attachments in spoiler
|
- hide NSFW content and attachments in spoiler
|
||||||
- some micro visual changes
|
- some micro visual changes
|
||||||
|
|
|
@ -213,6 +213,15 @@ type Toot struct {
|
||||||
Language string `json:"language"`
|
Language string `json:"language"`
|
||||||
ExpiresIn int `json:"expires_in"`
|
ExpiresIn int `json:"expires_in"`
|
||||||
ScheduledAt string `json:"scheduled_at"`
|
ScheduledAt string `json:"scheduled_at"`
|
||||||
|
Poll TootPoll `json:"poll"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TootPoll is struct to poll in post status.
|
||||||
|
type TootPoll struct {
|
||||||
|
ExpiresIn int `json:"expires_in"`
|
||||||
|
HideTotals bool `json:"hide_totals"`
|
||||||
|
Multiple bool `json:"multiple"`
|
||||||
|
Options []string `json:"options"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mention hold information for mention.
|
// Mention hold information for mention.
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
type StatusPleroma struct {
|
type StatusPleroma struct {
|
||||||
|
@ -367,7 +368,19 @@ func (c *Client) PostStatus(ctx context.Context, toot *Toot) (*Status, error) {
|
||||||
if toot.ScheduledAt != "" {
|
if toot.ScheduledAt != "" {
|
||||||
params.Set("scheduled_at", toot.ScheduledAt)
|
params.Set("scheduled_at", toot.ScheduledAt)
|
||||||
}
|
}
|
||||||
|
if len(toot.Poll.Options) > 2 {
|
||||||
|
for _, option := range toot.Poll.Options {
|
||||||
|
params.Add("poll[options][]", string(option))
|
||||||
|
}
|
||||||
|
params.Set("poll[expires_in]", strconv.Itoa(toot.Poll.ExpiresIn))
|
||||||
|
if toot.Poll.Multiple {
|
||||||
|
params.Set("poll[multiple]", "true")
|
||||||
|
}
|
||||||
|
if toot.Poll.HideTotals {
|
||||||
|
params.Set("poll[hide_totals]", "true")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
var status Status
|
var status Status
|
||||||
if toot.Edit != "" {
|
if toot.Edit != "" {
|
||||||
err := c.doAPI(ctx, http.MethodPut, fmt.Sprintf("/api/v1/statuses/%s", toot.Edit), params, &status, nil)
|
err := c.doAPI(ctx, http.MethodPut, fmt.Sprintf("/api/v1/statuses/%s", toot.Edit), params, &status, nil)
|
||||||
|
|
|
@ -19,4 +19,5 @@ type ReplyContext struct {
|
||||||
ReplySpoiler string
|
ReplySpoiler string
|
||||||
ReplyContent string
|
ReplyContent string
|
||||||
ForceVisibility bool
|
ForceVisibility bool
|
||||||
|
ReplyLanguage string
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,11 +109,6 @@ type UserData struct {
|
||||||
NextLink string
|
NextLink string
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserEditData struct {
|
|
||||||
*CommonData
|
|
||||||
User *mastodon.Account
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserSearchData struct {
|
type UserSearchData struct {
|
||||||
*CommonData
|
*CommonData
|
||||||
User *mastodon.Account
|
User *mastodon.Account
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"bloat/mastodon"
|
"bloat/mastodon"
|
||||||
)
|
)
|
||||||
|
@ -34,7 +35,6 @@ const (
|
||||||
RetweetedByPage = "retweetedby.tmpl"
|
RetweetedByPage = "retweetedby.tmpl"
|
||||||
SearchPage = "search.tmpl"
|
SearchPage = "search.tmpl"
|
||||||
SettingsPage = "settings.tmpl"
|
SettingsPage = "settings.tmpl"
|
||||||
UserEditPage = "useredit.tmpl"
|
|
||||||
FiltersPage = "filters.tmpl"
|
FiltersPage = "filters.tmpl"
|
||||||
ProfilePage = "profile.tmpl"
|
ProfilePage = "profile.tmpl"
|
||||||
MutePage = "mute.tmpl"
|
MutePage = "mute.tmpl"
|
||||||
|
@ -68,6 +68,14 @@ func emojiFilter(content string, emojis []mastodon.Emoji) string {
|
||||||
return strings.NewReplacer(replacements...).Replace(content)
|
return strings.NewReplacer(replacements...).Replace(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func generatePollOptions() string {
|
||||||
|
var pollbuilder string
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
pollbuilder = pollbuilder + `<div><input id="` + fmt.Sprintf("poll-option-%d", i) + `" name="` + fmt.Sprintf("poll-option-%d", i) + `"></div>`
|
||||||
|
}
|
||||||
|
return pollbuilder
|
||||||
|
}
|
||||||
|
|
||||||
var quoteRE = regexp.MustCompile("(?mU)(^|> *|\n)(>.*)(<br|$)")
|
var quoteRE = regexp.MustCompile("(?mU)(^|> *|\n)(>.*)(<br|$)")
|
||||||
|
|
||||||
func statusContentFilter(content string, emojis []mastodon.Emoji, mentions []mastodon.Mention) string {
|
func statusContentFilter(content string, emojis []mastodon.Emoji, mentions []mastodon.Mention) string {
|
||||||
|
@ -159,6 +167,7 @@ func NewRenderer(templateGlobPattern string) (r *renderer, err error) {
|
||||||
t, err = t.Funcs(template.FuncMap{
|
t, err = t.Funcs(template.FuncMap{
|
||||||
"EmojiFilter": emojiFilter,
|
"EmojiFilter": emojiFilter,
|
||||||
"Allowed_emoji_page": allowed_emoji_page,
|
"Allowed_emoji_page": allowed_emoji_page,
|
||||||
|
"GeneratePollOptions": generatePollOptions,
|
||||||
"StatusContentFilter": statusContentFilter,
|
"StatusContentFilter": statusContentFilter,
|
||||||
"DisplayInteractionCount": displayInteractionCount,
|
"DisplayInteractionCount": displayInteractionCount,
|
||||||
"TimeSince": timeSince,
|
"TimeSince": timeSince,
|
||||||
|
|
|
@ -362,6 +362,8 @@ func (s *service) ThreadPage(c *client, id string, reply bool) (err error) {
|
||||||
visibility = c.s.Settings.DefaultVisibility
|
visibility = c.s.Settings.DefaultVisibility
|
||||||
}
|
}
|
||||||
|
|
||||||
|
replyLanguage := status.Language
|
||||||
|
|
||||||
pctx = model.PostContext{
|
pctx = model.PostContext{
|
||||||
DefaultVisibility: visibility,
|
DefaultVisibility: visibility,
|
||||||
DefaultFormat: c.s.Settings.DefaultFormat,
|
DefaultFormat: c.s.Settings.DefaultFormat,
|
||||||
|
@ -372,6 +374,7 @@ func (s *service) ThreadPage(c *client, id string, reply bool) (err error) {
|
||||||
ReplySpoiler: spoilerText,
|
ReplySpoiler: spoilerText,
|
||||||
ReplyContent: content,
|
ReplyContent: content,
|
||||||
ForceVisibility: isDirect,
|
ForceVisibility: isDirect,
|
||||||
|
ReplyLanguage: replyLanguage,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -836,19 +839,6 @@ func (s *service) SearchPage(c *client,
|
||||||
return s.renderer.Render(c.rctx, c.w, renderer.SearchPage, data)
|
return s.renderer.Render(c.rctx, c.w, renderer.SearchPage, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) UserEditPage(c *client) (err error) {
|
|
||||||
cdata := s.cdata(c, "useredit", 0, 0, "")
|
|
||||||
u, err := c.GetAccountCurrentUser(c.ctx)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
data := &renderer.UserEditData{
|
|
||||||
CommonData: cdata,
|
|
||||||
User: u,
|
|
||||||
}
|
|
||||||
return s.renderer.Render(c.rctx, c.w, renderer.UserEditPage, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) SettingsPage(c *client) (err error) {
|
func (s *service) SettingsPage(c *client) (err error) {
|
||||||
cdata := s.cdata(c, "settings", 0, 0, "")
|
cdata := s.cdata(c, "settings", 0, 0, "")
|
||||||
data := &renderer.SettingsData{
|
data := &renderer.SettingsData{
|
||||||
|
@ -988,7 +978,8 @@ func (s *service) Signout(c *client) (err error) {
|
||||||
|
|
||||||
func (s *service) Post(c *client, content string, replyToID string,
|
func (s *service) Post(c *client, content string, replyToID string,
|
||||||
format string, visibility string, isNSFW bool, spoilerText string,
|
format string, visibility string, isNSFW bool, spoilerText string,
|
||||||
files []*multipart.FileHeader, edit string, language string, expiresIn int, scheduledAt string) (id string, err error) {
|
files []*multipart.FileHeader, edit string, language string, expiresIn int, scheduledAt string,
|
||||||
|
pollOptions []string, pollExpiresIn int, pollHideTotals bool, pollMultiple bool) (id string, err error) {
|
||||||
|
|
||||||
var mediaIDs []string
|
var mediaIDs []string
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
|
@ -1001,6 +992,13 @@ func (s *service) Post(c *client, content string, replyToID string,
|
||||||
|
|
||||||
expiresIn = expiresIn * 3600
|
expiresIn = expiresIn * 3600
|
||||||
|
|
||||||
|
pollTweet := mastodon.TootPoll{
|
||||||
|
ExpiresIn: pollExpiresIn,
|
||||||
|
Options: pollOptions,
|
||||||
|
Multiple: pollMultiple,
|
||||||
|
HideTotals: pollHideTotals,
|
||||||
|
}
|
||||||
|
|
||||||
tweet := &mastodon.Toot{
|
tweet := &mastodon.Toot{
|
||||||
SpoilerText: spoilerText,
|
SpoilerText: spoilerText,
|
||||||
Status: content,
|
Status: content,
|
||||||
|
@ -1013,6 +1011,7 @@ func (s *service) Post(c *client, content string, replyToID string,
|
||||||
Language: language,
|
Language: language,
|
||||||
ExpiresIn: expiresIn, // pleroma compatible
|
ExpiresIn: expiresIn, // pleroma compatible
|
||||||
ScheduledAt: scheduledAt,
|
ScheduledAt: scheduledAt,
|
||||||
|
Poll: pollTweet,
|
||||||
}
|
}
|
||||||
|
|
||||||
st, err := c.PostStatus(c.ctx, tweet)
|
st, err := c.PostStatus(c.ctx, tweet)
|
||||||
|
|
|
@ -327,8 +327,22 @@ func NewHandler(s *service, verbose bool, staticDir string) http.Handler {
|
||||||
}
|
}
|
||||||
scheduledAt = string(scheduled.UTC().Format(time.RFC3339))
|
scheduledAt = string(scheduled.UTC().Format(time.RFC3339))
|
||||||
}
|
}
|
||||||
|
var pollOptions []string
|
||||||
|
for i := 0; i < 16; i++ {
|
||||||
|
v := c.r.FormValue(fmt.Sprintf("poll-option-%d", i))
|
||||||
|
if len(v) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pollOptions = append(pollOptions, v)
|
||||||
|
}
|
||||||
|
pollExpiresIn, err := strconv.Atoi(c.r.FormValue("poll-expires-in"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pollHideTotals := c.r.FormValue("poll-hide-totals") == "true"
|
||||||
|
pollMultiple := c.r.FormValue("poll-is-multiple") == "true"
|
||||||
|
|
||||||
id, err := s.Post(c, content, replyToID, format, visibility, isNSFW, spoilerText, files, edit, language, expiresIn, scheduledAt)
|
id, err := s.Post(c, content, replyToID, format, visibility, isNSFW, spoilerText, files, edit, language, expiresIn, scheduledAt, pollOptions, pollExpiresIn, pollHideTotals, pollMultiple)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,9 +48,24 @@
|
||||||
<button type="submit" accesskey="P" title="Post (P)"> Post </button>
|
<button type="submit" accesskey="P" title="Post (P)"> Post </button>
|
||||||
<button type="reset" title="Reset"> Reset </button>
|
<button type="reset" title="Reset"> Reset </button>
|
||||||
<input id="edit-status-id" name="edit-status-id" placeholder="Input Status ID for edit" title="Edit ID">
|
<input id="edit-status-id" name="edit-status-id" placeholder="Input Status ID for edit" title="Edit ID">
|
||||||
<input id="lang-code" name="lang-code" placeholder="lang" title="Post language (ISO 639) [en, ru, etc..] Default: none" size="4">
|
<input id="lang-code" name="lang-code" placeholder="lang" title="Post language (ISO 639) [en, ru, etc..] Default: none" size="4" value="{{if .ReplyContext}}{{.ReplyContext.ReplyLanguage}}{{end}}">
|
||||||
<input type="number" id="expires-in" name="expires-in" title="Post autodeleted after hour(s)" min="0" value="0" size="4">
|
<input type="number" id="expires-in" name="expires-in" title="Post autodeleted after hour(s)" min="0" value="0" size="4">
|
||||||
<input type="datetime-local" id="scheduled" name="scheduled" step=300 title="Schedule your status (timezone UTC+0)">
|
<input type="datetime-local" id="scheduled" name="scheduled" step=300 title="Schedule your status (timezone UTC+0)">
|
||||||
|
<details><summary>Poll</summary>
|
||||||
|
<div>
|
||||||
|
<input type="number" id="poll-expires-in" name="poll-expires-in" min="1" value="300">
|
||||||
|
<label for="poll-expires-in"> Expires in (secs) </label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="checkbox" id="poll-hide-totals" name="poll-hide-totals" value="true">
|
||||||
|
<label for="poll-hide-totals"> Hide vote counts? </label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="checkbox" id="poll-is-multiple" name="poll-is-multiple" value="true">
|
||||||
|
<label for="poll-is_multiple "> Allow multiple choice? </label>
|
||||||
|
</div>
|
||||||
|
<div>{{GeneratePollOptions | Raw}}</div>
|
||||||
|
</details>
|
||||||
</form>
|
</form>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
|
|
@ -114,7 +114,6 @@
|
||||||
- <a href="/user/{{.User.ID}}/mutes"> mutes </a>
|
- <a href="/user/{{.User.ID}}/mutes"> mutes </a>
|
||||||
- <a href="/user/{{.User.ID}}/blocks"> blocks </a>
|
- <a href="/user/{{.User.ID}}/blocks"> blocks </a>
|
||||||
{{if .User.Locked}}- <a href="/user/{{.User.ID}}/requests"> requests </a>{{end}}
|
{{if .User.Locked}}- <a href="/user/{{.User.ID}}/requests"> requests </a>{{end}}
|
||||||
- <a href="/useredit"> edit </a>
|
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
{{with .Data}}
|
|
||||||
{{template "header.tmpl" (WithContext .CommonData $.Ctx)}}
|
|
||||||
<div class="page-title"> User settings </div>
|
|
||||||
|
|
||||||
<form id="user-settings-form" action="/useredit" method="POST">
|
|
||||||
<input type="hidden" name="csrf_token" value="{{$.Ctx.CSRFToken}}">
|
|
||||||
<input type="hidden" name="referrer" value="{{$.Ctx.Referrer}}">
|
|
||||||
<input type="hidden" name="id" value="{{.User.ID}}">
|
|
||||||
<div class="settings-form-field">
|
|
||||||
<input id="display-name" name="display-name" value="{{HTML .User.DisplayName}}">
|
|
||||||
<label for="display-name"> Your display name </label>
|
|
||||||
</div>
|
|
||||||
<div class="settings-form-field">
|
|
||||||
<label for="note"> Your bio: </label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<textarea id="note" name="note" cols="80" rows="8"></textarea>
|
|
||||||
<div>
|
|
||||||
<details><summary>Current bio for copypaste</summary>{{.User.Note | Raw}}</details>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="settings-form-field" title="Whether manual approval of follow requests is required.">
|
|
||||||
<input id="locked" name="locked" type="checkbox" value="true" {{if .User.Locked}}checked{{end}}>
|
|
||||||
<label for="locked"> Locked user? </label>
|
|
||||||
</div>
|
|
||||||
<div class="settings-form-field" title="Whether manual approval of follow requests is required.">
|
|
||||||
<input id="bot" name="bot" type="checkbox" value="true" {{if .User.Bot}}checked{{end}}>
|
|
||||||
<label for="bot"> User is bot? </label>
|
|
||||||
</div>
|
|
||||||
{{if .User.MastodonAccount}}
|
|
||||||
{{else}}
|
|
||||||
<div class="page-title"> Pleroma settings </div>
|
|
||||||
|
|
||||||
<div class="settings-form-field">
|
|
||||||
<input id="accepts-chat-messages" name="hide-attachments" type="checkbox" value="true" {{if .User.Pleroma.AcceptsChatMessages}}checked{{end}}>
|
|
||||||
<label for="accepts-chat-messages"> Allow receive chat messages (bloat not support chats feature) </label>
|
|
||||||
</div>
|
|
||||||
<div class="settings-form-field">
|
|
||||||
<input id="hide-favourites" name="hide-favourites" type="checkbox" value="true" {{if .User.Pleroma.HideFavourites}}checked{{end}}>
|
|
||||||
<label for="hide-favourites"> User's favorites timeline will be hidden </label>
|
|
||||||
</div>
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
<button type="submit"> Save </button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{{template "footer.tmpl"}}
|
|
||||||
{{end}}
|
|
Loading…
Reference in a new issue