removed gorilla cookies, single host mode, settings page, default filter, mobile viewport style, voting xhr, docker workflow

This commit is contained in:
Ryan Stafford 2023-07-02 17:29:35 -04:00
parent 8de7bbfa9c
commit d61d68d9e4
13 changed files with 234 additions and 156 deletions

36
main.go
View file

@ -7,9 +7,7 @@ import (
"log"
"net"
"net/http"
"os"
"github.com/gorilla/sessions"
"github.com/julienschmidt/httprouter"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/extension"
@ -19,7 +17,6 @@ var watch = flag.Bool("w", false, "watch for file changes")
var addr = flag.String("addr", ":80", "http service address")
var md goldmark.Markdown
var templates map[string]*template.Template
var store = sessions.NewCookieStore([]byte(os.Getenv("SESSIONSECRET")))
type AddHeaderTransport struct {
T http.RoundTripper
@ -54,7 +51,7 @@ func init() {
md = goldmark.New(goldmark.WithExtensions(extension.Linkify))
templates = make(map[string]*template.Template)
if !*watch {
for _, name := range []string{"index.html", "login.html", "frontpage.html", "root.html"} {
for _, name := range []string{"index.html", "login.html", "frontpage.html", "root.html", "settings.html"} {
t := template.New(name).Funcs(funcMap)
glob, err := t.ParseGlob("templates/*")
if err != nil {
@ -81,36 +78,7 @@ func middleware(n httprouter.Handle) httprouter.Handle {
func main() {
flag.Parse()
log.Println("serve", *addr)
router := httprouter.New()
router.ServeFiles("/:host/static/*filepath", http.Dir("public"))
router.GET("/", GetRoot)
router.POST("/", PostRoot)
router.GET("/:host/", middleware(GetFrontpage))
router.GET("/:host/search", middleware(Search))
router.POST("/:host/search", middleware(UserOp))
router.GET("/:host/inbox", middleware(Inbox))
router.GET("/:host/login", middleware(GetLogin))
router.POST("/:host/login", middleware(SignUpOrLogin))
router.POST("/:host/", middleware(UserOp))
router.GET("/:host/icon.jpg", middleware(GetIcon))
router.GET("/:host/c/:community", middleware(GetFrontpage))
router.POST("/:host/c/:community", middleware(UserOp))
router.GET("/:host/c/:community/search", middleware(Search))
router.GET("/:host/post/:postid", middleware(GetPost))
router.POST("/:host/post/:postid", middleware(UserOp))
router.GET("/:host/comment/:commentid", middleware(GetComment))
router.GET("/:host/comment/:commentid/:op", middleware(GetComment))
router.POST("/:host/comment/:commentid", middleware(UserOp))
router.GET("/:host/u/:username", middleware(GetUser))
router.GET("/:host/u/:username/message", middleware(GetMessageForm))
router.POST("/:host/u/:username/message", middleware(SendMessage))
router.POST("/:host/u/:username", middleware(UserOp))
router.GET("/:host/u/:username/search", middleware(Search))
router.GET("/:host/create_post", middleware(GetCreatePost))
router.POST("/:host/create_post", middleware(UserOp))
router.GET("/:host/create_community", middleware(GetCreateCommunity))
router.POST("/:host/create_community", middleware(UserOp))
router := GetRouter()
err := http.ListenAndServe(*addr, router)
if err != nil {
log.Fatal("ListenAndServe: ", err)

219
routes.go
View file

@ -11,6 +11,7 @@ import (
"io"
"net/http"
"net/url"
"os"
"regexp"
"strconv"
"strings"
@ -24,6 +25,12 @@ import (
)
var funcMap = template.FuncMap{
"host": func(host string) string {
if l := os.Getenv("LEMMY_DOMAIN"); l != "" {
return l
}
return host
},
"proxy": func(s string) string {
u, err := url.Parse(s)
if err != nil {
@ -68,7 +75,7 @@ var funcMap = template.FuncMap{
}
return false
},
"host": func(p Post) string {
"domain": func(p Post) string {
if p.Post.URL.IsValid() {
l, err := url.Parse(p.Post.URL.String())
if err != nil {
@ -111,8 +118,10 @@ var funcMap = template.FuncMap{
}
converted := buf.String()
converted = strings.Replace(converted, `<img `, `<img loading="lazy" `, -1)
re := regexp.MustCompile(`href="https:\/\/([a-zA-Z0-9\.]+\/(c\/[a-zA-Z0-9]+|(post|comment)\/\d+))`)
converted = re.ReplaceAllString(converted, `href="/$1`)
if os.Getenv("LEMMY_DOMAIN") != "" {
re := regexp.MustCompile(`href="https:\/\/([a-zA-Z0-9\.]+\/(c\/[a-zA-Z0-9]+|(post|comment)\/\d+))`)
converted = re.ReplaceAllString(converted, `href="/$1`)
}
return template.HTML(converted)
},
"contains": strings.Contains,
@ -124,17 +133,20 @@ var funcMap = template.FuncMap{
func Initialize(Host string, r *http.Request) (State, error) {
state := State{
Host: Host,
Sort: "Hot",
Page: 1,
Status: http.StatusOK,
}
state.ParseQuery(r.URL.RawQuery)
lemmyDomain := os.Getenv("LEMMY_DOMAIN")
if lemmyDomain != "" {
state.Host = "."
Host = lemmyDomain
}
remoteAddr := r.RemoteAddr
if r.Header.Get("CF-Connecting-IP") != "" {
remoteAddr = r.Header.Get("CF-Connecting-IP")
}
client := http.Client{Transport: NewAddHeaderTransport(remoteAddr)}
c, err := lemmy.NewWithClient("https://"+state.Host, &client)
c, err := lemmy.NewWithClient("https://"+Host, &client)
if err != nil {
fmt.Println(err)
state.Status = http.StatusInternalServerError
@ -142,24 +154,26 @@ func Initialize(Host string, r *http.Request) (State, error) {
}
state.HTTPClient = &client
state.Client = c
session, err := store.Get(r, state.Host)
if err == nil {
token, ok1 := session.Values["token"].(string)
username, ok2 := session.Values["username"].(string)
userid, ok3 := session.Values["id"].(int)
if ok1 && ok2 && ok3 {
token := getCookie(r, "jwt")
user := getCookie(r, "user")
parts := strings.Split(user, ":")
if len(parts) == 2 {
if id, err := strconv.Atoi(parts[1]); err == nil {
state.Client.Token = token
sess := Session{
UserName: username,
UserID: userid,
UserName: parts[0],
UserID: id,
}
state.Session = &sess
if state.Listing == "" {
state.Listing = "Subscribed"
}
}
}
if state.Listing == "" {
state.Listing = getCookie(r, "DefaultListingType")
state.Sort = getCookie(r, "DefaultSortType")
state.ParseQuery(r.URL.RawQuery)
if state.Sort == "" {
state.Sort = "Hot"
}
if state.Listing == "" || state.Session == nil && state.Listing == "Subscribed" {
state.Listing = "All"
}
return state, nil
@ -410,11 +424,16 @@ func GetCreatePost(w http.ResponseWriter, r *http.Request, ps httprouter.Params)
func GetCreateCommunity(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
state, err := Initialize(ps.ByName("host"), r)
if err != nil {
fmt.Println(err)
Render(w, "index.html", state)
return
}
state.GetSite()
state.Op = "create_community"
if ps.ByName("community") != "" {
state.GetCommunity(ps.ByName("community"))
state.Op = "edit_community"
}
Render(w, "index.html", state)
}
@ -428,6 +447,48 @@ func Inbox(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
Render(w, "index.html", state)
state.MarkAllAsRead()
}
func getCookie(r *http.Request, name string) string {
cookie, err := r.Cookie(name)
if err != nil {
return ""
}
return cookie.Value
}
func setCookie(w http.ResponseWriter, name string, value string) {
cookie := http.Cookie{
Name: name,
Value: value,
}
http.SetCookie(w, &cookie)
}
func deleteCookie(w http.ResponseWriter, name string) {
cookie := http.Cookie{
Name: name,
MaxAge: -1,
}
http.SetCookie(w, &cookie)
}
func Settings(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
state, err := Initialize(ps.ByName("host"), r)
if err != nil {
Render(w, "index.html", state)
return
}
switch r.Method {
case "POST":
fmt.Println(r.FormValue("DefaultSortType"))
for _, name := range []string{"DefaultSortType", "DefaultListingType"} {
setCookie(w, name, r.FormValue(name))
}
state.Listing = r.FormValue("DefaultListingType")
state.Sort = r.FormValue("DefaultSortType")
case "GET":
if state.Session != nil {
fmt.Println("get settings")
}
}
Render(w, "settings.html", state)
}
func SignUpOrLogin(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
state, err := Initialize(ps.ByName("host"), r)
@ -436,6 +497,7 @@ func SignUpOrLogin(w http.ResponseWriter, r *http.Request, ps httprouter.Params)
return
}
var token string
var username string
switch r.FormValue("submit") {
case "log in":
resp, err := state.Client.Login(context.Background(), types.Login{
@ -451,6 +513,7 @@ func SignUpOrLogin(w http.ResponseWriter, r *http.Request, ps httprouter.Params)
}
if resp.JWT.IsValid() {
token = resp.JWT.String()
username = r.FormValue("username")
}
case "sign up":
register := types.Register{
@ -480,6 +543,7 @@ func SignUpOrLogin(w http.ResponseWriter, r *http.Request, ps httprouter.Params)
return
}
if resp.JWT.IsValid() {
username = r.FormValue("username")
token = resp.JWT.String()
} else {
var alert string
@ -496,7 +560,6 @@ func SignUpOrLogin(w http.ResponseWriter, r *http.Request, ps httprouter.Params)
}
}
if token != "" {
session, err := store.Get(r, state.Host)
if err != nil {
state.Error = err
state.GetSite()
@ -504,20 +567,11 @@ func SignUpOrLogin(w http.ResponseWriter, r *http.Request, ps httprouter.Params)
Render(w, "login.html", state)
return
}
if resp, err := state.Client.Site(context.Background(), types.GetSite{
Auth: types.NewOptional(token),
}); err != nil {
fmt.Println(err)
return
} else if myUser, err := resp.MyUser.Value(); err == nil {
// Error is nil when value is nil?
return
} else {
session.Values["username"] = myUser.LocalUserView.Person.Name
session.Values["id"] = myUser.LocalUserView.Person.ID
}
session.Values["token"] = token
session.Save(r, w)
state.GetUser(username)
setCookie(w, "jwt", token)
userid := strconv.Itoa(state.User.PersonView.Person.ID)
setCookie(w, "user", state.User.PersonView.Person.Name+":"+userid)
setCookie(w, "jwt", token)
r.URL.Path = "/" + state.Host
http.Redirect(w, r, r.URL.String(), 301)
return
@ -598,10 +652,9 @@ func UserOp(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
Follow: true,
})
case "logout":
if session, err := store.Get(r, state.Host); err == nil {
session.Options.MaxAge = -1
session.Save(r, w)
}
fmt.Println("logout")
deleteCookie(w, "jwt")
deleteCookie(w, "user")
case "login":
resp, err := state.Client.Login(context.Background(), types.Login{
UsernameOrEmail: r.FormValue("user"),
@ -611,14 +664,10 @@ func UserOp(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
state.Status = http.StatusUnauthorized
}
if resp.JWT.IsValid() {
session, err := store.Get(r, state.Host)
if err == nil {
state.GetUser(r.FormValue("user"))
session.Values["token"] = resp.JWT.String()
session.Values["username"] = state.User.PersonView.Person.Name
session.Values["id"] = state.User.PersonView.Person.ID
session.Save(r, w)
}
state.GetUser(r.FormValue("user"))
setCookie(w, "jwt", resp.JWT.String())
userid := strconv.Itoa(state.User.PersonView.Person.ID)
setCookie(w, "user", state.User.PersonView.Person.Name+":"+userid)
}
case "create_community":
state.GetSite()
@ -804,6 +853,12 @@ func UserOp(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
Score: score,
}
state.Client.CreatePostLike(context.Background(), post)
if r.FormValue("xhr") != "" {
state.GetPost(postid)
state.XHR = true
Render(w, "index.html", state)
return
}
case "vote_comment":
var score int16
score = 1
@ -819,6 +874,12 @@ func UserOp(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
Score: score,
}
state.Client.CreateCommentLike(context.Background(), post)
if r.FormValue("xhr") != "" {
state.XHR = true
state.GetComment(commentid)
Render(w, "index.html", state)
return
}
case "create_comment":
if ps.ByName("postid") != "" {
postid, _ := strconv.Atoi(ps.ByName("postid"))
@ -873,3 +934,71 @@ func UserOp(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
}
http.Redirect(w, r, r.URL.String(), 301)
}
func GetRouter() *httprouter.Router {
host := os.Getenv("LEMMY_DOMAIN")
router := httprouter.New()
if host == "" {
router.ServeFiles("/:host/static/*filepath", http.Dir("public"))
router.GET("/", GetRoot)
router.POST("/", PostRoot)
router.GET("/:host/", middleware(GetFrontpage))
router.GET("/:host/search", middleware(Search))
router.POST("/:host/search", middleware(UserOp))
router.GET("/:host/inbox", middleware(Inbox))
router.GET("/:host/login", middleware(GetLogin))
router.POST("/:host/login", middleware(SignUpOrLogin))
router.GET("/:host/settings", middleware(Settings))
router.POST("/:host/settings", middleware(Settings))
router.POST("/:host/", middleware(UserOp))
router.GET("/:host/icon.jpg", middleware(GetIcon))
router.GET("/:host/c/:community", middleware(GetFrontpage))
router.POST("/:host/c/:community", middleware(UserOp))
router.GET("/:host/c/:community/search", middleware(Search))
router.GET("/:host/c/:community/edit", middleware(GetCreateCommunity))
router.GET("/:host/post/:postid", middleware(GetPost))
router.POST("/:host/post/:postid", middleware(UserOp))
router.GET("/:host/comment/:commentid", middleware(GetComment))
router.GET("/:host/comment/:commentid/:op", middleware(GetComment))
router.POST("/:host/comment/:commentid", middleware(UserOp))
router.GET("/:host/u/:username", middleware(GetUser))
router.GET("/:host/u/:username/message", middleware(GetMessageForm))
router.POST("/:host/u/:username/message", middleware(SendMessage))
router.POST("/:host/u/:username", middleware(UserOp))
router.GET("/:host/u/:username/search", middleware(Search))
router.GET("/:host/create_post", middleware(GetCreatePost))
router.POST("/:host/create_post", middleware(UserOp))
router.GET("/:host/create_community", middleware(GetCreateCommunity))
router.POST("/:host/create_community", middleware(UserOp))
} else {
router.ServeFiles("/_/static/*filepath", http.Dir("public"))
router.GET("/", middleware(GetFrontpage))
router.GET("/search", middleware(Search))
router.POST("/search", middleware(UserOp))
router.GET("/inbox", middleware(Inbox))
router.GET("/login", middleware(GetLogin))
router.POST("/login", middleware(SignUpOrLogin))
router.GET("/settings", middleware(Settings))
router.POST("/settings", middleware(Settings))
router.POST("/", middleware(UserOp))
router.GET("/icon.jpg", middleware(GetIcon))
router.GET("/c/:community", middleware(GetFrontpage))
router.POST("/c/:community", middleware(UserOp))
router.GET("/c/:community/search", middleware(Search))
router.GET("/c/:community/edit", middleware(GetCreateCommunity))
router.GET("/post/:postid", middleware(GetPost))
router.POST("/post/:postid", middleware(UserOp))
router.GET("/comment/:commentid", middleware(GetComment))
router.GET("/comment/:commentid/:op", middleware(GetComment))
router.POST("/comment/:commentid", middleware(UserOp))
router.GET("/u/:username", middleware(GetUser))
router.GET("/u/:username/message", middleware(GetMessageForm))
router.POST("/u/:username/message", middleware(SendMessage))
router.POST("/u/:username", middleware(UserOp))
router.GET("/u/:username/search", middleware(Search))
router.GET("/create_post", middleware(GetCreatePost))
router.POST("/create_post", middleware(UserOp))
router.GET("/create_community", middleware(GetCreateCommunity))
router.POST("/create_community", middleware(UserOp))
}
return router
}

View file

@ -176,41 +176,6 @@ func (state *State) ParseQuery(RawQuery string) {
}
}
//func (state *State) Build() {
// if state.Listing == "" {
// state.Listing = "All"
// }
// if state.Op == "create_post" {
// if state.CommunityName != "" {
// state.GetCommunity()
// }
// return
// }
// if state.CommentID > 0 {
// state.GetComment()
// state.GetPost()
// state.GetCommunity()
// return
// }
//
// if state.UserName != "" {
// state.GetUser()
// return
// }
//
// if state.PostID == 0 {
// state.GetPosts()
// } else {
// state.GetPost()
// state.GetComments()
// }
//
// if state.CommunityName != "" {
// state.GetCommunity()
// }
//
//}
func (state *State) LemmyError(domain string) error {
var nodeInfo NodeInfo
res, err := state.HTTPClient.Get("https://" + domain + "/nodeinfo/2.0.json")
@ -255,6 +220,9 @@ func (state *State) GetSite() {
}
func (state *State) GetComment(commentid int) {
if state.Sort != "Hot" && state.Sort != "Top" && state.Sort != "Old" && state.Sort != "New" {
state.Sort = "Hot"
}
state.CommentID = commentid
cresp, err := state.Client.Comments(context.Background(), types.GetComments{
ParentID: types.NewOptional(state.CommentID),
@ -284,6 +252,9 @@ func (state *State) GetComment(commentid int) {
}
}
func (state *State) GetComments() {
if state.Sort != "Hot" && state.Sort != "Top" && state.Sort != "Old" && state.Sort != "New" {
state.Sort = "Hot"
}
cresp, err := state.Client.Comments(context.Background(), types.GetComments{
PostID: types.NewOptional(state.PostID),
Sort: types.NewOptional(types.CommentSortType(state.Sort)),
@ -293,6 +264,7 @@ func (state *State) GetComments() {
})
if err != nil {
state.Status = http.StatusInternalServerError
fmt.Println(err)
return
}
state.CommentCount = len(cresp.Comments)

View file

@ -1,8 +1,8 @@
<div class="comment{{if or (lt .P.Counts.Score -5) .P.Comment.Deleted }} hidden{{end}}" id="c{{.P.Comment.ID}}" onclick="commentClick(event)">
<div class="meta">
{{ if .State.Session }}
<div class="score{{ if eq .P.MyVote.String "1"}} like{{ else if eq .P.MyVote.String "-1"}} dislike{{end}}">
<form class="link-btn" method="POST">
<div class="score">
<form class="link-btn{{ if eq .P.MyVote.String "1"}} like{{ else if eq .P.MyVote.String "-1"}} dislike{{end}}" method="POST">
<input type="submit" name="vote" value="▲">
<div></div>
{{ if .P.MyVote.IsValid}}
@ -10,7 +10,7 @@
{{ end}}
<input type="hidden" name="op" value="vote_comment">
<input type="hidden" name="commentid" value="{{.P.Comment.ID }}">
<input type="submit" value="▼">
<input type="submit" name="vote" value="▼">
</form>
</div>
{{ end }}

View file

@ -1,8 +1,8 @@
{{ $c := .Community }}
<form class="create" method="POST" enctype="multipart/form-data">
<form class="create" method="POST" {{ if eq .Op "edit_community" }} action="./" {{ end }} enctype="multipart/form-data">
<div>
<label class="required" for="name">name</label>
<input type="text" required name="name" id="name" value="{{ if $c }}{{ $c.CommunityView.Community.Name }}{{end}}">
<input type="text" required name="name" id="name" {{ if $c }}value="{{ $c.CommunityView.Community.Name }}" disabled{{end}}>
</div>
<div>
<label class="required" for="title">display name</label>

View file

@ -1,8 +1,9 @@
<!DOCTYPE html>
<head>
<title>{{ if and .Community (ne .Community.CommunityView.Community.Title "")}}{{.Community.CommunityView.Community.Title}}{{else if ne .CommunityName ""}}/c/{{.CommunityName}}{{ else if .User}}overview for {{.User.PersonView.Person.Name}}{{else}}{{ .Host }}{{end}}</title>
<title>{{ if and .Community (ne .Community.CommunityView.Community.Title "")}}{{.Community.CommunityView.Community.Title}}{{else if ne .CommunityName ""}}/c/{{.CommunityName}}{{ else if .User}}overview for {{.User.PersonView.Person.Name}}{{else}}{{ host .Host }}{{end}}</title>
<link rel="shortcut icon" href="/{{.Host}}/icon.jpg">
<link rel="stylesheet" href="/_/static/style.css">
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<noscript>
@ -17,7 +18,6 @@
</noscript>
{{ template "nav.html" . -}}
{{ template "sidebar.html" . }}
{{ if or (contains .Sort "Top") (and (not .PostID) (not .User) (not .Community) (not .Activities) (eq .Op ""))}}
{{ template "menu.html" . }}
{{ end}}
@ -31,11 +31,7 @@
{{ template "post.html" . }}
{{ end }}
{{ if and .Session (not .Posts) (not .Community) (and .Listing "Subscribed") }}
<h2>This is your home</h2>
<p>When you find a community that you like, click the <span class="join">join</span> button</p>
<p><a href="/{{.Host}}/search?searchtype=Communities">Click here</a> to find communities or <a href="/{{.Host}}?listingType=All&sort=TopDay">check out what's popular</a></p>
{{ else if or (and (not .Op) (not .Activities) (not .Comments) (not .Posts) (not .Communities)) (and (not .Comments) .PostID) (and (not .Activities) (not .Query) .User) }}
{{ if or (and (not .Op) (not .Activities) (not .Comments) (not .Posts) (not .Communities)) (and (not .Comments) .PostID) (and (not .Activities) (not .Query) .User) }}
<div class="error">there doesn't seem to be anything here</div>
{{ end }}
@ -46,7 +42,8 @@
</div>
{{ end }}
<script src="/_/static/utils.js"></script>
{{ template "sidebar.html" . }}
</main>
<script src="/_/static/utils.js"></script>
</body>
</html>

View file

@ -1,8 +1,9 @@
<!DOCTYPE html>
<head>
<title>{{ .Host }}: sign up or log in</title>
<title>{{ host .Host }}: sign up or log in</title>
<link rel="shortcut icon" href="/{{.Host}}/icon.jpg">
<link rel="stylesheet" href="/_/static/style.css">
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<nav>
@ -23,7 +24,7 @@
<img class="icon" src="{{ if .Site }}{{ .Site.SiteView.Site.Icon.String }}{{else}}/{{ .Host}}/icon.jpg{{end}}">
</a>
</div>
<span class="title"><a class="title" href="/{{.Host}}">{{.Host}}</a> - sign up or login</span>
<span class="title"><a class="title" href="/{{.Host}}">{{ host .Host}}</a> - sign up or login</span>
</nav>
{{ if .Alert }}
<div class="alert">

View file

@ -1,6 +1,7 @@
<!DOCTYPE html>
<head>
<title>{{if and .Posts .PostID }}{{ (index .Posts 0).Post.Name}} : {{.CommunityName}}{{else if and .Community (ne .Community.CommunityView.Community.Title "")}}{{.Community.CommunityView.Community.Title}}{{else if ne .CommunityName ""}}/c/{{.CommunityName}}{{ else if .User}}overview for {{.User.PersonView.Person.Name}}{{else}}{{ .Host }}{{end}}</title>
<title>{{if and .Posts .PostID }}{{ (index .Posts 0).Post.Name}} : {{.CommunityName}}{{else if and .Community (ne .Community.CommunityView.Community.Title "")}}{{.Community.CommunityView.Community.Title}}{{else if ne .CommunityName ""}}/c/{{.CommunityName}}{{ else if .User}}overview for {{.User.PersonView.Person.Name}}{{else}}{{ host .Host }}{{end}}</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="shortcut icon" href="/{{.Host}}/icon.jpg">
<link rel="stylesheet" href="/_/static/style.css">
</head>
@ -17,7 +18,6 @@
</noscript>
{{ template "nav.html" . -}}
{{ template "sidebar.html" . }}
{{ if or (contains .Sort "Top") (and (not .PostID) (not .User) (not .Community) (not .Activities) (eq .Op ""))}}
{{ template "menu.html" . }}
{{ end}}
@ -118,6 +118,7 @@
{{ end }}
<script src="/_/static/utils.js"></script>
{{ template "sidebar.html" . }}
</main>
</body>
</html>

View file

@ -1,4 +1,12 @@
<div class="menu">
listing:
<a href="{{ .ListBy "All"}}" {{if eq .Listing "All"}}class="selected"{{end}}>all</a>
<span>-</span>
<a href="{{ .ListBy "Local"}}" {{if eq .Listing "Local"}}class="selected"{{end}}>local</a>
{{ if .Session }}
<span>-</span>
<a href="{{ .ListBy "Subscribed"}}" {{if eq .Listing "Subscribed"}}class="selected"{{end}}>subscribed</a>
{{ end }}
{{ if contains .Sort "Top" }}
links from past:
<a {{ if eq .Sort "TopDay"}}class="selected"{{end}} href="{{ .SortBy "TopDay"}}">day</a>
@ -11,12 +19,5 @@
<span>-</span>
<a {{ if eq .Sort "TopAll"}}class="selected"{{end}} href="{{ .SortBy "TopAll"}}">all time</a>
{{ end }}
<a href="{{ .ListBy "All"}}" {{if eq .Listing "All"}}class="selected"{{end}}>everywhere</a>
<span>-</span>
<a href="{{ .ListBy "Local"}}" {{if eq .Listing "Local"}}class="selected"{{end}}>local</a>
{{ if .Session }}
<span>-</span>
<a href="{{ .ListBy "Subscribed"}}" {{if eq .Listing "Subscribed"}}class="selected"{{end}}>subscribed</a>
{{ end }}
</div>

View file

@ -11,6 +11,21 @@
{{ end }}
<a href="/{{$host}}/search?searchtype=Communities" class="more">more »</a>
</div>
<div class="right">
{{ if .Session }}
<a href="/{{.Host}}/u/{{ .Session.UserName}}">{{ .Session.UserName }}</a>
|
<a href="/{{.Host}}/inbox" class="mailbox{{ if .UnreadCount }} orangered{{end}}"></a>
|
<a href="/{{.Host}}/settings">settings</a>
|
<form method="POST"><input type="submit" name="op" value="logout"></form>
{{else}}
Want to join? <a href="/{{.Host}}/login">Log in</a> or <a href="/{{.Host}}/login">sign up</a> in seconds
|
<a href="/{{.Host}}/settings">settings</a>
{{end}}
</div>
<div class="spacer">
<a href="/{{ .Host}}/">
<img class="icon" src="{{ if .Site }}{{ .Site.SiteView.Site.Icon.String }}{{else}}/{{ .Host}}/icon.jpg{{end}}">
@ -21,7 +36,7 @@
{{ else if .User }}
<a class="title" href="/{{ .Host}}/u/{{fullname .User.PersonView.Person}}">{{fullname .User.PersonView.Person}}</a>
{{ else }}
<a class="title" href="/{{.Host}}">{{.Host}}</a>
<a class="title" href="/{{.Host}}">{{ host .Host}}</a>
{{- end -}}
{{- if eq .Op "create_post" "create_community" -}}
<span>: submit</span>
@ -49,15 +64,4 @@
{{ end }}
</ul>
{{ end }}
<div class="right">
{{ if .Session }}
<a href="/{{.Host}}/u/{{ .Session.UserName}}">{{ .Session.UserName }}</a>
|
<a href="/{{.Host}}/inbox" class="mailbox{{ if .UnreadCount }} orangered{{end}}"></a>
|
<form method="POST"><input type="submit" name="op" value="logout"></form>
{{else}}
Want to join? <a href="/{{.Host}}/login">Log in</a> or <a href="/{{.Host}}/login">sign up</a> in seconds
{{end}}
</div>
</nav>

View file

@ -1,10 +1,12 @@
{{ if not .State.XHR }}
<div class="post {{if .Post.Deleted}}deleted{{end}}" onclick="postClick(event)">
{{ if gt .Rank 0 }}
{{ if gt .Rank 0 }}
<div class="rank"> {{ .Rank }} </div>
{{ end }}
<div class="score">
{{ end }}
<div class="score {{ if lt .Rank 1 }}squish{{end}}{{ if eq .MyVote.String "1" }} like{{else if eq .MyVote.String "-1"}} dislike{{end}}">
{{ if .State.Session }}
<form class="link-btn" method="POST">
<form class="link-btn {{ if lt .Rank 1 }}squish{{end}}{{ if eq .MyVote.String "1" }} like{{else if eq .MyVote.String "-1"}} dislike{{end}}" method="POST" onsubmit="formSubmit(event)">
<input type="submit" name="vote" value="▲">
{{ if .MyVote.IsValid}}
<input type="hidden" name="undo" value="{{.MyVote.String}}">
@ -12,17 +14,18 @@
<input type="hidden" name="op" value="vote_post">
<input type="hidden" name="postid" value="{{.Post.ID }}">
<div>{{ .Counts.Score }}</div>
<input type="submit" value="▼">
<input type="submit" name="vote" value="▼">
</form>
{{ else }}
<div style="margin-top: 19px;">{{ .Counts.Score }}</div>
{{ end }}
{{ if not .State.XHR}}
</div>
<div class="thumb" style="background-image: url({{if .Post.ThumbnailURL.IsValid}}{{.Post.ThumbnailURL.String}}?format=jpg&thumbnail=96{{else if .Post.URL.IsValid}}/_/static/link.png{{else}}/_/static/text.png{{end}})"></div>
<div class="entry">
<div class="title">
<a href="{{ if .Post.URL.IsValid }}{{ .Post.URL }}{{ else }}/{{ .State.Host }}/post/{{ .Post.ID }}{{ end }}">{{ .Post.Name }}</a>
({{ host . }})
({{ domain . }})
</div>
{{ if or (and .Post.Body.IsValid (ne .Post.Body.String "")) (isImage .Post.URL.String) }}
<div class="expando-button {{ if eq .Rank 0}}open{{ end }}"></div>
@ -66,3 +69,4 @@
<div></div>
<div class="clearleft"></div>
</div>
{{ end }}

View file

@ -3,6 +3,7 @@
<title>mlmym</title>
<link rel="shortcut icon" href="/{{.Host}}/icon.jpg">
<link rel="stylesheet" href="/_/static/style.css">
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<nav>

View file

@ -105,7 +105,7 @@
{{ if and .Session (isMod .Community .Session.UserName) }}
MODERATOR TOOLS
<div class="moderators">
<a href="/{{.Host}}/c/{{.CommunityName}}?edit">community settings</a>
<a href="/{{.Host}}/c/{{.CommunityName}}/edit">community settings</a>
</div>
{{ end }}
{{ if .Community.Moderators }}