Add categories page

This commit is contained in:
Joey Payne 2019-01-25 16:40:04 -07:00 • committed by Andreas Rumpf
commit 9d19d70558
6 changed files with 187 additions and 41 deletions

View file

@ -22,6 +22,7 @@ table th {
// Custom styles.
// - Navigation bar.
$navbar-height: 60px;
$default-category-color: #98c766;
$logo-height: $navbar-height - 20px;
.navbar-button {
@ -166,6 +167,33 @@ $logo-height: $navbar-height - 20px;
}
}
.thread-list {
@extend .container;
@extend .grid-xl;
}
.category-list {
@extend .thread-list;
.category-title {
@extend .thread-title;
a, a:hover {
color: lighten($body-font-color, 10%);
text-decoration: none;
}
}
.category-description {
opacity: 0.6;
}
}
#categories-list .category {
border-left: 6px solid;
border-left-color: $default-category-color;
}
$super-popular-color: #f86713;
$popular-color: darken($super-popular-color, 25%);
$threads-meta-color: #545d70;
@ -217,7 +245,7 @@ $threads-meta-color: #545d70;
.category-color {
width: 0;
height: 0;
border: 0.3rem solid #98c766;
border: 0.3rem solid $default-category-color;
display: inline-block;
margin-right: 5px;
}
@ -726,8 +754,3 @@ hr {
margin-top: $control-padding-y*2;
}
}
// - Hide features that have not been implemented yet.
#main-buttons > section.navbar-section:nth-child(1) {
display: none;
}

View file

@ -806,14 +806,28 @@ routes:
var
start = getInt(@"start", 0)
count = getInt(@"count", 30)
categoryId = getInt(@"categoryId", -1)
var
categorySection = ""
categoryArgs: seq[string] = @[$start, $count]
countQuery = sql"select count(*) from thread;"
countArgs: seq[string] = @[]
if categoryId != -1:
categorySection = "c.id == ? and "
countQuery = sql"select count(*) from thread t, category c where category == c.id and c.id == ?;"
countArgs.add($categoryId)
categoryArgs.insert($categoryId, 0)
const threadsQuery =
sql"""select t.id, t.name, views, strftime('%s', modified), isLocked,
"""select t.id, t.name, views, strftime('%s', modified), isLocked,
c.id, c.name, c.description, c.color,
u.name, u.email, strftime('%s', u.lastOnline),
strftime('%s', u.previousVisitAt), u.status, u.isDeleted
from thread t, category c, person u
where t.isDeleted = 0 and category = c.id and
where t.isDeleted = 0 and category = c.id and $#
u.status <> 'Spammer' and u.status <> 'Troll' and
u.id in (
select u.id from post p, person u
@ -823,11 +837,11 @@ routes:
)
order by modified desc limit ?, ?;"""
let thrCount = getValue(db, sql"select count(*) from thread;").parseInt()
let thrCount = getValue(db, countQuery, countArgs).parseInt()
let moreCount = max(0, thrCount - (start + count))
var list = ThreadList(threads: @[], moreCount: moreCount)
for data in getAllRows(db, threadsQuery, start, count):
for data in getAllRows(db, sql(threadsQuery % categorySection), categoryArgs):
let thread = selectThread(data[0 .. 8], selectUser(data[9 .. ^1]))
list.threads.add(thread)

View file

@ -0,0 +1,83 @@
import strformat, times, options, json, httpcore, sugar, strutils, uri
import category
when defined(js):
include karax/prelude
import karax / [vstyles, kajax, kdom]
import karaxutils, error, user, mainbuttons
type
State = ref object
list: Option[CategoryList]
loading: bool
status: HttpCode
proc newState(): State =
State(
list: none[CategoryList](),
loading: false,
status: Http200
)
var
state = newState()
proc genCategory(category: Category, noBorder = false): VNode =
result = buildHtml():
tr(class=class({"no-border": noBorder})):
td(style=style((StyleAttr.borderLeftColor, kstring("#" & category.color))), class="category"):
h4(class="category-title"):
a(href=makeUri("/c/" & $category.id)):
tdiv():
tdiv(class="category-name"):
text category.name
tdiv(class="category-description"):
text category.description
td(class="topics"):
text "Topics"
proc onCategoriesRetrieved(httpStatus: int, response: kstring) =
state.loading = false
state.status = httpStatus.HttpCode
if state.status != Http200: return
let parsed = parseJson($response)
let list = to(parsed, CategoryList)
if state.list.isSome:
state.list.get().categories.add(list.categories)
else:
state.list = some(list)
proc renderCategories(): VNode =
if state.status != Http200:
return renderError("Couldn't retrieve threads.", state.status)
if state.list.isNone:
if not state.loading:
state.loading = true
ajaxGet(makeUri("categories.json"), @[], onCategoriesRetrieved)
return buildHtml(tdiv(class="loading loading-lg"))
let list = state.list.get()
return buildHtml():
section(class="category-list"):
table(id="categories-list", class="table"):
thead():
tr:
th(text "Category")
th(text "Topics")
tbody():
for i in 0 ..< list.categories.len:
let category = list.categories[i]
let isLastCategory = i+1 == list.categories.len
genCategory(category, noBorder=isLastCategory)
proc renderCategoryList*(currentUser: Option[User]): VNode =
result = buildHtml(tdiv):
renderMainButtons(currentUser)
renderCategories()

View file

@ -5,6 +5,7 @@ include karax/prelude
import jester/[patterns]
import threadlist, postlist, header, profile, newthread, error, about
import categorylist
import resetpassword, activateemail, search
import karaxutils
@ -81,6 +82,14 @@ proc render(): VNode =
result = buildHtml(tdiv()):
renderHeader()
route([
r("/categories",
(params: Params) =>
(renderCategoryList(getLoggedInUser()))
),
r("/c/@id",
(params: Params) =>
(renderThreadList(getLoggedInUser(), params["id"].parseInt))
),
r("/newthread",
(params: Params) =>
(render(state.newThread, getLoggedInUser()))

View file

@ -0,0 +1,37 @@
import options
import user
when defined(js):
include karax/prelude
import karax / [vstyles, kajax, kdom]
import karaxutils, error, user
let buttons = [
(name: "Latest", url: makeUri("/"), id: "latest-btn"),
(name: "Categories", url: makeUri("/categories"), id: "categories-btn"),
]
proc renderMainButtons*(currentUser: Option[User]): VNode =
result = buildHtml():
section(class="navbar container grid-xl", id="main-buttons"):
section(class="navbar-section"):
#[tdiv(class="dropdown"):
a(href="#", class="btn dropdown-toggle"):
text "Filter "
italic(class="fas fa-caret-down")
ul(class="menu"):
li: text "community"
li: text "dev" ]#
for btn in buttons:
let active = btn.url == window.location.href
a(id=btn.id, href=btn.url):
button(class=class({"btn-primary": active, "btn-link": not active}, "btn")):
text btn.name
section(class="navbar-section"):
if currentUser.isSome():
a(id="new-thread-btn", href=makeUri("/newthread"), onClick=anchorCB):
button(class="btn btn-secondary"):
italic(class="fas fa-plus")
text " New Thread"

View file

@ -29,7 +29,7 @@ when defined(js):
include karax/prelude
import karax / [vstyles, kajax, kdom]
import karaxutils, error, user
import karaxutils, error, user, mainbuttons
type
State = ref object
@ -65,27 +65,6 @@ when defined(js):
return true
proc genTopButtons(currentUser: Option[User]): VNode =
result = buildHtml():
section(class="navbar container grid-xl", id="main-buttons"):
section(class="navbar-section"):
tdiv(class="dropdown"):
a(href="#", class="btn dropdown-toggle"):
text "Filter "
italic(class="fas fa-caret-down")
ul(class="menu"):
li: text "community"
li: text "dev"
button(class="btn btn-primary"): text "Latest"
button(class="btn btn-link"): text "Most Active"
button(class="btn btn-link"): text "Categories"
section(class="navbar-section"):
if currentUser.isSome():
a(id="new-thread-btn", href=makeUri("/newthread"), onClick=anchorCB):
button(class="btn btn-secondary"):
italic(class="fas fa-plus")
text " New Thread"
proc genUserAvatars(users: seq[User]): VNode =
result = buildHtml(td):
for user in users:
@ -168,10 +147,10 @@ when defined(js):
else:
state.list = some(list)
proc onLoadMore(ev: Event, n: VNode) =
proc onLoadMore(ev: Event, n: VNode, categoryId: int) =
state.loading = true
let start = state.list.get().threads.len
ajaxGet(makeUri("threads.json?start=" & $start), @[], onThreadList)
ajaxGet(makeUri("threads.json?start=" & $start & "&categoryId=" & $categoryId), @[], onThreadList)
proc getInfo(
list: seq[Thread], i: int, currentUser: Option[User]
@ -196,20 +175,20 @@ when defined(js):
isNew: thread.creation > previousVisitAt
)
proc genThreadList(currentUser: Option[User]): VNode =
proc genThreadList(currentUser: Option[User], categoryId: int): VNode =
if state.status != Http200:
return renderError("Couldn't retrieve threads.", state.status)
if state.list.isNone:
if not state.loading:
state.loading = true
ajaxGet(makeUri("threads.json"), @[], onThreadList)
ajaxGet(makeUri("threads.json?categoryId=" & $categoryId), @[], onThreadList)
return buildHtml(tdiv(class="loading loading-lg"))
let list = state.list.get()
result = buildHtml():
section(class="container grid-xl"): # TODO: Rename to `.thread-list`.
section(class="thread-list"):
table(class="table", id="threads-list"):
thead():
tr:
@ -239,10 +218,11 @@ when defined(js):
td(colspan="6"):
tdiv(class="loading loading-lg")
else:
td(colspan="6", onClick=onLoadMore):
td(colspan="6",
onClick = (ev: Event, n: VNode) => (onLoadMore(ev, n, categoryId))):
span(text "load more threads")
proc renderThreadList*(currentUser: Option[User]): VNode =
proc renderThreadList*(currentUser: Option[User], categoryId = -1): VNode =
result = buildHtml(tdiv):
genTopButtons(currentUser)
genThreadList(currentUser)
renderMainButtons(currentUser)
genThreadList(currentUser, categoryId)