diff options
Diffstat (limited to 'public/assets/js/lib/views/stream')
| -rw-r--r-- | public/assets/js/lib/views/stream/hootform.js | 42 | ||||
| -rw-r--r-- | public/assets/js/lib/views/stream/hootstream.js | 205 | ||||
| -rw-r--r-- | public/assets/js/lib/views/stream/index.js | 69 |
3 files changed, 316 insertions, 0 deletions
diff --git a/public/assets/js/lib/views/stream/hootform.js b/public/assets/js/lib/views/stream/hootform.js new file mode 100644 index 0000000..368e616 --- /dev/null +++ b/public/assets/js/lib/views/stream/hootform.js @@ -0,0 +1,42 @@ +var HootForm = FormView.extend({ + el: "#hootform", + + events: {}, + + action: "/api/thread/1/comment", + + initialize: function (opt) { + this.__super__.initialize.call(this); + this.$comment = this.$("[name=comment]"); + }, + + show: function () { + this.$el.show(); + }, + + hide: function () { + this.$el.hide(); + }, + + load: function (comments) { + if (!comments || (!comments.length && !this.options.required)) { + this.$el.hide(); + return; + } + comments.forEach(this.appendComment.bind(this)); + }, + + validate: function () { + var errors = []; + var comment = $("[name=comment]").val(); + if (!comment || !comment.length) { + errors.push("Please enter a comment."); + } + return errors.length ? errors : null; + }, + + success: function (data) { + this.parent.onComment(data.comment); + this.$("[name=comment]").val(""); + }, +}); diff --git a/public/assets/js/lib/views/stream/hootstream.js b/public/assets/js/lib/views/stream/hootstream.js new file mode 100644 index 0000000..cdc7acf --- /dev/null +++ b/public/assets/js/lib/views/stream/hootstream.js @@ -0,0 +1,205 @@ +const IMAGE_REGEXP = /(.gif|.jpg|.jpeg|.png)$/gi; + +var HootStream = View.extend({ + el: "#hootstream", + + initialize: function ({ parent }) { + this.parent = parent; + this.$hootevents = this.$("#hootevents"); + this.hootTemplate = this.$(".hootTemplate").html(); + this.lastlogTemplate = this.$(".lastlogTemplate").html(); + this.fileTemplate = this.$(".fileTemplate").html(); + }, + + load: function (data) { + this.$hootevents.empty(); + const { order, threadLookup } = this.agglutinate(data); + console.log(data, threadLookup, order); + const $els = order.map( + function (item) { + return item.type === "thread" + ? this.renderThread(threadLookup[item.thread_id]).reduce( + ($el, $item) => $el.append($item), + $("<div class='thread'>") + ) + : item.type === "hoot" + ? this.renderHoot(item.data) + : item.type === "lastlog" + ? this.renderLastlog(item.data) + : "Unknown item"; + }.bind(this) + ); + this.$hootevents.append($els); + }, + + render: (template, object) => { + const rendered = Object.entries(object).reduce( + (newTemplate, [key, value]) => + newTemplate.replace(new RegExp(`{{${key}}}`, "g"), value), + template + ); + return $(rendered); + }, + + renderLastlog: function ({ username, lastseen }) { + const age = get_age(lastseen); + const age_ago = age === "now" ? age : `${age} ago`; + return this.render(this.lastlogTemplate, { + className: "hoot streamLastlog", + username, + age, + opacity: 0.6, + showAvatar: 0, + hoot: "last seen " + age_ago, + }); + }, + + renderHoot: function ({ + id, + thread, + date, + username, + hoot, + comment, + hidden, + className, + showAvatar, + }) { + return this.render(this.hootTemplate, { + username, + className: className ? `hoot ${className}` : "hoot", + image: profile_image(username), + showAvatar: showAvatar === false ? 0 : 1, + hoot: hoot || tidy_urls(comment, true), + age: get_age(date), + age_opacity: get_age_opacity(date), + }); + }, + + renderHoots: function ({ hoots, className }) { + const els = []; + for (hoot of hoots) { + els.push(this.renderHoot({ ...hoot, className })); + } + return els; + }, + + renderThread: function ({ thread, comments, files, images }) { + thread = thread.shift(); + // console.log(thread, comments, files, images); + const postedToday = +new Date() / 1000 - thread.date < 86400; + + if (postedToday) { + return [ + "<div class='divider dark'></div>", + this.renderHoot({ + hoot: `<a class="threadLink" href="/details/${thread.id}">${thread.title}</a>`, + username: thread.username, + className: "isRecent", + date: thread.lastmodified, + ...this.renderFiles(files), + ...this.renderHoots({ + hoots: comments.slice(0, 1), + tag: "first_post", + }), + ...this.renderHoots({ hoots: comments.slice(1) }), + }), + "<div class='divider'></div>", + ]; + // display very proud headline + // title, avatar, first image, full file list, + } else { + return [ + "<div class='divider dark'></div>", + this.renderHoot({ + hoot: `<a class="threadLink" href="/details/${thread.id}">${thread.title}</a>`, + username: thread.username, + className: "", + date: thread.lastmodified, + }), + this.renderFiles(files.slice(0, 3)), + ...this.renderHoots({ hoots: comments.slice(0, 1), tag: "first_post" }), + ...this.renderHoots({ hoots: comments.slice(1).slice(-3) }), + "<div class='divider'></div>", + ]; + // say "in ... " + // audio player OR recent file list + // recent 3 comments + } + }, + + renderFiles: function (files) { + if (!files.length) { + return null; + } + const $table = $("<table>"); + for (const file of files) { + const $el = this.renderFile(file); + $table.append($el); + } + return $table; + }, + + renderFile: function (file) { + var size = hush_size(file.size); + var datetime = verbose_date(file.date, true); + var date_class = carbon_date(file.date); + var link = make_link(file); + return this.render(this.fileTemplate, { + id: file.id, + username: file.username, + link, + filename: file.filename, + age: get_age(file.date), + date_class, + // date: datetime[0], + // time: datetime[1], + size_class: size[0], + size: size[1], + }); + }, + + agglutinate: ({ threads, comments, files, hootbox, lastlog }) => + [ + ...threads.map((thread) => [ + thread.id, + thread.createdate, + "thread", + thread, + ]), + ...comments + .filter((comment) => comment.thread !== 1) + .map((comment) => [comment.thread, comment.date, "comments", comment]), + ...files.map((file) => [ + file.thread, + file.date, + IMAGE_REGEXP.test(file.filename) ? "images" : "files", + file, + ]), + ...hootbox.map((hoot) => [1, hoot.date, "hoot", hoot]), + ...lastlog.map((user) => [1, user.lastseen, "lastlog", user]), + ] + .sort((a, b) => b[1] - a[1]) + .reduce( + ({ threadLookup, order }, [thread_id, date, type, data]) => { + if (type === "hoot") { + order.push({ type: "hoot", date, data }); + } else if (type === "lastlog") { + order.push({ type: "lastlog", date, data }); + } else if (thread_id !== 1) { + if (!(thread_id in threadLookup)) { + threadLookup[thread_id] = { + thread: [], + comments: [], + files: [], + images: [], + }; + order.push({ type: "thread", date, thread_id }); + } + threadLookup[thread_id][type].push(data); + } + return { threadLookup, order }; + }, + { threadLookup: {}, order: [] } + ), +}); diff --git a/public/assets/js/lib/views/stream/index.js b/public/assets/js/lib/views/stream/index.js new file mode 100644 index 0000000..d55a9c3 --- /dev/null +++ b/public/assets/js/lib/views/stream/index.js @@ -0,0 +1,69 @@ +var StreamView = View.extend({ + events: {}, + + action: "/api/stream", + keywordAction: "/api/keyword/", + + initialize: function (opt) { + // opt.parent = parent + // this.hootbox = new HootBox({ parent: this }); + this.hootform = new HootForm({ parent: this }); + this.hootstream = new HootStream({ parent: this }); + // this.threadbox = new ThreadBox({ parent: this }); + // this.lastlog = new LastLog({ parent: this }); + // this.countdown = new Countdown({ parent: this }); + }, + + load: function (keyword) { + $("body").addClass("index"); + // if (keyword) { + // $(".subtitle").html( + // '<a href="/">< Home</a> · <a href="/keywords">Keywords</a>' + // ); + // this.threadbox.options.latest = false; + // this.threadbox.options.welcome = false; + // $.get(this.keywordAction + keyword, this.populate.bind(this)); + // } else { + // this.hootbox.options.required = true; + // this.threadbox.options.latest = true; + // this.threadbox.options.welcome = true; + // } + $.get(this.action, this.populate.bind(this)); + }, + + populate: function (data) { + $("body").removeClass("loading"); + this.data = data; + this.hootstream.load(data); + // this.hootbox.load(data.hootbox); + // this.hootbox.hide(); + // this.threadbox.load(data); + // this.lastlog.load(data.lastlog); + if (data.mail.count) { + $(".alert") + .show() + .html( + "<a href='/mail'>" + + "You have " + + data.mail.count + + " new message" + + courtesy_s(data.mail.count) + + "!</a>" + ); + if (is_mobile) { + $("#content").prepend($(".alert")); + } + } + $(".search_form input").focus(); + }, + + onComment: function (comment) { + this.data.hootbox.comments.push(comment); + this.data.hootstream.comments.push(comment); + this.populate(this.data); + }, + + success: function () { + window.location.href = "/index"; + }, +}); |
