Add pagination on files and lists page
This commit is contained in:
10
Makefile
10
Makefile
@@ -1,9 +1,9 @@
|
|||||||
run:
|
run:
|
||||||
${MAKE} -j2 backgroundrun backgroundts
|
${MAKE} -j2 backgroundrun backgroundts
|
||||||
build:
|
build:
|
||||||
tsc res/static/res/typescript/lib/*.ts --outFile res/static/res/script/pixellib.js \
|
tsc res/static/typescript/lib/*.ts --outFile res/static/script/pixellib.js \
|
||||||
res/static/res/typescript/home/*.ts \
|
res/static/typescript/home/*.ts \
|
||||||
res/static/res/typescript/lib/*.ts --outFile res/static/res/script/home.js
|
res/static/typescript/lib/*.ts --outFile res/static/script/home.js
|
||||||
go build main.go -o pixeldrain-web
|
go build main.go -o pixeldrain-web
|
||||||
|
|
||||||
deps:
|
deps:
|
||||||
@@ -12,8 +12,8 @@ deps:
|
|||||||
backgroundrun:
|
backgroundrun:
|
||||||
go run main.go
|
go run main.go
|
||||||
backgroundts:
|
backgroundts:
|
||||||
tsc --watch --project res/static/res/typescript/home
|
tsc --watch --project res/static/typescript/home
|
||||||
--project res/static/res/typescript/textupload
|
--project res/static/typescript/textupload
|
||||||
|
|
||||||
docker:
|
docker:
|
||||||
go build -o docker/pixeldrain-web docker/main.go
|
go build -o docker/pixeldrain-web docker/main.go
|
||||||
|
@@ -86,7 +86,7 @@ body{
|
|||||||
.navigation .icon {
|
.navigation .icon {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@media screen and (max-width: 500px) {
|
@media screen and (max-width: 35em) {
|
||||||
.navigation a:not(:first-child) {display: none;}
|
.navigation a:not(:first-child) {display: none;}
|
||||||
.navigation a.icon {
|
.navigation a.icon {
|
||||||
float: right;
|
float: right;
|
||||||
@@ -163,7 +163,7 @@ a:hover {color: var(--highlight_color); text-decoration: underline;}
|
|||||||
table:not(.form) {border-collapse: collapse; width: 100%;}
|
table:not(.form) {border-collapse: collapse; width: 100%;}
|
||||||
tr:not(.form) {border-bottom: 1px var(--accent_color_medium_border) solid;}
|
tr:not(.form) {border-bottom: 1px var(--accent_color_medium_border) solid;}
|
||||||
tr > td {padding: 0.5em;}
|
tr > td {padding: 0.5em;}
|
||||||
@media(max-width: 28em) {
|
@media(max-width: 30em) {
|
||||||
tr > td {
|
tr > td {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
float: left;
|
float: left;
|
||||||
|
@@ -14,12 +14,12 @@
|
|||||||
<form onSubmit="return submitForm();" class="highlight_dark border_top border_bottom">
|
<form onSubmit="return submitForm();" class="highlight_dark border_top border_bottom">
|
||||||
<table class="form">
|
<table class="form">
|
||||||
<tr class="form">
|
<tr class="form">
|
||||||
<td>Username</td>
|
<td>Username / e-mail</td>
|
||||||
<td><input id="username" name="username" type="text" autocomplete="username" value=""/></td>
|
<td><input id="username" name="username" type="text" autocomplete="username" value="" class="form_input"/></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="form">
|
<tr class="form">
|
||||||
<td>Password</td>
|
<td>Password</td>
|
||||||
<td><input id="password" name="password" type="password" autocomplete="current-password"/></td>
|
<td><input id="password" name="password" type="password" autocomplete="current-password" class="form_input"/></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="form">
|
<tr class="form">
|
||||||
<td colspan=2 style="text-align: right;"><input type="submit" value="Login" class="button_highlight"/></td>
|
<td colspan=2 style="text-align: right;"><input type="submit" value="Login" class="button_highlight"/></td>
|
||||||
|
@@ -7,14 +7,30 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
{{template "menu" .}}
|
{{template "menu" .}}
|
||||||
<div class="highlight_dark border_bottom">
|
<div class="highlight_middle border_bottom">
|
||||||
These files were uploaded while logged in to your pixeldrain account,
|
These files were uploaded while logged in to your pixeldrain account,
|
||||||
<a href="/history">click here</a> to view files uploaded anonymously
|
<a href="/history">click here</a> to view files uploaded anonymously
|
||||||
in this browser.
|
in this browser.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{$limit := 100}}
|
||||||
|
{{$page := .URLQuery.Get "page" | pageNr}}
|
||||||
|
{{$files := .PixelAPI.UserFiles $page $limit}}
|
||||||
|
<div class="highlight_dark">
|
||||||
|
{{if ne $page 0}}
|
||||||
|
<a href="?page={{sub $page 4}}" class="button">🡄 4 Pages</a>
|
||||||
|
<a href="?page={{sub $page 2}}" class="button">⬅ 2 Pages</a>
|
||||||
|
<a href="?page={{sub $page 1}}" class="button button_highlight" style="margin-right: 2em;">← Last Page</a>
|
||||||
|
{{end}}
|
||||||
|
Page {{$page}}
|
||||||
|
{{if len $files.Files | eq $limit}}
|
||||||
|
<a href="?page={{add $page 1}}" class="button button_highlight" style="margin-left: 2em;">Next Page →</a>
|
||||||
|
<a href="?page={{add $page 2}}" class="button">2 Pages ⮕</a>
|
||||||
|
<a href="?page={{add $page 4}}" class="button">4 Pages 🡆</a>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
<br/>
|
<br/>
|
||||||
{{$files := .PixelAPI.UserFiles 0 1000}}
|
|
||||||
{{range $files.Files}}
|
{{range $files.Files}}
|
||||||
<a class="file_button" href="/u/{{.ID}}" target="_blank">
|
<a class="file_button" href="/u/{{.ID}}" target="_blank">
|
||||||
<img src="{{$.APIEndpoint}}/file/{{.ID}}/thumbnail" alt="{{.Name}}" />
|
<img src="{{$.APIEndpoint}}/file/{{.ID}}/thumbnail" alt="{{.Name}}" />
|
||||||
@@ -24,6 +40,21 @@
|
|||||||
</a>
|
</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<div class="highlight_dark">
|
||||||
|
{{if ne $page 0}}
|
||||||
|
<a href="?page={{sub $page 4}}" class="button">🡄 4 Pages</a>
|
||||||
|
<a href="?page={{sub $page 2}}" class="button">⬅ 2 Pages</a>
|
||||||
|
<a href="?page={{sub $page 1}}" class="button button_highlight" style="margin-right: 2em;">← Last Page</a>
|
||||||
|
{{end}}
|
||||||
|
Page {{$page}}
|
||||||
|
{{if len $files.Files | eq $limit}}
|
||||||
|
<a href="?page={{add $page 1}}" class="button button_highlight" style="margin-left: 2em;">Next Page →</a>
|
||||||
|
<a href="?page={{add $page 2}}" class="button">2 Pages ⮕</a>
|
||||||
|
<a href="?page={{add $page 4}}" class="button">4 Pages 🡆</a>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
|
||||||
{{template "analytics"}}
|
{{template "analytics"}}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@@ -22,7 +22,7 @@
|
|||||||
</a>
|
</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
<br/>
|
<br/>
|
||||||
<a href="/user/files">...All my files</a>
|
<a href="/user/files" class="button">...All my files</a>
|
||||||
</div>
|
</div>
|
||||||
<br/>
|
<br/>
|
||||||
<h2>Your most recently created lists:</h2>
|
<h2>Your most recently created lists:</h2>
|
||||||
@@ -38,6 +38,7 @@
|
|||||||
</a>
|
</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
<br/>
|
<br/>
|
||||||
|
<a href="/user/lists" class="button">...All my lists</a>
|
||||||
</div>
|
</div>
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|
||||||
|
56
res/template/account/user_lists.html
Normal file
56
res/template/account/user_lists.html
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
{{define "user_lists"}}<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
{{template "meta_tags" "Lists"}}
|
||||||
|
<script type="text/javascript">var apiEndpoint = '{{.APIEndpoint}}';</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
{{template "menu" .}}
|
||||||
|
{{$limit := 100}}
|
||||||
|
{{$page := .URLQuery.Get "page" | pageNr}}
|
||||||
|
{{$lists := .PixelAPI.UserLists $page $limit}}
|
||||||
|
<div class="highlight_dark">
|
||||||
|
{{if ne $page 0}}
|
||||||
|
<a href="?page={{sub $page 4}}" class="button">🡄 4 Pages</a>
|
||||||
|
<a href="?page={{sub $page 2}}" class="button">⬅ 2 Pages</a>
|
||||||
|
<a href="?page={{sub $page 1}}" class="button button_highlight" style="margin-right: 2em;">← Last Page</a>
|
||||||
|
{{end}}
|
||||||
|
Page {{$page}}
|
||||||
|
{{if len $lists.Lists | eq $limit}}
|
||||||
|
<a href="?page={{add $page 1}}" class="button button_highlight" style="margin-left: 2em;">Next Page →</a>
|
||||||
|
<a href="?page={{add $page 2}}" class="button">2 Pages ⮕</a>
|
||||||
|
<a href="?page={{add $page 4}}" class="button">4 Pages 🡆</a>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
{{range $lists.Lists}}
|
||||||
|
<a class="file_button" href="/l/{{.ID}}" target="_blank">
|
||||||
|
<img src="{{$.APIEndpoint}}/list/{{.ID}}/thumbnail" alt="{{.Title}}" />
|
||||||
|
<span style="color: var(--highlight_color);">{{.Title}}</span>
|
||||||
|
({{.FileCount}} Files)
|
||||||
|
<br/>
|
||||||
|
{{.DateCreated.Format "2006-01-02 15:04:05"}}
|
||||||
|
</a>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<div class="highlight_dark">
|
||||||
|
{{if ne $page 0}}
|
||||||
|
<a href="?page={{sub $page 4}}" class="button">🡄 4 Pages</a>
|
||||||
|
<a href="?page={{sub $page 2}}" class="button">⬅ 2 Pages</a>
|
||||||
|
<a href="?page={{sub $page 1}}" class="button button_highlight" style="margin-right: 2em;">← Last Page</a>
|
||||||
|
{{end}}
|
||||||
|
Page {{$page}}
|
||||||
|
{{if len $lists.Lists | eq $limit}}
|
||||||
|
<a href="?page={{add $page 1}}" class="button button_highlight" style="margin-left: 2em;">Next Page →</a>
|
||||||
|
<a href="?page={{add $page 2}}" class="button">2 Pages ⮕</a>
|
||||||
|
<a href="?page={{add $page 4}}" class="button">4 Pages 🡆</a>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{template "analytics"}}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{{end}}
|
@@ -2,6 +2,7 @@
|
|||||||
<div id="navigation" class="highlight_light border_top border_bottom navigation">
|
<div id="navigation" class="highlight_light border_top border_bottom navigation">
|
||||||
<a href="/">Home</a>
|
<a href="/">Home</a>
|
||||||
<a href="{{if .Authenticated}}/user/files{{else}}/history{{end}}">My Files</a>
|
<a href="{{if .Authenticated}}/user/files{{else}}/history{{end}}">My Files</a>
|
||||||
|
{{if .Authenticated}}<a href="/user/lists">My Lists</a>{{end}}
|
||||||
<a href="/api">API</a>
|
<a href="/api">API</a>
|
||||||
{{if .Authenticated}}<a href="/user">{{.Username}}</a>
|
{{if .Authenticated}}<a href="/user">{{.Username}}</a>
|
||||||
<a href="/logout" style="vertical-align: 0.6em; font-size: 0.9em; padding: 1px;">(Log out)</a>{{else}}
|
<a href="/logout" style="vertical-align: 0.6em; font-size: 0.9em; padding: 1px;">(Log out)</a>{{else}}
|
||||||
|
@@ -3,6 +3,7 @@ package webcontroller
|
|||||||
import (
|
import (
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"fornaxian.com/pixeldrain-web/pixelapi"
|
"fornaxian.com/pixeldrain-web/pixelapi"
|
||||||
@@ -17,8 +18,9 @@ type TemplateData struct {
|
|||||||
APIEndpoint template.URL
|
APIEndpoint template.URL
|
||||||
PixelAPI *pixelapi.PixelAPI
|
PixelAPI *pixelapi.PixelAPI
|
||||||
|
|
||||||
Other interface{}
|
Other interface{}
|
||||||
Title string
|
URLQuery url.Values
|
||||||
|
Title string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wc *WebController) newTemplateData(w http.ResponseWriter, r *http.Request) *TemplateData {
|
func (wc *WebController) newTemplateData(w http.ResponseWriter, r *http.Request) *TemplateData {
|
||||||
@@ -26,6 +28,7 @@ func (wc *WebController) newTemplateData(w http.ResponseWriter, r *http.Request)
|
|||||||
Authenticated: false,
|
Authenticated: false,
|
||||||
Username: "",
|
Username: "",
|
||||||
APIEndpoint: template.URL(wc.conf.APIURLExternal),
|
APIEndpoint: template.URL(wc.conf.APIURLExternal),
|
||||||
|
URLQuery: r.URL.Query(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if key, err := wc.getAPIKey(r); err == nil {
|
if key, err := wc.getAPIKey(r); err == nil {
|
||||||
|
@@ -4,11 +4,14 @@ import (
|
|||||||
"html/template"
|
"html/template"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Fornaxian/log"
|
"github.com/Fornaxian/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TemplateManager parses templates and provides utility functions to the
|
||||||
|
// templates' scripting language
|
||||||
type TemplateManager struct {
|
type TemplateManager struct {
|
||||||
templates *template.Template
|
templates *template.Template
|
||||||
|
|
||||||
@@ -18,6 +21,7 @@ type TemplateManager struct {
|
|||||||
debugModeEnabled bool
|
debugModeEnabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewTemplateManager creates a new template manager
|
||||||
func NewTemplateManager(templateDir, externalAPIEndpoint string, debugMode bool) *TemplateManager {
|
func NewTemplateManager(templateDir, externalAPIEndpoint string, debugMode bool) *TemplateManager {
|
||||||
return &TemplateManager{
|
return &TemplateManager{
|
||||||
templateDir: templateDir,
|
templateDir: templateDir,
|
||||||
@@ -72,17 +76,31 @@ func (tm *TemplateManager) funcMap() template.FuncMap {
|
|||||||
"bgPatternCount": tm.bgPatternCount,
|
"bgPatternCount": tm.bgPatternCount,
|
||||||
"debugMode": tm.debugMode,
|
"debugMode": tm.debugMode,
|
||||||
"apiUrl": tm.apiURL,
|
"apiUrl": tm.apiURL,
|
||||||
|
"pageNr": tm.pageNr,
|
||||||
|
"add": tm.add,
|
||||||
|
"sub": tm.sub,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *TemplateManager) bgPatternCount() uint8 {
|
func (tm *TemplateManager) bgPatternCount() uint8 {
|
||||||
return uint8(time.Now().UnixNano() % 17)
|
return uint8(time.Now().UnixNano() % 17)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *TemplateManager) debugMode() bool {
|
func (tm *TemplateManager) debugMode() bool {
|
||||||
return tm.debugModeEnabled
|
return tm.debugModeEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *TemplateManager) apiURL() string {
|
func (tm *TemplateManager) apiURL() string {
|
||||||
return tm.externalAPIEndpoint
|
return tm.externalAPIEndpoint
|
||||||
}
|
}
|
||||||
|
func (tm *TemplateManager) pageNr(s string) (nr int) {
|
||||||
|
// Atoi returns 0 on error, which is fine for page numbers
|
||||||
|
if nr, _ = strconv.Atoi(s); nr < 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return nr
|
||||||
|
}
|
||||||
|
func (tm *TemplateManager) add(a, b int) int {
|
||||||
|
return a + b
|
||||||
|
}
|
||||||
|
func (tm *TemplateManager) sub(a, b int) int {
|
||||||
|
return a - b
|
||||||
|
}
|
||||||
|
@@ -59,6 +59,7 @@ func New(r *httprouter.Router, prefix string, conf *conf.PixelWebConfig) *WebCon
|
|||||||
r.POST(p+"/logout" /* */, wc.serveLogout)
|
r.POST(p+"/logout" /* */, wc.serveLogout)
|
||||||
r.GET(p+"/user" /* */, wc.serveTemplate("user_home", true))
|
r.GET(p+"/user" /* */, wc.serveTemplate("user_home", true))
|
||||||
r.GET(p+"/user/files" /* */, wc.serveTemplate("user_files", true))
|
r.GET(p+"/user/files" /* */, wc.serveTemplate("user_files", true))
|
||||||
|
r.GET(p+"/user/lists" /* */, wc.serveTemplate("user_lists", true))
|
||||||
r.GET(p+"/user/filemanager" /**/, wc.serveTemplate("file_manager", true))
|
r.GET(p+"/user/filemanager" /**/, wc.serveTemplate("file_manager", true))
|
||||||
|
|
||||||
r.NotFound = http.HandlerFunc(wc.serveNotFound)
|
r.NotFound = http.HandlerFunc(wc.serveNotFound)
|
||||||
|
Reference in New Issue
Block a user