mirror of
https://gitea.phreedom.club/localhost_frssoft/bloat.git
synced 2024-11-23 21:29:22 +02:00
Compare commits
20 Commits
44f8a72a76
...
215930e3b9
Author | SHA1 | Date | |
---|---|---|---|
localhost_frssoft | 215930e3b9 | ||
localhost_frssoft | baf388cb42 | ||
localhost_frssoft | 5d4aab1d58 | ||
localhost_frssoft | 3490890c44 | ||
localhost_frssoft | 11f2b2fd08 | ||
localhost_frssoft | 2bbcce5133 | ||
localhost_frssoft | 62f653fc28 | ||
localhost_frssoft | 679348d1f7 | ||
localhost_frssoft | 18c37c0c2f | ||
localhost_frssoft | fc43de6012 | ||
localhost_frssoft | 0f33427d5b | ||
localhost_frssoft | eedd9d2c5c | ||
localhost_frssoft | 34940a65df | ||
localhost_frssoft | cfddec036c | ||
localhost_frssoft | 58a2a1748e | ||
localhost_frssoft | 4c0c1c40a8 | ||
localhost_frssoft | 8dcd6cfdd7 | ||
localhost_frssoft | 14898fea07 | ||
localhost_frssoft | 6a233f6cb2 | ||
localhost_frssoft | 4cd8fb2ddb |
10
README
10
README
|
@ -9,9 +9,15 @@ Changes (localhost_custom fork):
|
||||||
- Is personal fork, no recommended for public use (use as local frontend)
|
- Is personal fork, no recommended for public use (use as local frontend)
|
||||||
- Add reactions for pleroma (includes custom emojis input)
|
- Add reactions for pleroma (includes custom emojis input)
|
||||||
- tag timeline
|
- tag timeline
|
||||||
|
- true remote timeline
|
||||||
- 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 media description submit (very woozy way, but it works as possible)
|
||||||
|
- 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
|
||||||
|
@ -49,3 +55,7 @@ this software. If not, see http://creativecommons.org/publicdomain/zero/1.0/.
|
||||||
|
|
||||||
|
|
||||||
[1] https://pleroma.social
|
[1] https://pleroma.social
|
||||||
|
|
||||||
|
Related projects:
|
||||||
|
https://codeberg.org/SimpleWeb/SimpleerTube (archived, but it works)
|
||||||
|
https://codeberg.org/ManeraKai/simplytranslate
|
||||||
|
|
|
@ -64,8 +64,16 @@ type MetaData struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type FederationInfo struct {
|
type FederationInfo struct {
|
||||||
Enabled bool `json:enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
MrfPolicies []string `json:mrf_policies"`
|
MrfPolicies []string `json:"mrf_policies"`
|
||||||
|
MrfSimple MRFSimple `json:"mrf_simple"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MRFSimple struct {
|
||||||
|
Reject []string `json:"reject"`
|
||||||
|
MediaNsfw []string `json:"media_nsfw"`
|
||||||
|
FederatedTimelineRemoval []string `json:"federated_timeline_removal"`
|
||||||
|
RejectDeletes []string `json:"reject_deletes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -210,6 +210,18 @@ type Toot struct {
|
||||||
SpoilerText string `json:"spoiler_text"`
|
SpoilerText string `json:"spoiler_text"`
|
||||||
Visibility string `json:"visibility"`
|
Visibility string `json:"visibility"`
|
||||||
ContentType string `json:"content_type"`
|
ContentType string `json:"content_type"`
|
||||||
|
Language string `json:"language"`
|
||||||
|
ExpiresIn int `json:"expires_in"`
|
||||||
|
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.
|
||||||
|
@ -244,6 +256,11 @@ type Attachment struct {
|
||||||
TextURL string `json:"text_url"`
|
TextURL string `json:"text_url"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Meta AttachmentMeta `json:"meta"`
|
Meta AttachmentMeta `json:"meta"`
|
||||||
|
|
||||||
|
//Misskey fields
|
||||||
|
Comment string `json:"comment"`
|
||||||
|
ThumbnailUrl string `json:"thumbnailUrl"`
|
||||||
|
Sensitive bool `json:"isSensitive"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AttachmentMeta holds information for attachment metadata.
|
// AttachmentMeta holds information for attachment metadata.
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
package mastodon
|
||||||
|
|
||||||
|
type MisskeyStatus struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
User AccountMisskey `json:"user"`
|
||||||
|
Renote *MisskeyStatus `json:"renote"`
|
||||||
|
CreatedAt CreatedAt `json:"createdAt"`
|
||||||
|
Visibility string `json:"visibility"`
|
||||||
|
CW string `json:"cw"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
Files []Attachment `json:"files"`
|
||||||
|
RenoteCount int64 `json:"renoteCount"`
|
||||||
|
RepliesCount int64 `json:"repliesCount"`
|
||||||
|
Reactions map[string]int `json:"reactions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccountMisskey struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Host string `json:"host"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
AvatarURL string `json:"avatarUrl"`
|
||||||
|
IsBot bool `json:"isBot"`
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"path"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -230,47 +230,168 @@ func (c *Client) GetTimelineHome(ctx context.Context, pg *Pagination) ([]*Status
|
||||||
return statuses, nil
|
return statuses, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type RemoteTimelineInstance struct {
|
|
||||||
http.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
// TrueRemoteTimeline get public timeline from remote Mastodon API compatible instance directly
|
// TrueRemoteTimeline get public timeline from remote Mastodon API compatible instance directly
|
||||||
func (c *Client) TrueRemoteTimeline(ctx context.Context, instance string, pg *Pagination) ([]*Status, error) {
|
func (c *Client) TrueRemoteTimeline(ctx context.Context, instance string, instance_type string, pg *Pagination) ([]*Status, error) {
|
||||||
var httpclient RemoteTimelineInstance
|
|
||||||
var publicstatuses []*Status
|
var publicstatuses []*Status
|
||||||
|
var instanceParams []string
|
||||||
|
instanceParams = strings.Split(instance, ":")[1:]
|
||||||
|
instance = strings.TrimSpace(strings.Split(instance, ":")[0])
|
||||||
params := url.Values{}
|
params := url.Values{}
|
||||||
params.Set("local", "true")
|
params.Set("local", "true")
|
||||||
|
|
||||||
if pg != nil {
|
if pg != nil {
|
||||||
params = pg.setValues(params)
|
params = pg.setValues(params)
|
||||||
}
|
}
|
||||||
u, err := url.Parse("https://" + instance)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
u.Path = path.Join(u.Path, "/api/v1/timelines/public")
|
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, u.String(), strings.NewReader(params.Encode()))
|
perform := url.URL{
|
||||||
if err != nil {
|
Scheme: "https",
|
||||||
return nil, err
|
Host: instance,
|
||||||
}
|
}
|
||||||
|
var paramval []string
|
||||||
|
withFiles := "false"
|
||||||
|
withReplies := "false"
|
||||||
|
globalTimeline := false
|
||||||
|
for _, instanceParam := range instanceParams {
|
||||||
|
switch instanceParam {
|
||||||
|
case "withFiles":
|
||||||
|
withFiles = "true"
|
||||||
|
params.Set("only_media", "true")
|
||||||
|
case "withReplies":
|
||||||
|
withReplies = "true"
|
||||||
|
case "remote":
|
||||||
|
globalTimeline = true
|
||||||
|
params.Set(instanceParam, "true")
|
||||||
|
default:
|
||||||
|
paramval = strings.Split(instanceParam, "=")
|
||||||
|
if len(paramval) == 2 {
|
||||||
|
params.Set(paramval[0], paramval[1])
|
||||||
|
} else {
|
||||||
|
params.Set(instanceParam, "true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var method string
|
||||||
|
var ContentType string
|
||||||
|
var bytesAttach []byte
|
||||||
|
switch instance_type {
|
||||||
|
case "misskey":
|
||||||
|
if globalTimeline {
|
||||||
|
perform.Path = "api/notes/global-timeline"
|
||||||
|
} else {
|
||||||
|
perform.Path = "api/notes/local-timeline"
|
||||||
|
}
|
||||||
|
perform.RawQuery = ""
|
||||||
|
method = http.MethodPost
|
||||||
|
ContentType = "application/json"
|
||||||
|
bytesAttach = []byte(fmt.Sprintf(
|
||||||
|
`{"limit":20,"withRenotes":false, "withReplies": %s, "withFiles": %s}`,
|
||||||
|
withReplies, withFiles))
|
||||||
|
if pg != nil {
|
||||||
|
if pg.MaxID != "" {
|
||||||
|
bytesAttach = []byte(fmt.Sprintf(
|
||||||
|
`{"limit": %s,"withRenotes": false,"untilId":"%s", "withReplies": %s, "withFiles": %s}`,
|
||||||
|
strconv.Itoa(int(pg.Limit)), pg.MaxID, withReplies, withFiles))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if globalTimeline {
|
||||||
|
params.Set("local", "false")
|
||||||
|
}
|
||||||
|
perform.RawQuery = params.Encode()
|
||||||
|
perform.Path = "api/v1/timelines/public"
|
||||||
|
method = http.MethodGet
|
||||||
|
ContentType = "application/x-www-form-urlencoded"
|
||||||
|
bytesAttach = []byte("")
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest(method, perform.String(), bytes.NewBuffer(bytesAttach))
|
||||||
req = req.WithContext(ctx)
|
req = req.WithContext(ctx)
|
||||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
req.Header.Set("Content-Type", ContentType)
|
||||||
resp, err := httpclient.Do(req)
|
req.Header.Set("User-Agent", "Bloat")
|
||||||
fmt.Println(req)
|
client := http.Client{}
|
||||||
fmt.Println(resp)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return nil, parseAPIError("bad request", resp)
|
return nil, parseAPIError("Can't get remote timeline for " + instance + ", try select another type instance. Error" , resp)
|
||||||
}
|
}
|
||||||
|
switch instance_type {
|
||||||
err = json.NewDecoder(resp.Body).Decode(&publicstatuses)
|
case "misskey":
|
||||||
fmt.Println(resp.Body)
|
var misskeyData []MisskeyStatus
|
||||||
if err != nil {
|
err = json.NewDecoder(resp.Body).Decode(&misskeyData)
|
||||||
return nil, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, statusMisskey := range misskeyData {
|
||||||
|
var status Status
|
||||||
|
status.ID = statusMisskey.ID
|
||||||
|
if statusMisskey.Renote != nil {
|
||||||
|
// small handle for strange reblogs in misskey
|
||||||
|
// handle as quoted post because akkoma/pleroma makes same
|
||||||
|
var quote Status
|
||||||
|
quote.ID = statusMisskey.Renote.ID
|
||||||
|
quote.Content = strings.Replace(statusMisskey.Renote.Text, "\n", "<br>", -1)
|
||||||
|
status.Pleroma.Quote = "e
|
||||||
|
}
|
||||||
|
status.Account.ID = statusMisskey.User.ID
|
||||||
|
status.Account.DisplayName = statusMisskey.User.Name
|
||||||
|
if statusMisskey.User.Host != "" {
|
||||||
|
status.Account.Acct = statusMisskey.User.Username + "@" + statusMisskey.User.Host
|
||||||
|
} else {
|
||||||
|
status.Account.Acct = statusMisskey.User.Username
|
||||||
|
}
|
||||||
|
status.Account.Username = statusMisskey.User.Username
|
||||||
|
status.Account.Avatar = statusMisskey.User.AvatarURL
|
||||||
|
status.CreatedAt = statusMisskey.CreatedAt
|
||||||
|
status.Visibility = statusMisskey.Visibility
|
||||||
|
status.Content = strings.Replace(statusMisskey.Text, "\n", "<br>", -1) + "<br><br>"
|
||||||
|
for reaction, count := range statusMisskey.Reactions { // woozyface
|
||||||
|
if reaction == "❤" {
|
||||||
|
status.FavouritesCount = int64(count)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
status.Content = status.Content + "[" + reaction + strconv.Itoa(count) + "]"
|
||||||
|
}
|
||||||
|
status.MediaAttachments = statusMisskey.Files
|
||||||
|
for idx, attach := range statusMisskey.Files {
|
||||||
|
status.MediaAttachments[idx].Type = strings.Split(attach.Type, "/")[0]
|
||||||
|
status.MediaAttachments[idx].Description = strings.Replace(attach.Comment, "\n", "<br>", -1)
|
||||||
|
status.MediaAttachments[idx].PreviewURL = attach.ThumbnailUrl
|
||||||
|
status.MediaAttachments[idx].RemoteURL = attach.URL
|
||||||
|
status.MediaAttachments[idx].TextURL = attach.URL
|
||||||
|
if status.Sensitive == false {
|
||||||
|
if attach.Sensitive { // mark status as NSFW if any attachment marked as NSFW
|
||||||
|
status.Sensitive = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if statusMisskey.CW != "" {
|
||||||
|
status.Sensitive = true
|
||||||
|
status.SpoilerText = statusMisskey.CW
|
||||||
|
}
|
||||||
|
status.RepliesCount = statusMisskey.RepliesCount
|
||||||
|
status.ReblogsCount = statusMisskey.RenoteCount
|
||||||
|
status.Account.Bot = statusMisskey.User.IsBot
|
||||||
|
status.URL = "https://" + instance + "/notes/" + statusMisskey.ID
|
||||||
|
publicstatuses = append(publicstatuses, &status)
|
||||||
|
}
|
||||||
|
case "friendica":
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&publicstatuses)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, status := range publicstatuses {
|
||||||
|
status.URL = status.URI // Fix federate URL
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&publicstatuses)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return publicstatuses, nil
|
return publicstatuses, nil
|
||||||
}
|
}
|
||||||
|
@ -358,6 +479,28 @@ func (c *Client) PostStatus(ctx context.Context, toot *Toot) (*Status, error) {
|
||||||
if toot.ContentType != "" {
|
if toot.ContentType != "" {
|
||||||
params.Set("content_type", toot.ContentType)
|
params.Set("content_type", toot.ContentType)
|
||||||
}
|
}
|
||||||
|
if toot.Language != "" {
|
||||||
|
params.Set("language", toot.Language)
|
||||||
|
}
|
||||||
|
if toot.ExpiresIn >= 3600 {
|
||||||
|
params.Set("expires_in", fmt.Sprint(toot.ExpiresIn))
|
||||||
|
}
|
||||||
|
if 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)
|
||||||
|
@ -408,7 +551,7 @@ func (c *Client) Search(ctx context.Context, q string, qType string, limit int,
|
||||||
return &results, nil
|
return &results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) UploadMediaFromMultipartFileHeader(ctx context.Context, fh *multipart.FileHeader) (*Attachment, error) {
|
func (c *Client) UploadMediaFromMultipartFileHeader(ctx context.Context, fh *multipart.FileHeader, descr string) (*Attachment, error) {
|
||||||
f, err := fh.Open()
|
f, err := fh.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -418,7 +561,7 @@ func (c *Client) UploadMediaFromMultipartFileHeader(ctx context.Context, fh *mul
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
mw := multipart.NewWriter(&buf)
|
mw := multipart.NewWriter(&buf)
|
||||||
fname := filepath.Base(fh.Filename)
|
fname := filepath.Base(fh.Filename)
|
||||||
err = mw.WriteField("description", fname)
|
err = mw.WriteField("description", descr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,4 +19,5 @@ type ReplyContext struct {
|
||||||
ReplySpoiler string
|
ReplySpoiler string
|
||||||
ReplyContent string
|
ReplyContent string
|
||||||
ForceVisibility bool
|
ForceVisibility bool
|
||||||
|
ReplyLanguage string
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,7 @@ type TimelineData struct {
|
||||||
Title string
|
Title string
|
||||||
Type string
|
Type string
|
||||||
Instance string
|
Instance string
|
||||||
|
InstanceType string
|
||||||
Statuses []*mastodon.Status
|
Statuses []*mastodon.Status
|
||||||
NextLink string
|
NextLink string
|
||||||
PrevLink string
|
PrevLink string
|
||||||
|
@ -109,11 +110,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,22 @@ 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
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateMediaDescrForm() string {
|
||||||
|
var mediadescrbuilder string
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
mediadescrbuilder = mediadescrbuilder + `<div><textarea rows="2" id="` + fmt.Sprintf("media-descr-%d", i) + `" name="` + fmt.Sprintf("media-descr-%d", i) + `"></textarea></div>`
|
||||||
|
}
|
||||||
|
return mediadescrbuilder
|
||||||
|
}
|
||||||
|
|
||||||
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 {
|
||||||
|
@ -158,7 +174,9 @@ func NewRenderer(templateGlobPattern string) (r *renderer, err error) {
|
||||||
t := template.New("default")
|
t := template.New("default")
|
||||||
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,
|
||||||
|
"GenerateMediaDescrForm": generateMediaDescrForm,
|
||||||
"StatusContentFilter": statusContentFilter,
|
"StatusContentFilter": statusContentFilter,
|
||||||
"DisplayInteractionCount": displayInteractionCount,
|
"DisplayInteractionCount": displayInteractionCount,
|
||||||
"TimeSince": timeSince,
|
"TimeSince": timeSince,
|
||||||
|
|
|
@ -117,7 +117,7 @@ func (s *service) NavPage(c *client) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) TimelinePage(c *client, tType, instance, listId, maxID,
|
func (s *service) TimelinePage(c *client, tType, instance, listId, maxID,
|
||||||
minID string, tag string) (err error) {
|
minID string, tag string, instance_type string) (err error) {
|
||||||
|
|
||||||
var nextLink, prevLink, title string
|
var nextLink, prevLink, title string
|
||||||
var statuses []*mastodon.Status
|
var statuses []*mastodon.Status
|
||||||
|
@ -159,10 +159,18 @@ func (s *service) TimelinePage(c *client, tType, instance, listId, maxID,
|
||||||
title = "Remote Timeline"
|
title = "Remote Timeline"
|
||||||
case "tremote":
|
case "tremote":
|
||||||
if len(instance) > 0 {
|
if len(instance) > 0 {
|
||||||
statuses, err = c.TrueRemoteTimeline(c.ctx, instance, &pg)
|
if instance_type == "" {
|
||||||
|
instance_type = "mastodon-compatible"
|
||||||
|
}
|
||||||
|
statuses, err = c.TrueRemoteTimeline(c.ctx, instance, instance_type, &pg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
v := make(url.Values)
|
||||||
|
v.Set("max_id", statuses[len(statuses)-1].ID)
|
||||||
|
v.Set("instance", instance)
|
||||||
|
v.Set("instance_type", instance_type)
|
||||||
|
nextLink = "/timeline/" + tType + "?" + v.Encode()
|
||||||
}
|
}
|
||||||
title = "True Remote Timeline"
|
title = "True Remote Timeline"
|
||||||
case "twkn":
|
case "twkn":
|
||||||
|
@ -207,6 +215,10 @@ func (s *service) TimelinePage(c *client, tType, instance, listId, maxID,
|
||||||
}
|
}
|
||||||
statuses[i].Pleroma.Reactions = reactions
|
statuses[i].Pleroma.Reactions = reactions
|
||||||
}
|
}
|
||||||
|
if tType == "tremote" {
|
||||||
|
statuses[i].URL = fmt.Sprintf("/search?q=%s&type=statuses", statuses[i].URL)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len(maxID) > 0 || len(minID) > 0) && len(statuses) > 0 {
|
if (len(maxID) > 0 || len(minID) > 0) && len(statuses) > 0 {
|
||||||
|
@ -215,6 +227,9 @@ func (s *service) TimelinePage(c *client, tType, instance, listId, maxID,
|
||||||
if len(instance) > 0 {
|
if len(instance) > 0 {
|
||||||
v.Set("instance", instance)
|
v.Set("instance", instance)
|
||||||
}
|
}
|
||||||
|
if len(instance_type) > 0 {
|
||||||
|
v.Set("instance_type", instance_type)
|
||||||
|
}
|
||||||
if len(tag) > 0 {
|
if len(tag) > 0 {
|
||||||
v.Set("tag", tag)
|
v.Set("tag", tag)
|
||||||
}
|
}
|
||||||
|
@ -227,9 +242,13 @@ func (s *service) TimelinePage(c *client, tType, instance, listId, maxID,
|
||||||
if len(minID) > 0 || (len(pg.MaxID) > 0 && len(statuses) == 20) {
|
if len(minID) > 0 || (len(pg.MaxID) > 0 && len(statuses) == 20) {
|
||||||
v := make(url.Values)
|
v := make(url.Values)
|
||||||
v.Set("max_id", pg.MaxID)
|
v.Set("max_id", pg.MaxID)
|
||||||
|
v.Set("max_id", statuses[len(statuses)-1].ID)
|
||||||
if len(instance) > 0 {
|
if len(instance) > 0 {
|
||||||
v.Set("instance", instance)
|
v.Set("instance", instance)
|
||||||
}
|
}
|
||||||
|
if len(instance_type) > 0 {
|
||||||
|
v.Set("instance_type", instance_type)
|
||||||
|
}
|
||||||
if len(tag) > 0 {
|
if len(tag) > 0 {
|
||||||
v.Set("tag", tag)
|
v.Set("tag", tag)
|
||||||
}
|
}
|
||||||
|
@ -244,6 +263,7 @@ func (s *service) TimelinePage(c *client, tType, instance, listId, maxID,
|
||||||
Title: title,
|
Title: title,
|
||||||
Type: tType,
|
Type: tType,
|
||||||
Instance: instance,
|
Instance: instance,
|
||||||
|
InstanceType: instance_type,
|
||||||
Statuses: statuses,
|
Statuses: statuses,
|
||||||
NextLink: nextLink,
|
NextLink: nextLink,
|
||||||
PrevLink: prevLink,
|
PrevLink: prevLink,
|
||||||
|
@ -362,6 +382,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 +394,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 +859,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,17 +998,28 @@ 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) (id string, err error) {
|
files []*multipart.FileHeader, edit string, language string, expiresIn int, scheduledAt string,
|
||||||
|
pollOptions []string, pollExpiresIn int, pollHideTotals bool, pollMultiple bool,
|
||||||
|
mediaDescription []string) (id string, err error) {
|
||||||
|
|
||||||
var mediaIDs []string
|
var mediaIDs []string
|
||||||
for _, f := range files {
|
for idx, f := range files {
|
||||||
a, err := c.UploadMediaFromMultipartFileHeader(c.ctx, f)
|
a, err := c.UploadMediaFromMultipartFileHeader(c.ctx, f, mediaDescription[idx])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
mediaIDs = append(mediaIDs, a.ID)
|
mediaIDs = append(mediaIDs, a.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
@ -1008,7 +1029,12 @@ func (s *service) Post(c *client, content string, replyToID string,
|
||||||
Visibility: visibility,
|
Visibility: visibility,
|
||||||
Sensitive: isNSFW,
|
Sensitive: isNSFW,
|
||||||
Edit: edit,
|
Edit: edit,
|
||||||
|
Language: language,
|
||||||
|
ExpiresIn: expiresIn, // pleroma compatible
|
||||||
|
ScheduledAt: scheduledAt,
|
||||||
|
Poll: pollTweet,
|
||||||
}
|
}
|
||||||
|
|
||||||
st, err := c.PostStatus(c.ctx, tweet)
|
st, err := c.PostStatus(c.ctx, tweet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|
|
@ -139,7 +139,8 @@ func NewHandler(s *service, verbose bool, staticDir string) http.Handler {
|
||||||
maxID := q.Get("max_id")
|
maxID := q.Get("max_id")
|
||||||
minID := q.Get("min_id")
|
minID := q.Get("min_id")
|
||||||
tag := q.Get("tag")
|
tag := q.Get("tag")
|
||||||
return s.TimelinePage(c, tType, instance, list, maxID, minID, tag)
|
instance_type := q.Get("instance_type")
|
||||||
|
return s.TimelinePage(c, tType, instance, list, maxID, minID, tag, instance_type)
|
||||||
}, SESSION, HTML)
|
}, SESSION, HTML)
|
||||||
|
|
||||||
defaultTimelinePage := handle(func(c *client) error {
|
defaultTimelinePage := handle(func(c *client) error {
|
||||||
|
@ -313,9 +314,41 @@ func NewHandler(s *service, verbose bool, staticDir string) http.Handler {
|
||||||
isNSFW := c.r.FormValue("is_nsfw") == "true"
|
isNSFW := c.r.FormValue("is_nsfw") == "true"
|
||||||
quickReply := c.r.FormValue("quickreply") == "true"
|
quickReply := c.r.FormValue("quickreply") == "true"
|
||||||
files := c.r.MultipartForm.File["attachments"]
|
files := c.r.MultipartForm.File["attachments"]
|
||||||
|
var mediaDescription []string
|
||||||
|
for i := 0; i < len(files); i++ {
|
||||||
|
v := c.r.FormValue(fmt.Sprintf("media-descr-%d", i))
|
||||||
|
mediaDescription = append(mediaDescription, v)
|
||||||
|
}
|
||||||
edit := c.r.FormValue("edit-status-id")
|
edit := c.r.FormValue("edit-status-id")
|
||||||
|
language := c.r.FormValue("lang-code")
|
||||||
|
expiresIn, err := strconv.Atoi(c.r.FormValue("expires-in"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
scheduledAt := c.r.FormValue("scheduled")
|
||||||
|
if scheduledAt != "" {
|
||||||
|
scheduled, err := time.Parse("2006-01-02T15:04", scheduledAt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
scheduledAt = string(scheduled.UTC().Format(time.RFC3339))
|
||||||
|
}
|
||||||
|
var pollOptions []string
|
||||||
|
for i := 0; i < 20; 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)
|
id, err := s.Post(c, content, replyToID, format, visibility, isNSFW, spoilerText, files, edit, language, expiresIn, scheduledAt, pollOptions, pollExpiresIn, pollHideTotals, pollMultiple, mediaDescription)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -737,6 +770,9 @@ func NewHandler(s *service, verbose bool, staticDir string) http.Handler {
|
||||||
id, _ := mux.Vars(c.r)["id"]
|
id, _ := mux.Vars(c.r)["id"]
|
||||||
q := c.r.URL.Query()
|
q := c.r.URL.Query()
|
||||||
uid := q.Get("uid")
|
uid := q.Get("uid")
|
||||||
|
if uid == "" {
|
||||||
|
uid = c.r.FormValue("uid")
|
||||||
|
}
|
||||||
err := s.ListAddUser(c, id, uid)
|
err := s.ListAddUser(c, id, uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -73,10 +73,16 @@
|
||||||
<div>
|
<div>
|
||||||
{{if .Instance.Pleroma}}
|
{{if .Instance.Pleroma}}
|
||||||
<div class="federation-enabled"> Federation enabled: {{.Instance.Pleroma.MetaData.Federation.Enabled}} </div>
|
<div class="federation-enabled"> Federation enabled: {{.Instance.Pleroma.MetaData.Federation.Enabled}} </div>
|
||||||
<div class="mrf-policies"> MRF Policies </div>
|
<h6> MRF Policies </h6>
|
||||||
{{range .Instance.Pleroma.MetaData.Federation.MrfPolicies}}
|
{{range .Instance.Pleroma.MetaData.Federation.MrfPolicies}}{{.}}<br>{{end}}
|
||||||
{{.}}<br>
|
<h6> Rejected instances </h6>
|
||||||
{{end}}
|
{{range .Instance.Pleroma.MetaData.Federation.MrfSimple.Reject}}{{.}}<br>{{end}}
|
||||||
|
<h6> Force NSFW instances </h6>
|
||||||
|
{{range .Instance.Pleroma.MetaData.Federation.MrfSimple.MediaNsfw}}{{.}}<br>{{end}}
|
||||||
|
<h6> Hiden from federated timeline instances </h6>
|
||||||
|
{{range .Instance.Pleroma.MetaData.Federation.MrfSimple.FederatedTimelineRemoval}}{{.}}<br>{{end}}
|
||||||
|
<h6> Rejected deletion activities </h6>
|
||||||
|
{{range .Instance.Pleroma.MetaData.Federation.MrfSimple.RejectDeletes}}{{.}}<br>{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
<details>
|
<details>
|
||||||
<summary>Current peers ({{.Instance.Stats.DomainCount}})</summary>
|
<summary>Current peers ({{.Instance.Stats.DomainCount}})</summary>
|
||||||
|
|
|
@ -37,6 +37,14 @@
|
||||||
</span>
|
</span>
|
||||||
<button type="submit"> Search </button>
|
<button type="submit"> Search </button>
|
||||||
</form>
|
</form>
|
||||||
|
<div class="page-title"> Add user by their ID </div>
|
||||||
|
<span>some times it can works, some times not</span>
|
||||||
|
<form class="user-list-action" action="/list/{{$.Data.List.ID}}/adduser?" method="POST">
|
||||||
|
<input type="hidden" name="csrf_token" value="{{$.Ctx.CSRFToken}}">
|
||||||
|
<input type="hidden" name="referrer" value="{{$.Ctx.Referrer}}">
|
||||||
|
<input name="uid">
|
||||||
|
<button type="submit"> Add </button>
|
||||||
|
</form>
|
||||||
|
|
||||||
{{if .Q}}
|
{{if .Q}}
|
||||||
{{if .SearchAccounts}}
|
{{if .SearchAccounts}}
|
||||||
|
|
|
@ -43,11 +43,32 @@
|
||||||
<div>
|
<div>
|
||||||
<span class="post-form-field">
|
<span class="post-form-field">
|
||||||
<input id="post-file-picker" type="file" name="attachments" multiple accesskey="A" title="Attachments (A)">
|
<input id="post-file-picker" type="file" name="attachments" multiple accesskey="A" title="Attachments (A)">
|
||||||
|
<details><summary> Descriptions for media attachments </summary>
|
||||||
|
{{GenerateMediaDescrForm | Raw}}
|
||||||
|
</details>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<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" 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="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}}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,17 @@
|
||||||
<a class="img-link" href="/user/{{.ID}}">
|
<a class="img-link" href="/user/{{.ID}}">
|
||||||
<div class="status-uname">{{.Acct}}</div>
|
<div class="status-uname">{{.Acct}}</div>
|
||||||
</a>
|
</a>
|
||||||
|
<div class="status-uregdate">registered/federated {{TimeSince .CreatedAt}}</div>
|
||||||
|
{{if eq (len .Note) 0}}
|
||||||
|
<div class="status-ubio">empty bio!</div>
|
||||||
|
{{end}}
|
||||||
|
{{if not .StatusesCount}}
|
||||||
|
<div class="status-ubio">no statuses!</div>
|
||||||
|
{{end}}
|
||||||
|
{{if .Moved}}
|
||||||
|
<div class="status-umv">Moved to <a href="/user/{{.Moved.ID}}">{{.Moved.Acct}}</a></div>
|
||||||
|
{{end}}
|
||||||
|
<div class="status-ufls">follows: {{.FollowingCount}} followers: {{.FollowersCount}}</div>
|
||||||
</div>
|
</div>
|
||||||
<form class="d-inline" action="/accept/{{.ID}}" method="post" target="_self">
|
<form class="d-inline" action="/accept/{{.ID}}" method="post" target="_self">
|
||||||
<input type="hidden" name="csrf_token" value="{{$.Ctx.CSRFToken}}">
|
<input type="hidden" name="csrf_token" value="{{$.Ctx.CSRFToken}}">
|
||||||
|
|
|
@ -227,6 +227,11 @@
|
||||||
</form>
|
</form>
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="status-action-container">
|
<div class="status-action-container">
|
||||||
|
{{if eq (printf "%.7s" .URL) "/search"}}
|
||||||
|
<div class="status-action">
|
||||||
|
<a href="{{.URL}}" target="_blank">Federate!</a>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
<div class="status-action">
|
<div class="status-action">
|
||||||
<a href="/thread/{{.ID}}?reply=true#status-{{.ID}}">
|
<a href="/thread/{{.ID}}?reply=true#status-{{.ID}}">
|
||||||
reply
|
reply
|
||||||
|
@ -237,6 +242,7 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
{{end}}
|
||||||
<div class="status-action">
|
<div class="status-action">
|
||||||
{{$rt := "retweet"}} {{if .Reblogged}} {{$rt = "unretweet"}} {{end}}
|
{{$rt := "retweet"}} {{if .Reblogged}} {{$rt = "unretweet"}} {{end}}
|
||||||
<form class="status-retweet" data-action="{{$rt}}" action="/{{$rt}}/{{.ID}}" method="post" target="_self">
|
<form class="status-retweet" data-action="{{$rt}}" action="/{{$rt}}/{{.ID}}" method="post" target="_self">
|
||||||
|
|
|
@ -13,6 +13,27 @@
|
||||||
</span>
|
</span>
|
||||||
<button type="submit"> Submit </button>
|
<button type="submit"> Submit </button>
|
||||||
</form>
|
</form>
|
||||||
|
{{if eq .Instance ""}}
|
||||||
|
<a href="/timeline/tremote"> True remote timeline viewer </a>
|
||||||
|
{{else}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{if eq .Type "tremote"}}
|
||||||
|
<form class="search-form" action="/timeline/tremote" method="GET">
|
||||||
|
<span class="post-form-field">
|
||||||
|
<label for="instance"> Instance </label>
|
||||||
|
<input id="instance" name="instance" value="{{.Instance}}" placeholder="example.com:optional_param">
|
||||||
|
<select id="instance_type" name="instance_type" title="Select instance type">
|
||||||
|
<option value="mastodon-compatible" default>Mastodon compatible</option>
|
||||||
|
<option value="misskey"{{if eq .InstanceType "misskey"}}selected{{end}}>Misskey</option>
|
||||||
|
<option value="friendica"{{if eq .InstanceType "friendica"}}selected{{end}}>Friendica</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
<button type="submit"> Submit </button>
|
||||||
|
</form>
|
||||||
|
{{if eq .Instance ""}}
|
||||||
|
<span>Possible params:<br>withFiles - show posts only with media<br>remote - try fetch their global timeline<br>You can combine params: withFile:remote:some_other_param</span>
|
||||||
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if eq .Type "tag"}}
|
{{if eq .Type "tag"}}
|
||||||
<form class="search-form" action="/timeline/tag" method="GET">
|
<form class="search-form" action="/timeline/tag" method="GET">
|
||||||
|
|
|
@ -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 New Issue