···28 // pinnedRepositories: Any ATURI, it is up to appviews to validate these fields.
29 PinnedRepositories []string `json:"pinnedRepositories,omitempty" cborgen:"pinnedRepositories,omitempty"`
30 Stats []string `json:"stats,omitempty" cborgen:"stats,omitempty"`
031}
···28 // pinnedRepositories: Any ATURI, it is up to appviews to validate these fields.
29 PinnedRepositories []string `json:"pinnedRepositories,omitempty" cborgen:"pinnedRepositories,omitempty"`
30 Stats []string `json:"stats,omitempty" cborgen:"stats,omitempty"`
31+ Pronouns *string `json:"pronouns,omitempty" cborgen:"pronouns,omitempty"`
32}
···13 <title>login · tangled</title>
14 </head>
15 <body class="flex items-center justify-center min-h-screen">
16+ <main class="max-w-md px-7 mt-4">
17 <h1 class="flex place-content-center text-3xl font-semibold italic dark:text-white" >
18 {{ template "fragments/logotype" }}
19 </h1>
···21 tightly-knit social coding.
22 </h2>
23 <form
24+ class="mt-4"
25 hx-post="/login"
26 hx-swap="none"
27 hx-disabled-elt="#login-button"
···29 <div class="flex flex-col">
30 <label for="handle">handle</label>
31 <input
32+ autocapitalize="none"
33+ autocorrect="off"
34+ autocomplete="username"
35 type="text"
36 id="handle"
37 name="handle"
···56 <span>login</span>
57 </button>
58 </form>
59+ {{ if .ErrorCode }}
60+ <div class="flex gap-2 my-2 bg-red-50 dark:bg-red-900 border border-red-500 rounded drop-shadow-sm px-3 py-2 text-red-500 dark:text-red-300">
61+ <span class="py-1">{{ i "circle-alert" "w-4 h-4" }}</span>
62+ <div>
63+ <h5 class="font-medium">Login error</h5>
64+ <p class="text-sm">
65+ {{ if eq .ErrorCode "access_denied" }}
66+ You have not authorized the app.
67+ {{ else if eq .ErrorCode "session" }}
68+ Server failed to create user session.
69+ {{ else }}
70+ Internal Server error.
71+ {{ end }}
72+ Please try again.
73+ </p>
74+ </div>
75+ </div>
76+ {{ end }}
77 <p class="text-sm text-gray-500">
78 Don't have an account? <a href="/signup" class="underline">Create an account</a> on Tangled now!
79 </p>
+23
appview/pagination/page.go
···1package pagination
2003type Page struct {
4 Offset int // where to start from
5 Limit int // number of items in a page
···10 Offset: 0,
11 Limit: 30,
12 }
00000000000000000000013}
1415func (p Page) Previous() Page {
···1package pagination
23+import "context"
4+5type Page struct {
6 Offset int // where to start from
7 Limit int // number of items in a page
···12 Offset: 0,
13 Limit: 30,
14 }
15+}
16+17+type ctxKey struct{}
18+19+func IntoContext(ctx context.Context, page Page) context.Context {
20+ return context.WithValue(ctx, ctxKey{}, page)
21+}
22+23+func FromContext(ctx context.Context) Page {
24+ if ctx == nil {
25+ return FirstPage()
26+ }
27+ v := ctx.Value(ctxKey{})
28+ if v == nil {
29+ return FirstPage()
30+ }
31+ page, ok := v.(Page)
32+ if !ok {
33+ return FirstPage()
34+ }
35+ return page
36}
3738func (p Page) Previous() Page {
+1
appview/state/follow.go
···26 subjectIdent, err := s.idResolver.ResolveIdent(r.Context(), subject)
27 if err != nil {
28 log.Println("failed to follow, invalid did")
029 }
3031 if currentUser.Did == subjectIdent.DID.String() {
···26 subjectIdent, err := s.idResolver.ResolveIdent(r.Context(), subject)
27 if err != nil {
28 log.Println("failed to follow, invalid did")
29+ return
30 }
3132 if currentUser.Did == subjectIdent.DID.String() {
+1-4
appview/state/gfi.go
···18func (s *State) GoodFirstIssues(w http.ResponseWriter, r *http.Request) {
19 user := s.oauth.GetUser(r)
2021- page, ok := r.Context().Value("page").(pagination.Page)
22- if !ok {
23- page = pagination.FirstPage()
24- }
2526 goodFirstIssueLabel := fmt.Sprintf("at://%s/%s/%s", consts.TangledDid, tangled.LabelDefinitionNSID, "good-first-issue")
27
···18func (s *State) GoodFirstIssues(w http.ResponseWriter, r *http.Request) {
19 user := s.oauth.GetUser(r)
2021+ page := pagination.FromContext(r.Context())
0002223 goodFirstIssueLabel := fmt.Sprintf("at://%s/%s/%s", consts.TangledDid, tangled.LabelDefinitionNSID, "good-first-issue")
24