Add support for scopes
authorr <r@freesoftwareextremist.com>
Sat, 21 Dec 2019 13:26:31 +0000 (13:26 +0000)
committerr <r@freesoftwareextremist.com>
Sat, 21 Dec 2019 13:26:31 +0000 (13:26 +0000)
- Add scope selection for for new post
- Save new post scope in db
- Copy scope on reply
- Show scope icon on posts

13 files changed:
model/postContext.go [moved from model/replyContext.go with 55% similarity]
model/session.go
model/settings.go
renderer/model.go
service/auth.go
service/logging.go
service/service.go
service/transport.go
static/main.css
templates/postform.tmpl
templates/status.tmpl
templates/thread.tmpl
templates/timeline.tmpl

similarity index 55%
rename from model/replyContext.go
rename to model/postContext.go
index b17aacb1200a10b735a303899f0f25a053f9a51b..90e5771047ddcf2aeb27dd1b34cd58bc7f90e9ea 100644 (file)
@@ -1,5 +1,10 @@
 package model
 
+type PostContext struct {
+       DefaultVisibility string
+       ReplyContext      *ReplyContext
+}
+
 type ReplyContext struct {
        InReplyToID   string
        InReplyToName string
index 42c0aff9ea655e966fb2510cfcc3d70ca78257d4..fce6173159fc0d91f624d450596121f525f8ef57 100644 (file)
@@ -17,7 +17,6 @@ type Session struct {
 
 type SessionRepository interface {
        Add(session Session) (err error)
-       Update(sessionID string, accessToken string) (err error)
        Get(sessionID string) (session Session, err error)
 }
 
index ad7ec0fde833ab4850455092ccfb2691d00b4e00..7afe936e310d0bd2aba65d891edc34622e0040cb 100644 (file)
@@ -1,4 +1,5 @@
 package model
 
 type Settings struct {
+       DefaultVisibility string `json:"default_visibility"`
 }
index ce81e78d2743da7750386f6b6d7d809edb759e5d..9380b7fb40244af07eef8a2d83676589d1666eab 100644 (file)
@@ -16,39 +16,41 @@ func NewNavbarTemplateData(notificationCount int) *NavbarTemplateData {
 }
 
 type TimelinePageTemplateData struct {
-       Statuses   []*mastodon.Status
-       HasNext    bool
-       NextLink   string
-       HasPrev    bool
-       PrevLink   string
-       NavbarData *NavbarTemplateData
+       Statuses    []*mastodon.Status
+       HasNext     bool
+       NextLink    string
+       HasPrev     bool
+       PrevLink    string
+       PostContext model.PostContext
+       NavbarData  *NavbarTemplateData
 }
 
 func NewTimelinePageTemplateData(statuses []*mastodon.Status, hasNext bool, nextLink string, hasPrev bool,
-       prevLink string, navbarData *NavbarTemplateData) *TimelinePageTemplateData {
+       prevLink string, postContext model.PostContext, navbarData *NavbarTemplateData) *TimelinePageTemplateData {
        return &TimelinePageTemplateData{
-               Statuses:   statuses,
-               HasNext:    hasNext,
-               NextLink:   nextLink,
-               HasPrev:    hasPrev,
-               PrevLink:   prevLink,
-               NavbarData: navbarData,
+               Statuses:    statuses,
+               HasNext:     hasNext,
+               NextLink:    nextLink,
+               HasPrev:     hasPrev,
+               PrevLink:    prevLink,
+               PostContext: postContext,
+               NavbarData:  navbarData,
        }
 }
 
 type ThreadPageTemplateData struct {
-       Statuses     []*mastodon.Status
-       ReplyContext *model.ReplyContext
-       ReplyMap     map[string][]mastodon.ReplyInfo
-       NavbarData   *NavbarTemplateData
+       Statuses    []*mastodon.Status
+       PostContext model.PostContext
+       ReplyMap    map[string][]mastodon.ReplyInfo
+       NavbarData  *NavbarTemplateData
 }
 
-func NewThreadPageTemplateData(statuses []*mastodon.Status, replyContext *model.ReplyContext, replyMap map[string][]mastodon.ReplyInfo, navbarData *NavbarTemplateData) *ThreadPageTemplateData {
+func NewThreadPageTemplateData(statuses []*mastodon.Status, postContext model.PostContext, replyMap map[string][]mastodon.ReplyInfo, navbarData *NavbarTemplateData) *ThreadPageTemplateData {
        return &ThreadPageTemplateData{
-               Statuses:     statuses,
-               ReplyContext: replyContext,
-               ReplyMap:     replyMap,
-               NavbarData:   navbarData,
+               Statuses:    statuses,
+               PostContext: postContext,
+               ReplyMap:    replyMap,
+               NavbarData:  navbarData,
        }
 }
 
index 0209273f3ecce491b69c15737b6fcb37393ed312..13e9c5059f31187d5416cbbeb87992b6eea5f7c0 100644 (file)
@@ -23,17 +23,9 @@ func NewAuthService(sessionRepo model.SessionRepository, appRepo model.AppReposi
        return &authService{sessionRepo, appRepo, s}
 }
 
-func getSessionID(ctx context.Context) (sessionID string, err error) {
+func (s *authService) getClient(ctx context.Context) (c *model.Client, err error) {
        sessionID, ok := ctx.Value("session_id").(string)
        if !ok || len(sessionID) < 1 {
-               return "", ErrInvalidSession
-       }
-       return sessionID, nil
-}
-
-func (s *authService) getClient(ctx context.Context) (c *model.Client, err error) {
-       sessionID, err := getSessionID(ctx)
-       if err != nil {
                return nil, ErrInvalidSession
        }
        session, err := s.sessionRepo.Get(sessionID)
@@ -50,7 +42,7 @@ func (s *authService) getClient(ctx context.Context) (c *model.Client, err error
                ClientSecret: client.ClientSecret,
                AccessToken:  session.AccessToken,
        })
-       c = &model.Client{Client: mc}
+       c = &model.Client{Client: mc, Session: session}
        return c, nil
 }
 
@@ -61,21 +53,18 @@ func (s *authService) GetAuthUrl(ctx context.Context, instance string) (
 
 func (s *authService) GetUserToken(ctx context.Context, sessionID string, c *model.Client,
        code string) (token string, err error) {
-       sessionID, err = getSessionID(ctx)
-       if err != nil {
-               return
-       }
        c, err = s.getClient(ctx)
        if err != nil {
                return
        }
 
-       token, err = s.Service.GetUserToken(ctx, sessionID, c, code)
+       token, err = s.Service.GetUserToken(ctx, c.Session.ID, c, code)
        if err != nil {
                return
        }
 
-       err = s.sessionRepo.Update(sessionID, token)
+       c.Session.AccessToken = token
+       err = s.sessionRepo.Add(c.Session)
        if err != nil {
                return
        }
@@ -168,12 +157,12 @@ func (s *authService) UnRetweet(ctx context.Context, client io.Writer, c *model.
        return s.Service.UnRetweet(ctx, client, c, id)
 }
 
-func (s *authService) PostTweet(ctx context.Context, client io.Writer, c *model.Client, content string, replyToID string, files []*multipart.FileHeader) (id string, err error) {
+func (s *authService) PostTweet(ctx context.Context, client io.Writer, c *model.Client, content string, replyToID string, visibility string, files []*multipart.FileHeader) (id string, err error) {
        c, err = s.getClient(ctx)
        if err != nil {
                return
        }
-       return s.Service.PostTweet(ctx, client, c, content, replyToID, files)
+       return s.Service.PostTweet(ctx, client, c, content, replyToID, visibility, files)
 }
 
 func (s *authService) Follow(ctx context.Context, client io.Writer, c *model.Client, id string) (err error) {
index f34bef538f99535bd9a534b64b47dbb90778ad36..ceba716871adaac0fb01f80fda592d75b02f7395 100644 (file)
@@ -133,12 +133,12 @@ func (s *loggingService) UnRetweet(ctx context.Context, client io.Writer, c *mod
        return s.Service.UnRetweet(ctx, client, c, id)
 }
 
-func (s *loggingService) PostTweet(ctx context.Context, client io.Writer, c *model.Client, content string, replyToID string, files []*multipart.FileHeader) (id string, err error) {
+func (s *loggingService) PostTweet(ctx context.Context, client io.Writer, c *model.Client, content string, replyToID string, visibility string, files []*multipart.FileHeader) (id string, err error) {
        defer func(begin time.Time) {
-               s.logger.Printf("method=%v, content=%v, reply_to_id=%v, took=%v, err=%v\n",
-                       "PostTweet", content, replyToID, time.Since(begin), err)
+               s.logger.Printf("method=%v, content=%v, reply_to_id=%v, visibility=%v, took=%v, err=%v\n",
+                       "PostTweet", content, replyToID, visibility, time.Since(begin), err)
        }(time.Now())
-       return s.Service.PostTweet(ctx, client, c, content, replyToID, files)
+       return s.Service.PostTweet(ctx, client, c, content, replyToID, visibility, files)
 }
 
 func (s *loggingService) Follow(ctx context.Context, client io.Writer, c *model.Client, id string) (err error) {
index d844a6f0ba2ec8700ca037a9d20ee468d22f1211..07a026aadc070bda52c497ee8f57a2d39c1a05c9 100644 (file)
@@ -38,7 +38,7 @@ type Service interface {
        UnLike(ctx context.Context, client io.Writer, c *model.Client, id string) (err error)
        Retweet(ctx context.Context, client io.Writer, c *model.Client, id string) (err error)
        UnRetweet(ctx context.Context, client io.Writer, c *model.Client, id string) (err error)
-       PostTweet(ctx context.Context, client io.Writer, c *model.Client, content string, replyToID string, files []*multipart.FileHeader) (id string, err error)
+       PostTweet(ctx context.Context, client io.Writer, c *model.Client, content string, replyToID string, visibility string, files []*multipart.FileHeader) (id string, err error)
        Follow(ctx context.Context, client io.Writer, c *model.Client, id string) (err error)
        UnFollow(ctx context.Context, client io.Writer, c *model.Client, id string) (err error)
 }
@@ -251,12 +251,16 @@ func (svc *service) ServeTimelinePage(ctx context.Context, client io.Writer,
                nextLink = "/timeline?max_id=" + pg.MaxID
        }
 
+       postContext := model.PostContext{
+               DefaultVisibility: c.Session.Settings.DefaultVisibility,
+       }
+
        navbarData, err := svc.getNavbarTemplateData(ctx, client, c)
        if err != nil {
                return
        }
 
-       data := renderer.NewTimelinePageTemplateData(statuses, hasNext, nextLink, hasPrev, prevLink, navbarData)
+       data := renderer.NewTimelinePageTemplateData(statuses, hasNext, nextLink, hasPrev, prevLink, postContext, navbarData)
        err = svc.renderer.RenderTimelinePage(ctx, client, data)
        if err != nil {
                return
@@ -276,7 +280,7 @@ func (svc *service) ServeThreadPage(ctx context.Context, client io.Writer, c *mo
                return
        }
 
-       var replyContext *model.ReplyContext
+       var postContext model.PostContext
        if reply {
                var content string
                if u.ID != status.Account.ID {
@@ -287,10 +291,19 @@ func (svc *service) ServeThreadPage(ctx context.Context, client io.Writer, c *mo
                                content += "@" + status.Mentions[i].Acct + " "
                        }
                }
-               replyContext = &model.ReplyContext{
-                       InReplyToID:   id,
-                       InReplyToName: status.Account.Acct,
-                       ReplyContent:  content,
+
+               s, err := c.GetStatus(ctx, id)
+               if err != nil {
+                       return err
+               }
+
+               postContext = model.PostContext{
+                       DefaultVisibility: s.Visibility,
+                       ReplyContext: &model.ReplyContext{
+                               InReplyToID:   id,
+                               InReplyToName: status.Account.Acct,
+                               ReplyContent:  content,
+                       },
                }
        }
 
@@ -314,7 +327,7 @@ func (svc *service) ServeThreadPage(ctx context.Context, client io.Writer, c *mo
                return
        }
 
-       data := renderer.NewThreadPageTemplateData(statuses, replyContext, replyMap, navbarData)
+       data := renderer.NewThreadPageTemplateData(statuses, postContext, replyMap, navbarData)
        err = svc.renderer.RenderThreadPage(ctx, client, data)
        if err != nil {
                return
@@ -469,7 +482,7 @@ func (svc *service) UnRetweet(ctx context.Context, client io.Writer, c *model.Cl
        return
 }
 
-func (svc *service) PostTweet(ctx context.Context, client io.Writer, c *model.Client, content string, replyToID string, files []*multipart.FileHeader) (id string, err error) {
+func (svc *service) PostTweet(ctx context.Context, client io.Writer, c *model.Client, content string, replyToID string, visibility string, files []*multipart.FileHeader) (id string, err error) {
        var mediaIds []string
        for _, f := range files {
                a, err := c.UploadMediaFromMultipartFileHeader(ctx, f)
@@ -479,10 +492,17 @@ func (svc *service) PostTweet(ctx context.Context, client io.Writer, c *model.Cl
                mediaIds = append(mediaIds, a.ID)
        }
 
+       // save visibility if it's a non-reply post
+       if len(replyToID) < 1 && visibility != c.Session.Settings.DefaultVisibility {
+               c.Session.Settings.DefaultVisibility = visibility
+               svc.sessionRepo.Add(c.Session)
+       }
+
        tweet := &mastodon.Toot{
                Status:      content,
                InReplyToID: replyToID,
                MediaIDs:    mediaIds,
+               Visibility:  visibility,
        }
 
        s, err := c.PostStatus(ctx, tweet)
index 2b7566291a8b03d3aa9b31d99d59dfedb38f9a9e..4e852f240033f4449fdabf17639655b91ac0cc7f 100644 (file)
@@ -155,9 +155,10 @@ func NewHandler(s Service, staticDir string) http.Handler {
 
                content := getMultipartFormValue(req.MultipartForm, "content")
                replyToID := getMultipartFormValue(req.MultipartForm, "reply_to_id")
+               visibility := getMultipartFormValue(req.MultipartForm, "visibility")
                files := req.MultipartForm.File["attachments"]
 
-               id, err := s.PostTweet(ctx, w, nil, content, replyToID, files)
+               id, err := s.PostTweet(ctx, w, nil, content, replyToID, visibility, files)
                if err != nil {
                        s.ServeErrorPage(ctx, w, err)
                        return
index 4f8d1565b58e6c806f34110594ee383f50cbb503..e37cef44da034d5ba36d40b7d6bd53e6a61ae842 100644 (file)
     font-family: inherit;
     font-size: inherit;
 }
+
+.status-visibility {
+       margin: 0 4px;
+       display: inline-block;
+       color: #444444;
+}
+
+.status-visibility span {
+       font-size: 9pt;
+       vertical-align: bottom;
+}
index 3f6eeaa0cf091a141627c582d8994a306587a6bb..137232f3d5d9b41317a7f66a4c2207a298789f59 100644 (file)
@@ -1,15 +1,25 @@
 <form class="post-form" action="/post" method="POST" enctype="multipart/form-data">
-       {{if .}}
-       <input type="hidden" name="reply_to_id" value="{{.InReplyToID}}" />
-       <label for="post-content"> Reply to {{.InReplyToName}} </label>
+       {{if .ReplyContext}}
+       <input type="hidden" name="reply_to_id" value="{{.ReplyContext.InReplyToID}}" />
+       <label for="post-content"> Reply to {{.ReplyContext.InReplyToName}} </label>
        {{else}}
        <label for="post-content"> New post </label>
        {{end}}
        <div class="post-form-content-container">
-               <textarea id="post-content" name="content" class="post-content" cols="50" rows="5">{{if .}}{{.ReplyContent}}{{end}}</textarea>
+               <textarea id="post-content" name="content" class="post-content" cols="50" rows="5">{{if .ReplyContext}}{{.ReplyContext.ReplyContent}}{{end}}</textarea>
        </div>
        <div>
-               Attachments <input id="post-file-picker" type="file" name="attachments" multiple>
+               <label for ="post-visilibity"> Visibility </label>
+               <select id="post-visilibity" type="file" name="visibility">
+                       <option value="public" {{if eq .DefaultVisibility "public"}}selected{{end}}>Public</option>
+                       <option value="unlisted" {{if eq .DefaultVisibility "unlisted"}}selected{{end}}>Unlisted</option>
+                       <option value="private" {{if eq .DefaultVisibility "private"}}selected{{end}}>Private</option>
+                       <option value="direct" {{if eq .DefaultVisibility "direct"}}selected{{end}}>Direct</option>
+               </select>
+       </div>
+       <div>
+               <label for ="post-file-picker"> Attachments </label>
+               <input id="post-file-picker" type="file" name="attachments" multiple>
        </div>
        <button type="submit"> Post </button>
 </form>
index 0e6f0c0ba71f5223096a9aafbdf45bf6dc37d7f7..a89fc78eeb0d33e90008ae236811a8f93d882543 100644 (file)
                                <a href="/user/{{.Account.ID}}" >
                                        <span class="status-uname"> {{.Account.Acct}} </span>
                                </a>
+                               <a class="status-visibility" href="{{.URL}}" target="_blank">
+                                       {{if eq .Visibility "public"}}
+                                       <span class="icon dripicons-web" title="Public"></span> 
+                                       {{else if eq .Visibility "unlisted"}}
+                                       <span class="icon dripicons-lock-open" title="Unlisted"></span> 
+                                       {{else if eq .Visibility "private"}}
+                                       <span class="icon dripicons-lock" title="Private"></span> 
+                                       {{else if eq .Visibility "direct"}}
+                                       <span class="icon dripicons-mail" title="Direct"></span> 
+                                       {{end}}
+                               </a>
                        </div>
                        {{end}}
                        <div class="status-reply-container">
                                        <span class="icon dripicons-reply"></span> 
                                        <span> {{DisplayInteractionCount .RepliesCount}} </span>
                                </a>
-                               {{if .Reblogged}}
-                               <a class="status-retweet" href="/unretweet/{{.ID}}" title="undo repost"> 
-                                       <span class="icon dripicons-retweet retweeted"></span> 
-                                       <span> {{DisplayInteractionCount .ReblogsCount}} </span>
-                               </a>
-                               {{else}}
-                               <a class="status-retweet" href="/retweet/{{.ID}}" title="repost"> 
+                               {{if or (eq .Visibility "private") (eq .Visibility "direct")}}
+                               <a class="status-retweet" title="this status cannot be retweeted"> 
                                        <span class="icon dripicons-retweet"></span> 
                                        <span> {{DisplayInteractionCount .ReblogsCount}} </span>
                                </a>
+                               {{else}}
+                                       {{if .Reblogged}}
+                                       <a class="status-retweet" href="/unretweet/{{.ID}}" title="undo retweet"> 
+                                               <span class="icon dripicons-retweet retweeted"></span> 
+                                               <span> {{DisplayInteractionCount .ReblogsCount}} </span>
+                                       </a>
+                                       {{else}}
+                                       <a class="status-retweet" href="/retweet/{{.ID}}" title="retweet"> 
+                                               <span class="icon dripicons-retweet"></span> 
+                                               <span> {{DisplayInteractionCount .ReblogsCount}} </span>
+                                       </a>
+                                       {{end}}
                                {{end}}
                                {{if .Favourited}}
                                <a class="status-like" href="/unlike/{{.ID}}" title="unlike"> 
index 9d8f6503aacf02fcf8f06a0899f64f17dd2aa6f8..afb307b3b9ea5eb2e695692f4e230898af184e7b 100644 (file)
@@ -5,8 +5,8 @@
 {{range .Statuses}}
 
 {{template "status.tmpl" .}}
-{{if $.ReplyContext}}{{if eq .ID $.ReplyContext.InReplyToID}}
-{{template "postform.tmpl" $.ReplyContext}}
+{{if $.PostContext.ReplyContext}}{{if eq .ID $.PostContext.ReplyContext.InReplyToID}}
+{{template "postform.tmpl" $.PostContext}}
 {{end}}{{end}}
 
 {{end}}
index 09717c16194f0949ade1a9d990279d116aac64b1..6280e653a4fec5f14cc3384205a6cc3746d7f466 100644 (file)
@@ -3,7 +3,7 @@
 <div class="page-title"> Timeline </div>
 
 
-{{template "postform.tmpl" }}
+{{template "postform.tmpl" .PostContext}}
 
 {{range .Statuses}}
 {{template "status.tmpl" .}}