summaryrefslogtreecommitdiff
path: root/public/assets/js/lib/views/stream
diff options
context:
space:
mode:
authorjulian laplace <julescarbon@gmail.com>2022-10-25 23:26:31 +0200
committerjulian laplace <julescarbon@gmail.com>2022-10-25 23:26:31 +0200
commit1cfed2612fd1042a15d470a44ec87588c966dc12 (patch)
treeba850f589194a21fd1587dd71dfcd056a242e4ac /public/assets/js/lib/views/stream
parentae311b71cccf5df9e0e19e276615cc32426d9de1 (diff)
hootstream... dark mode only
Diffstat (limited to 'public/assets/js/lib/views/stream')
-rw-r--r--public/assets/js/lib/views/stream/hootform.js42
-rw-r--r--public/assets/js/lib/views/stream/hootstream.js205
-rw-r--r--public/assets/js/lib/views/stream/index.js69
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="/">&lt; Home</a> &middot; <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";
+ },
+});