summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bucky/app/api.js25
-rw-r--r--bucky/app/bucky.js64
-rw-r--r--bucky/app/pages.js11
-rw-r--r--bucky/db/index.js36
-rw-r--r--fortune/titles25
-rw-r--r--package.json3
-rw-r--r--public/assets/css/bucky.css79
-rw-r--r--public/assets/js/lib/router.js14
-rw-r--r--public/assets/js/lib/views/details/comments.js8
-rw-r--r--public/assets/js/lib/views/details/files.js5
-rw-r--r--public/assets/js/lib/views/keywords/keywords.js13
-rw-r--r--public/assets/js/lib/views/users/users.js88
-rw-r--r--public/assets/js/util/color.js16
-rw-r--r--public/assets/js/util/format.js57
-rw-r--r--sdk/auth.js1
-rw-r--r--sdk/index.js28
-rw-r--r--views/pages/keywords.ejs12
-rw-r--r--views/pages/users.ejs139
-rw-r--r--views/partials/hootbox.ejs2
-rw-r--r--views/partials/scripts.ejs2
-rw-r--r--views/partials/threads.ejs1
21 files changed, 584 insertions, 45 deletions
diff --git a/bucky/app/api.js b/bucky/app/api.js
index 75cdbd7..d2472c3 100644
--- a/bucky/app/api.js
+++ b/bucky/app/api.js
@@ -34,6 +34,19 @@ function route (app){
function(req, res){
res.json(util.sanitizeUser(res.user))
})
+ app.get("/api/users",
+ middleware.ensureAuthenticated,
+ bucky.ensureUserlist,
+ bucky.ensureUserThreadCounts,
+ bucky.ensureUserFileCounts,
+ bucky.ensureUserCommentCounts,
+ bucky.ensureUserStatistics,
+ function(req, res) {
+ res.json({
+ users: res.users,
+ userStats: res.userStats,
+ })
+ })
app.get("/api/profile/:username",
middleware.ensureAuthenticated,
bucky.ensureUser,
@@ -153,6 +166,16 @@ function route (app){
function(req, res){
res.send({ status: 'ok' })
})
+ app.get("/api/thread/:id/bury",
+ middleware.ensureAuthenticated,
+ bucky.ensureThread,
+ privacy.checkThreadPrivacy,
+ bucky.buryThread,
+ function(req, res){
+ res.json({
+ thread: res.thread,
+ })
+ })
/* comments */
@@ -259,9 +282,11 @@ function route (app){
middleware.ensureAuthenticated,
bucky.ensureKeywords,
bucky.ensureThreadGroups,
+ bucky.ensureLatestKeywordThreads,
function(req, res){
res.json({
keywords: res.keywords,
+ threads: res.threads,
threadGroups: res.threadGroups,
})
})
diff --git a/bucky/app/bucky.js b/bucky/app/bucky.js
index 8d9839f..ab153f9 100644
--- a/bucky/app/bucky.js
+++ b/bucky/app/bucky.js
@@ -227,6 +227,10 @@ var bucky = module.exports = {
next()
})
},
+ buryThread: function (req, res, next){
+ res.thread.set('lastmodified', util.now() - (14 * 86400))
+ res.thread.save().then( () => next() )
+ },
// ensureInterestedUsers: function(req, res, next){
// // given a thread, find people who might be interested in it
// // - other people who have been in threads with you
@@ -296,6 +300,12 @@ var bucky = module.exports = {
next()
})
},
+ ensureLatestKeywordThreads: function (req, res, next){
+ db.getLatestKeywordThreads().then(function(threads){
+ res.threads = threads
+ next()
+ })
+ },
ensureThreadGroups: function (req, res, next){
db.getThreadGroups().then(function(threadGroups){
res.threadGroups = threadGroups
@@ -571,6 +581,60 @@ var bucky = module.exports = {
}
})
},
+ ensureUserlist: function (req, res, next){
+ db.getUsers().then(function(users){
+ if (! users) {
+ return res.sendStatus(404)
+ }
+ res.users = users
+ next()
+ })
+ },
+ ensureUserThreadCounts: function (req, res, next) {
+ db.getUserThreadCounts().then(function(counts){
+ if (!counts) {
+ return res.sendStatus(404)
+ }
+ res.threadCounts = counts
+ next()
+ })
+ },
+ ensureUserCommentCounts: function (req, res, next) {
+ db.getUserCommentCounts().then(function(counts){
+ if (!counts) {
+ return res.sendStatus(404)
+ }
+ res.commentCounts = counts
+ next()
+ })
+ },
+ ensureUserFileCounts: function (req, res, next) {
+ db.getUserFileCounts().then(function(counts){
+ if (!counts) {
+ return res.sendStatus(404)
+ }
+ res.fileCounts = counts
+ next()
+ })
+ },
+ ensureUserStatistics: function (req, res, next) {
+ var stats = {}
+ res.threadCounts.forEach(function(user){
+ stats[user.username] = stats[user.username] || {}
+ stats[user.username].threads = user.count
+ })
+ res.commentCounts.forEach(function(user){
+ stats[user.username] = stats[user.username] || {}
+ stats[user.username].comments = user.count
+ })
+ res.fileCounts.forEach(function(user){
+ stats[user.username] = stats[user.username] || {}
+ stats[user.username].files = user.count
+ stats[user.username].fileSize = user.size
+ })
+ res.userStats = stats
+ next()
+ },
sanitizeUser: function(req, res, next) {
res.user = util.sanitizeUser(res.user)
next()
diff --git a/bucky/app/pages.js b/bucky/app/pages.js
index 7f666be..5d8551e 100644
--- a/bucky/app/pages.js
+++ b/bucky/app/pages.js
@@ -73,6 +73,17 @@ function route (app){
res.render("pages/profile_form", {title: "edit your profile"})
})
+ app.get("/users",
+ middleware.ensureAuthenticated,
+ function(req, res){
+ res.render("pages/users", {})
+ })
+ app.get("/users/all",
+ middleware.ensureAuthenticated,
+ function(req, res){
+ res.render("pages/users", {})
+ })
+
app.get("/search/",
middleware.ensureAuthenticated,
function(req, res){
diff --git a/bucky/db/index.js b/bucky/db/index.js
index f454c92..312ca13 100644
--- a/bucky/db/index.js
+++ b/bucky/db/index.js
@@ -55,8 +55,13 @@ db.createUser = function(data){
}
db.getUsers = function () {
return User.query(function(qb){
- qb.orderBy("id", "desc")
- }).fetchAll()
+ qb.orderBy("username", "asc")
+ }).fetchAll({
+ columns: [
+ "id", "username", "realname", "firstseen", "lastseen",
+ "location", "website", "avatar",
+ ]
+ })
}
db.getUser = function(id) {
var model = new User({'id': id})
@@ -143,6 +148,15 @@ db.getThreadUsers = function(thread_id){
db.getUserThreadIds = function(user_id){
return ThreadUser.query("where", "user_id", "=", user_id).fetch()
}
+db.getUserThreadCounts = function(ids){
+ return knex.column('username').count('* as count').select().from('threads').groupBy('username')
+}
+db.getUserCommentCounts = function(ids){
+ return knex.column('username').count('* as count').select().from('comments').groupBy('username')
+}
+db.getUserFileCounts = function(ids){
+ return knex.column('username').sum('size as size').count('* as count').select().from('files').groupBy('username')
+}
/* FILES */
@@ -281,8 +295,24 @@ db.getKeyword = function (keyword) {
return Keyword.query("where", "keyword", "=", keyword).fetch()
}
db.getThreadGroups = function (keyword) {
- return knex.column('keyword').sum('viewed').as('viewed').count('*').as('count').column('id').column('title').column('lastmodified').column('privacy').select().from('threads').groupBy('keyword')
+ return (
+ knex.column('keyword')
+ .sum('viewed').as('viewed')
+ .count('*').as('count')
+ .select().from('threads').groupBy('keyword')
+ )
+}
+db.getLatestKeywordThreads = function (keyword) {
+ var ids = knex('threads').max('id').groupBy('keyword')
+ return (
+ knex.select('id', 'keyword', 'title', 'lastmodified', 'privacy').from('threads').where('id', 'in', ids)
+ )
}
+ // .column('id')
+ // .column('title')
+ // .column('lastmodified')
+ // .column('privacy')
+
db.createKeyword = function(data){
return new db.Keyword(data).save()
}
diff --git a/fortune/titles b/fortune/titles
index 1a68d5e..d682a14 100644
--- a/fortune/titles
+++ b/fortune/titles
@@ -397,8 +397,31 @@ bucky pulls up the traps
bucky casts the line
bucky leaps from the high rock
bucky crisps up some smores
-bucky's raw vegan rhapsody
+bucky's vegan rhapsody
bucky's new attitude
bucky has so many ideas
bucky welcomes you
bucky's friends forever
+bucky's pedal board
+bucky layers the chorus
+bucky hides in tall grass
+bucky tweaks the filter
+bucky's no scope melee
+bucky fills the table
+bucky's cable salad
+enough detune on there, bucky?
+bucky tweezes the filter
+bucky opens the hat
+bucky rides the cymbal
+bucky's midi masterpiece
+bucky solders the pots
+bucky's intricate drum line
+bucky's reverse polyphony
+bucky shreds in 7/8
+bucky's vegan delicacies
+bucky's raw delight
+bucky commmunicates with lobsters
+bucky's snake adventure
+bucky accounts for bloom
+bucky's attack release
+bucky refuses to wing it
diff --git a/package.json b/package.json
index 0ff9faf..97e5b9f 100644
--- a/package.json
+++ b/package.json
@@ -21,7 +21,6 @@
"berkeleydb": "^0.2.1",
"body-parser": "^1.19.0",
"bookshelf": "^0.13.3",
- "connect-mongo": "^2.0.3",
"connect-redis": "^5.0.0",
"cookie-parser": "^1.4.4",
"csurf": "^1.10.0",
@@ -35,7 +34,6 @@
"knox": "^0.9.2",
"lodash": "^4.17.15",
"mock-express": "^1.3.0",
- "mongodb": "^2.2.36",
"multer": "^1.4.2",
"multiparty": "^4.2.1",
"mysql2": "^0.15.8",
@@ -44,6 +42,7 @@
"passport": "^0.3.0",
"passport-local": "^1.0.0",
"redis": "^3.0.2",
+ "pm2": "^3.5.1",
"serve-favicon": "^2.5.0",
"sessionstore": "^1.3.5",
"skipper": "^0.8.7",
diff --git a/public/assets/css/bucky.css b/public/assets/css/bucky.css
index 9c9aa8a..bc35ddd 100644
--- a/public/assets/css/bucky.css
+++ b/public/assets/css/bucky.css
@@ -7,6 +7,7 @@ html {
min-height: 100%;
margin: 0;
padding: 0;
+ margin-bottom: 150px;
}
body {
min-width: 100%;
@@ -17,7 +18,7 @@ body {
font-size: 10px;
font-family: Trebuchet MS, Helvetica, Arial, sans-serif;
margin: 0;
- padding: 20px 30px 200px 30px;
+ padding: 20px 30px 30px 30px;
transition: opacity 100ms;
}
body.loading {
@@ -507,6 +508,15 @@ code br,
pre br {
display: none;
}
+tt, kbd {
+ max-width: 580px;
+ display: inline-block;
+ font-family: Menlo, monospace;
+ background: #fefefe;
+ border: 1px solid #ddd;
+ padding: 2px;
+ font-size: 11px;
+}
#thread_form form {
width: 530px;
@@ -1244,3 +1254,70 @@ audio {
padding-right: 2px;
}
}
+@media (prefers-color-scheme: dark) {
+ body {
+ background: #180808;
+ color: #f8f8f8;
+ }
+ button:not(.hoot), input[type=submit] {
+ color: #f8f8f8;
+ border: 2px #eee solid;
+ padding: 2px;
+ margin: 3px;
+ background-color: #18202c;
+ text-transform: uppercase;
+ cursor: pointer;
+ }
+ .desktop button:not(.hoot):hover,
+ .desktop input[type=submit] {
+ color: #fff;
+ background-color: #28303c;
+ }
+ hr {
+ border-color: #fff;
+ }
+ a.headline:link,
+ a.headline:visited { color: #eee; text-decoration: none; }
+ .desktop a.headline:hover { color: #fff; text-decoration: none; }
+ .ledger .row td:nth-child(1) a { color: #ddd; }
+ #threads .keyword td a { color: #ddd; text-decoration: none; }
+ .desktop #threads .keyword td a:hover { color: #fff; text-decoration: underline; }
+ #threads .keyword td b a { color: #ddd; }
+ .desktop #threads .keyword td b a:hover { color: #fff; }
+ .subtitle a { color: #eee; text-decoration: none; }
+ .desktop.subtitle a:hover { color: #fff; text-decoration: underline; }
+ .ledger .new { color: #ffffff; font-weight: bold; }
+ .ledger .recent { color: #f8f0f0; }
+ .ledger .med { color: #e8e0e0; }
+ .ledger .old { color: #d8d0d0; }
+ .ledger .older { color: #b8b0b0; }
+ .ledger .quiet { color: #a7a4a4; }
+
+ .files .new { color: #000000; }
+ .files .recent { color: #001111; }
+ .files .med { color: #203838; }
+ .files .old { color: #425050; }
+ .files .older { color: #5D6464; }
+ .files .quiet { color: #787878; }
+ .files .total a { color: #fff; }
+
+ .bluebox { box-shadow: 0 0px 1.5px rgba(238,238,255,1.0), 0 0px 1.5px rgba(238,238,255,1.0); }
+ .search_form input[type='text']:focus {
+ border-bottom-color: #888;
+ }
+ .search_form input[type='text']:invalid {
+ caret-color: #888;
+ }
+ #comment_form textarea.empty {
+ border-bottom-color: #fff;
+ }
+ #comment_form textarea.empty::placeholder {
+ color: #fff;
+ }
+ #search {
+ color: #eee;
+ }
+ #search b {
+ color: #fff;
+ }
+}
diff --git a/public/assets/js/lib/router.js b/public/assets/js/lib/router.js
index b6eff73..ebdfa78 100644
--- a/public/assets/js/lib/router.js
+++ b/public/assets/js/lib/router.js
@@ -22,7 +22,9 @@ var SiteRouter = Router.extend({
"/mail/compose/:username": 'compose',
"/mail/read/:id": 'message',
"/mail/reply/:id": 'compose',
- "/profile": 'profile',
+ "/users": 'users',
+ "/users/all": 'usersAll',
+ "/profile": 'profile',
"/profile/:username": 'profile',
"/profile/:username/edit": 'editProfile',
"/adminz": 'adminz',
@@ -85,6 +87,16 @@ var SiteRouter = Router.extend({
app.view.load(keyword || "")
},
+ users: function(username){
+ app.view = new UsersView ()
+ app.view.load()
+ },
+
+ usersAll: function(username){
+ app.view = new UsersView ({ all: true })
+ app.view.load()
+ },
+
profile: function(username){
app.view = new ProfileView ()
app.view.load(username || auth.user.username)
diff --git a/public/assets/js/lib/views/details/comments.js b/public/assets/js/lib/views/details/comments.js
index b1c86bb..1e8d127 100644
--- a/public/assets/js/lib/views/details/comments.js
+++ b/public/assets/js/lib/views/details/comments.js
@@ -14,8 +14,8 @@ var CommentsView = FormView.extend({
this.$formRow = this.$("#comment_form")
},
- load: function(comments, thread){
-console.log(comments)
+ load: function(comments, thread) {
+ // console.log(comments)
thread = this.thread = thread || this.thread
if (thread.settings.hootbox) {
comments
@@ -35,13 +35,13 @@ console.log(comments)
}
},
- parse: function(comment){
+ parse: function(comment) {
if (! comment.comment.length) return $('')
var datetime = verbose_date(comment.date, true)
var t = this.template.replace(/{{image}}/g, profile_image(comment.username))
.replace(/{{username}}/g, comment.username)
.replace(/{{id}}/g, comment.id)
- .replace(/{{comment}}/g, tidy_urls(comment.comment))
+ .replace(/{{comment}}/g, function(){ return tidy_urls(comment.comment) })
.replace(/{{date}}/g, datetime[0])
.replace(/{{time}}/g, datetime[1])
var $t = $(t)
diff --git a/public/assets/js/lib/views/details/files.js b/public/assets/js/lib/views/details/files.js
index 44c65c4..98b9350 100644
--- a/public/assets/js/lib/views/details/files.js
+++ b/public/assets/js/lib/views/details/files.js
@@ -171,10 +171,15 @@ var FilesView = FormView.extend({
if (dateClass && this.reverse) {
dateClass += ' italic'
}
+ var sizeClass = this.sort === 'size' ? 'bold' : ''
+ if (sizeClass && this.reverse) {
+ sizeClass += ' italic'
+ }
var t = this.templateTotal.replace(/{{size_class}}/g, size[0])
.replace(/{{size}}/g, size[1])
.replace(/{{nameClass}}/g, nameClass)
.replace(/{{dateClass}}/g, dateClass)
+ .replace(/{{sizeClass}}/g, sizeClass)
this.$el.append(t)
},
diff --git a/public/assets/js/lib/views/keywords/keywords.js b/public/assets/js/lib/views/keywords/keywords.js
index 12bd5a8..c5a9491 100644
--- a/public/assets/js/lib/views/keywords/keywords.js
+++ b/public/assets/js/lib/views/keywords/keywords.js
@@ -19,16 +19,21 @@ var KeywordsView = View.extend({
populate: function(data){
// console.log(data)
var keywordThreads = {}
- data.threadGroups.forEach(kw => {
+ var keywordStats = {}
+ data.threads.forEach(kw => {
keywordThreads[kw.keyword] = kw
})
+ data.threadGroups.forEach(kw => {
+ keywordStats[kw.keyword] = kw
+ })
data.keywords
.map(a => [parseInt((keywordThreads[a.keyword] || {})['sum(`viewed`)']) || 0, a])
.sort((b,a) => cmp(a[0], b[0]))
.map(a => a[1])
.forEach(keyword => {
var thread = keywordThreads[keyword.keyword.toLowerCase()] || {
- title: '',
+ }
+ var stats = keywordStats[keyword.keyword.toLowerCase()] || {
}
// {
// keyword: "warez",
@@ -38,9 +43,9 @@ var KeywordsView = View.extend({
// lastmodified: 1192401724
// },
// console.log(keyword, thread)
- var viewed = thread['sum(`viewed`)']
+ var viewed = stats['sum(`viewed`)']
var views = viewed ? hush_views(viewed) : ['','']
- var threadCountNum = thread['count(*)']
+ var threadCountNum = stats['count(*)']
var threadCount = threadCountNum ? hush_threads(threadCountNum) : ['','']
var dot = privacy_dot(thread.privacy)
var datetime = verbose_date(keyword.createdate)
diff --git a/public/assets/js/lib/views/users/users.js b/public/assets/js/lib/views/users/users.js
new file mode 100644
index 0000000..b7e6450
--- /dev/null
+++ b/public/assets/js/lib/views/users/users.js
@@ -0,0 +1,88 @@
+var UsersView = View.extend({
+
+ el: "#user_list",
+
+ events: {
+ },
+
+ action: "/api/users",
+
+ initialize: function(opt){
+ opt = opt || {}
+ this.showAll = !!opt.all
+ this.template = this.$(".template").html()
+ this.form = new NewKeywordForm ({ parent: this })
+ if (!this.showAll) {
+ $('.all_link').attr('href', '/users/all').html('Show all users')
+ } else {
+ $('.all_link').attr('href', '/users').html('Show active users')
+ }
+ },
+
+ load: function(){
+ $.get(this.action, this.populate.bind(this))
+ },
+
+ populate: function(data){
+ console.log(data)
+ // var keywordThreads = {}
+ // data.threadGroups.forEach(kw => {
+ // keywordThreads[kw.keyword] = kw
+ // })
+ var showAll = this.showAll
+ var userStats = data.userStats
+ data.users
+ // .map(a => [parseInt((keywordThreads[a.keyword] || {})['sum(`viewed`)']) || 0, a])
+ // .sort((b,a) => cmp(a[0], b[0]))
+ // .map(a => a[1])
+ .map(user => {
+ // var user = users[user.username.toLowerCase()] || return
+ // var viewed = thread['sum(`viewed`)']
+ // var views = viewed ? hush_views(viewed) : ['','']
+ // var threadCountNum = thread['count(*)']
+ // var threadCount = threadCountNum ? hush_threads(threadCountNum) : ['','']
+ // "id", "username", "realname", "firstseen", "lastseen",
+ // "location", "website", "avatar",
+ var stats = userStats[user.username] || {}
+ if (!showAll && !stats.threads && !stats.files && !stats.comments) {
+ return
+ }
+ var threadCount = stats.threads ? hush_threads(stats.threads) : ['','']
+ var fileCount = stats.files ? hush_null(stats.files, 'f') : ['','']
+ var fileSize = stats.files ? hush_size(stats.fileSize) : ['','']
+ var commentCount = stats.comments ? hush_null(stats.comments, 'c') : ['','']
+ var firstseen_datetime = verbose_date(user.firstseen)
+ var lastseen = get_age(user.lastseen, true)
+ var avatar = profile_image(user.username)
+ var t = this.template
+ .replace(/{{username}}/g, sanitizeHTML(user.username))
+ .replace(/{{id}}/g, user.id)
+ .replace(/{{avatar}}/g, user.avatar)
+ .replace(/{{location}}/g, sanitizeHTML(user.location))
+ .replace(/{{realname}}/g, sanitizeHTML(user.realname))
+ .replace(/{{firstseen_date}}/g, firstseen_datetime[0])
+ .replace(/{{firstseen_time}}/g, firstseen_datetime[1])
+ .replace(/{{firstseen_date_class}}/g, carbon_date(user.firstseen) )
+ .replace(/{{lastseen}}/g, lastseen )
+ .replace(/{{lastseen_date_class}}/g, carbon_date(lastseen) )
+ .replace(/{{threadcount}}/, threadCount[1])
+ .replace(/{{threadcount_class}}/, threadCount[0])
+ .replace(/{{filecount}}/, fileCount[1])
+ .replace(/{{filecount_class}}/, fileCount[0])
+ .replace(/{{filesize}}/, fileSize[1])
+ .replace(/{{filesize_class}}/, fileSize[0])
+ .replace(/{{commentcount}}/, commentCount[1])
+ .replace(/{{commentcount_class}}/, commentCount[0])
+ var $t = $(t)
+ if (!user.firstseen) {
+ $t.find('.date').html('')
+ }
+ // if (!user.avatar) {
+ // $t.find('.avatar').addClass('hidden')
+ // }
+ this.$el.append($t)
+ })
+ $("body").removeClass('loading')
+ },
+
+})
diff --git a/public/assets/js/util/color.js b/public/assets/js/util/color.js
index 6d33a72..ec21925 100644
--- a/public/assets/js/util/color.js
+++ b/public/assets/js/util/color.js
@@ -65,6 +65,19 @@ var COLORS = {
black: new Color(32,32,37),
}
+var DARK_COLORS = {
+ plain: new Color(24,10,10),
+ ivory: new Color(18,18,18),
+ pink: new Color(40,23,35),
+ red: new Color(40,24,23),
+ orange: new Color(40,32,23),
+ yellow: new Color(40,40,31),
+ green: new Color(33,40,31),
+ blue: new Color(24,26,40),
+ purple: new Color(35,31,40),
+ black: new Color(0,0,0),
+}
+
function nighttime_quotient() {
var q = -10;
var date = new Date()
@@ -111,7 +124,8 @@ function set_background_color_from_time(){
}
function set_background_color(color_name){
color_name = color_name || "plain"
- var color = COLORS[color_name]
+ var color_set = window.matchMedia('(prefers-color-scheme: dark)').matches ? DARK_COLORS : COLORS
+ var color = color_set[color_name]
.clone()
.mottleRGB(4,4,8)
// .add(nighttime_quotient())
diff --git a/public/assets/js/util/format.js b/public/assets/js/util/format.js
index b04c056..d868ddd 100644
--- a/public/assets/js/util/format.js
+++ b/public/assets/js/util/format.js
@@ -23,19 +23,20 @@ function querystring(opt){
}).join("&")
}
function commatize (n, radix) {
- radix = radix || 1024
+ radix = radix || 1000
var nums = [], i, counter = 0, r = Math.floor
- if (n > radix) {
+ if (radix !== -1 && n > radix) {
n /= radix
nums.unshift(r((n * 10) % 10))
nums.unshift(".")
}
do {
- i = n % 10
+ i = r(n % 10)
n = r(n / 10)
- if (n && ! (++counter % 3))
+ counter += 1
+ if (n && ! (counter % 3))
{ i = ' ' + r(i) }
- nums.unshift(r(i))
+ nums.unshift(i)
}
while (n)
return nums.join("")
@@ -102,14 +103,14 @@ function hush_views (n, bias, no_bold) {
n = n || 0
if (n < 30) { return["quiet", n + "&nbsp;v."] }
if (n < 200) { return ["quiet", txt + "&nbsp;v."] }
- else if (n < 500) { return ["quiet", txt + "&nbsp;v."] }
- else if (n < 1000) { return ["old", txt + "&nbsp;v."] }
- else if (n < 5000) { return ["med", txt + "&nbsp;kv."] }
+ else if (n < 500) { return ["old", txt + "&nbsp;v."] }
+ else if (n < 1000) { return ["med", txt + "&nbsp;v."] }
+ else if (n < 5000) { return ["recent", txt + "&nbsp;kv."] }
else if (no_bold || n < 10000) { return ["recent", txt + "&nbsp;kv."] }
else { return ["new", txt + "&nbsp;kv."] }
}
function hush_threads (n, bias, no_bold) {
- var txt = commatize(n, 1000)
+ var txt = commatize(n, -1)
bias = bias || 1
n = n || 0
if (n < 10) { return["quiet", n + "&nbsp;t."] }
@@ -120,33 +121,37 @@ function hush_threads (n, bias, no_bold) {
}
function hush_size (n, bias, no_bold) {
- var txt = commatize(Math.floor(n / 1024))
+ var txt = commatize(Math.floor(n / 1024), 1000)
bias = 1 || bias
n = n || 0
if (n < 1024) {
return ["quiet", n + "&nbsp;b."]
}
- if (n < 1024*1024) {
+ if (n < 1000*1000) {
return ["quiet", txt + "&nbsp;kb."]
}
- else if (n < (20000000/bias)) {
+ if (n < (20000000/bias)) {
return ["quiet", txt + "&nbsp;mb."]
}
- else if (n < (50000000/bias)) {
+ if (n < (50000000/bias)) {
return ["old", txt + "&nbsp;mb."]
}
- else if (n < (80000000/bias)) {
+ if (n < (80000000/bias)) {
return ["med", txt + "&nbsp;mb."]
}
- else if (no_bold || n < (170000000/bias)) {
+ if (no_bold || n < (170000000/bias)) {
return ["recent", txt + "&nbsp;mb."]
}
- else {
- return ["new", txt + "&nbsp;mb."]
+ if (n >= 10000*1000*1000) {
+ console.log(n)
+ txt = commatize(Math.floor(n / (1024 * 1024 * 1024)))
+ return ["new", txt + "&nbsp;gb."]
}
+ return ["new", txt + "&nbsp;mb."]
}
function hush_null (n, unit, no_bold) {
+ n = commatize(n, -1)
var s = unit ? n + "&nbsp;" + unit + "." : n
if (n < 3) {
return ["quiet", s]
@@ -185,26 +190,26 @@ function get_revision (thread) {
return digits
}
-function get_age (t) {
+function get_age (t, long) {
var age = Math.abs( Date.now()/1000 - t)
var r = Math.floor
var m
if (age < 5) { return "now" }
- if (age < 60) { return r(age) + "s" }
+ if (age < 60) { return r(age) + (long ? ' seconds' : "s") }
age /= 60
- if (age < 60) { return r(age) + "m" }
+ if (age < 60) { return r(age) + (long ? ' minutes' : "m") }
m = r(age % 60)
age /= 60
- if (m > 0 && age < 2) { return r(age) + "h" + m + "m" }
- if (age < 24) { return r(age) + "h" }
+ if (m > 0 && age < 2) { return r(age) + (long ? ' hours ' + m + ' minutes' : "h" + m + "m") }
+ if (age < 24) { return r(age) + (long ? ' hours' : "h") }
age /= 24
- if (age < 7) { return r(age) + "d" }
+ if (age < 7) { return r(age) + (long ? ' days' : "d") }
age /= 7
- if (age < 12) { return r(age) + "w" }
+ if (age < 12) { return r(age) + (long ? ' weeks' : "w") }
age /= 4
- if (age < 12) { return r(age) + "m" }
+ if (age < 12) { return r(age) + (long ? ' months' : "m") }
age /= 12
- return r(age) + "y"
+ return r(age) + (long ? ' years' : "y")
}
function tidy_urls (s, short_urls) {
diff --git a/sdk/auth.js b/sdk/auth.js
new file mode 100644
index 0000000..72d38f1
--- /dev/null
+++ b/sdk/auth.js
@@ -0,0 +1 @@
+auth.js \ No newline at end of file
diff --git a/sdk/index.js b/sdk/index.js
new file mode 100644
index 0000000..0c92f0c
--- /dev/null
+++ b/sdk/index.js
@@ -0,0 +1,28 @@
+// bucky 3.0.0 sdk
+
+let env = 'development'
+let endpoint = 'http://localhost:5000/'
+
+export function init(opt){
+ env = opt.env || env
+ endpoint = opt.endpoint || endpoint
+
+ switch (env) {
+ case 'test':
+ break
+ default:
+ case 'development':
+ break
+ case 'production':
+ break
+ }
+}
+
+export function path(api){
+ return endpoint + api
+}
+
+export function image(file, size){
+ return "https://" + sdk.opt.s3.bucket + sdk.opt.s3.path + "/data/" + file.thread + "/" + file.id
+}
+
diff --git a/views/pages/keywords.ejs b/views/pages/keywords.ejs
index 89f44a2..9a1f90a 100644
--- a/views/pages/keywords.ejs
+++ b/views/pages/keywords.ejs
@@ -1,6 +1,16 @@
<% include ../partials/header %>
-<div class="subtitle"></div>
+<div class="subtitle">
+ <a href="/">&lt; Home</a>
+ &middot;
+ <a href="/keywords">Keywords</a>
+ &middot;
+ <a href="/users">Users</a>
+ &middot;
+ <a href="/inbox">Inbox</a>
+ &middot;
+ <a href="/profile">Profile</a>
+</div>
<div id="content">
<div id="keyword_list">
diff --git a/views/pages/users.ejs b/views/pages/users.ejs
new file mode 100644
index 0000000..7c8f417
--- /dev/null
+++ b/views/pages/users.ejs
@@ -0,0 +1,139 @@
+<% include ../partials/header %>
+
+<div class="subtitle">
+ <a href="/">&lt; Home</a>
+ &middot;
+ <a href="/keywords">Keywords</a>
+ &middot;
+ <a href="/users">Users</a>
+ &middot;
+ <a href="/inbox">Inbox</a>
+ &middot;
+ <a href="/profile">Profile</a>
+ &middot;:&middot;
+ <a class='all_link'></a>
+</div>
+
+<div id="content">
+ <div id="user_list" class="ledger">
+ <script type="text/html" class="template">
+ <div class="user_main_row">
+ <div><a href="/profile/{{username}}" class="avatar" style="background-image:url({{avatar}})"></a></div>
+ <div>
+ <div class="user_row">
+ <div class="username"><a href="/profile/{{username}}">{{username}}</a></div>
+ <div class='realname'>
+ {{realname}}
+ </div>
+ <div class="count {{threadcount_class}}">
+ {{threadcount}}
+ </div>
+ <div class="count {{commentcount_class}}">
+ {{commentcount}}
+ </div>
+ <div class="count {{filecount_class}}">
+ {{filecount}}
+ </div>
+ <div class="size {{filesize_class}}">
+ {{filesize}}
+ </div>
+ </div>
+ <div class="user_sub_row">
+ <div class="location">
+ {{location}}
+ </div>
+ <div class="date {{lastseen_date_class}}">
+ seen {{lastseen}} ago
+ </div>
+ <div class="date {{firstseen_date_class}}">
+ joined {{firstseen_date}} <small>{{firstseen_time}}</small>
+ </div>
+ </div>
+ </div>
+ </div>
+ </script>
+ </div>
+</div>
+
+<% include ../partials/footer %>
+
+<style>
+.user_main_row {
+ display: flex;
+ flex-flow: row;
+ margin-bottom: 10px;
+}
+.user_row {
+ display: flex;
+ flex-flow: row wrap;
+ align-items: center;
+ margin-top: 10px;
+}
+.user_sub_row {
+ padding-left: 110px;
+}
+.user_sub_row div {
+ margin-top: 5px;
+}
+.user_main_row .avatar {
+ display: block;
+ width: 40px;
+ height: 40px;
+ background-size: contain;
+ background-repeat: no-repeat;
+ background-position: center;
+}
+.user_main_row .avatar.hidden {
+ opacity: 0;
+ height: 0;
+}
+.user_row .username {
+ min-width: 100px;
+ margin-left: 10px;
+}
+.user_row .username a {
+ font-size: 12px;
+ font-weight: bold;
+ display: block;
+ text-align: right;
+ margin-right: 5px;
+ margin-top: -3px;
+}
+@media (prefers-color-scheme: dark) {
+ .user_row .username a {
+ color: #fff;
+ }
+}
+.user_row .realname {
+ min-width: 150px;
+}
+.user_row .location {
+ min-width: 150px;
+}
+.user_row .date {
+ min-width: 160px;
+}
+.user_row div {
+ display: flex;
+}
+.user_row .dot {
+ margin-right: 5px;
+}
+.user_row .date small {
+ font-size: 9px;
+ margin-left: 3px;
+}
+.user_row .views {
+ min-width: 40px;
+ justify-content: center;
+}
+.user_row .count {
+ min-width: 50px;
+ justify-content: flex-end;
+ margin-right: 10px;
+}
+.user_row .size {
+ min-width: 60px;
+ justify-content: flex-end;
+}
+</style>
diff --git a/views/partials/hootbox.ejs b/views/partials/hootbox.ejs
index fe0298b..826b89a 100644
--- a/views/partials/hootbox.ejs
+++ b/views/partials/hootbox.ejs
@@ -1,7 +1,7 @@
<div class="bluebox" id="hootbox">
<form autocomplete="off">
<input type="text" name="comment" autocomplete="off">
- <button><%= hoot_text %></button>
+ <button class="hoot"><%= hoot_text %></button>
<div class="errors"></div>
</form>
<table id="hoots">
diff --git a/views/partials/scripts.ejs b/views/partials/scripts.ejs
index 7a8e8da..ce606f8 100644
--- a/views/partials/scripts.ejs
+++ b/views/partials/scripts.ejs
@@ -37,6 +37,8 @@
<script src="/assets/js/lib/views/profile/profile.js"></script>
<script src="/assets/js/lib/views/profile/profile_edit.js"></script>
+<script src="/assets/js/lib/views/users/users.js"></script>
+
<script src="/assets/js/lib/views/details/details.js"></script>
<script src="/assets/js/lib/views/details/settings.js"></script>
<script src="/assets/js/lib/views/details/comments.js"></script>
diff --git a/views/partials/threads.ejs b/views/partials/threads.ejs
index bf065f5..a5f5bee 100644
--- a/views/partials/threads.ejs
+++ b/views/partials/threads.ejs
@@ -9,6 +9,7 @@
<td colspan="4">
<a href="/post/">Start a new thread!</a> |
<a href="/keywords">Keywords</a> |
+ <a href="/users">Users</a> |
<a href="/mail">Inbox</a> |
<a href="/profile">Profile</a> |
<a href="/logout" class="logout">Logout</a>