diff options
Diffstat (limited to 'public/assets')
| -rw-r--r-- | public/assets/css/hootstream.css | 329 | ||||
| -rw-r--r-- | public/assets/js/lib/views/stream/hootstream.js | 82 | ||||
| -rw-r--r-- | public/assets/js/util/format.js | 52 |
3 files changed, 399 insertions, 64 deletions
diff --git a/public/assets/css/hootstream.css b/public/assets/css/hootstream.css new file mode 100644 index 0000000..efaa989 --- /dev/null +++ b/public/assets/css/hootstream.css @@ -0,0 +1,329 @@ +/** + * hootstream.css + */ + +#hootstream { + margin-top: 3rem; + font-size: 16px; +} +#hootstream form { + display: flex; + width: 100%; + justify-content: space-between; + align-items: center; + flex-direction: row; + margin-bottom: 0.75rem; +} +#hootstream form input { + flex: 1; + font-size: 16px; + padding: 0.25rem; + margin-bottom: 0; +} +#hootstream form button { + margin: 0; + padding: 0.25rem; + margin-left: 0.5rem; + min-width: 2.5rem; +} +#hootevents { + font-size: 14px; + padding-right: 3rem; +} + +/** HOOT HOOT content */ + +#hootevents .hoot { + display: flex; + flex-flow: row wrap; + justify-content: space-between; + align-items: flex-start; + margin-bottom: 0.5rem; + width: 100%; +} +#hootevents .hootText { + max-width: 60rem; +} + +/** metadata */ + +#hootevents .keywordLink { + text-decoration: none; + opacity: 0.8; + transition: opacity 0.1s; + font-size: 13px; +} +#hootevents .keywordLink:hover { + text-decoration: none; + opacity: 1; +} +#hootevents .fileCount, +#hootevents .commentCount, +#hootevents .age { + width: 40px; + display: flex; + justify-content: flex-end; + align-items: flex-start; + text-align: right; + margin: 0px 0.75rem 0px 0px; + font-size: 13px; +} +#hootevents .fileCount, +#hootevents .commentCount { +} +#hootevents .fileCount { + margin-right: 1.5rem; +} +#hootevents .userLink { + color: #118; + opacity: 0.8; + text-align: right; + width: 6rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-right: 0.5rem; + display: flex; + justify-content: flex-end; + align-items: center; + margin-top: 0.175rem; +} +#hootevents .avatar { + width: 1.5rem; + height: 1.5rem; + background-size: cover; + border-radius: 2px; + border: 1px solid #000; + margin: 0px 0.5rem 0px 0px; + box-shadow: 0 0 1px 1px rgba(128, 128, 128, 0.1); + position: relative; + top: -0.25rem; +} +#hootevents .hoot .text { + text-align: left; + flex: 1; + margin: 0px 0.5rem 0px 0px; + white-space: pre-wrap; + line-height: 1.5; +} + +#hootevents .hoot.isRecent .threadLink { + font-weight: bold; + text-shadow: 0 0 3px rgba(192, 192, 192, 0.2); +} +#hootevents .hoot .threadLink { + color: #38f; + font-size: 16px; +} + +/** DiViDErZ */ + +#hootevents .divider { + border-top: 0; + border-bottom: 1px solid rgba(88, 88, 88, 0.4); + margin: 0 auto; + width: 80%; + height: 0rem; + margin-bottom: 1rem; +} +#hootevents .divider.dark { + border-top: 1px solid rgba(88, 88, 88, 0.4); + border-bottom: 0; + height: 1rem; + margin-bottom: 0rem; + margin-top: 0rem; +} +#hootevents .thread { + background: linear-gradient( + 90deg, + rgba(127, 127, 127, 0), + rgba(127, 127, 127, 0.05), + rgba(127, 127, 127, 0) + ); +} + +/** LastLog */ + +#hootevents .hoot.streamLastlog { + font-size: 13px; + margin-bottom: 1rem; + align-items: center; +} +#hootevents .hoot.streamLastlog .userLink { + margin-top: 0; + margin-right: 0.375rem; +} +#hootevents .hoot.streamLastlog .text { + line-height: 0; +} +#hootevents .hoot.streamLastlog .age { +} + +/** Files */ + +#hootevents .fileList { + width: calc(100% - 8.25rem); + margin-top: 0.5rem; + margin-bottom: 1rem; + margin-left: 8rem; + margin-bottom: 1rem; +} +#hootevents .fileRow { + width: 100%; + display: flex; + flex-direction: row; + align-items: center; + position: relative; +} +#hootevents .fileRow .age { + margin-right: 0; +} +#hootevents .fileRow .size { + font-size: 13px; + padding-right: 1.5rem; +} +#hootevents .fileRow .filename { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; +} +#hootevents .fileRow div { + color: rgba(64, 64, 64, 1); + background: transparent; + padding: 0.5rem; + white-space: nowrap; + transition: opacity 0.1s; +} +#hootevents .fileRow::after { + content: ""; + width: 100%; + height: 100%; + opacity: 0.2; + position: absolute; + top: 0; + left: 0; + z-index: -1; +} +#hootevents .fileRow:nth-child(odd)::after { + background-color: #448; +} +#hootevents .fileRow:nth-child(even)::after { + background-color: #88b; +} +#hootevents .fileRow:nth-child(even):hover::after, +#hootevents .fileRow:nth-child(odd):hover::after { + background-color: #bbf; +} + +/** Misc */ +#hootevents code, +#hootevents pre { + background: rgba(127, 127, 127, 0.2); + border: 1px solid rgba(127, 127, 127, 0.2); + max-width: calc(100vw - 12.5rem); + margin: 0.25rem; + padding: 0.5rem; +} + +/** HOOT FORM */ + +#hootform { + margin-bottom: 0.5rem; + padding: 0.5rem; +} +#hootform input[type="text"] { + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid rgba(127, 127, 127, 0.5); + padding-bottom: 3px; + font-size: 14px; + font-family: "Trebuchet MS", sans-serif; + background: transparent; + outline: 0; + width: 270px; + transition: background 200ms; + background: white; +} +#hootform input[type="text"]:invalid { + background: transparent; +} +#hootform input[type="text"]:focus { + border-bottom: 1px solid rgba(40, 20, 20, 0.2); + border-radius: 2px 2px 0 0; + color: #211; +} +#hootform input[type="text"]:focus { + border-bottom-color: rgba(127, 127, 127, 0.2); +} +#hootform input[type="text"]:invalid { + caret-color: #888; +} + +/** MEDIA QUERY: DARK MODE */ +@media (prefers-color-scheme: dark) { + .search_form input[type="text"]:focus { + border-bottom-color: rgba(127, 127, 127, 0.2); + } + .search_form input[type="text"]:invalid { + caret-color: #888; + } + #hootstream .userLink { + color: #def; + } + #hootevents .fileRow div { + color: rgba(192, 192, 192, 1); + } + #hootevents .fileRow:nth-child(odd)::after { + background-color: #88b; + } + #hootevents .fileRow:nth-child(even)::after { + background-color: #448; + } + #hootevents .fileRow:nth-child(even):hover::after, + #hootevents .fileRow:nth-child(odd):hover::after { + background-color: #bbf; + } + #hootevents .hoot .threadLink:hover, + .desktop a:hover { + color: #2bf; + } +} + +/** MEDIA QUERY: PHONE */ +@media (max-width: 700px) { + html, + body { + overflow-x: hidden; + } + #hootform { + margin-bottom: 0rem; + } + #hootstream { + padding-right: 0; + margin-top: 1rem; + } + #hootevents .userLink { + display: none; + } + #hootevents .streamLastlog .userLink { + display: block; + width: auto; + margin-left: 2rem; + } + #hootevents .avatarLink { + margin-left: 0.25rem; + } + #hootevents .fileList { + margin-top: 0.5rem; + margin-bottom: 1rem; + margin-left: 1.75rem; + width: calc(100% - 2rem); + } + #hootevents code, + #hootevents pre { + max-width: calc(100vw - 5.75rem); + margin-right: 0; + padding: 0.25rem 0.25rem 0.75rem 0.25rem; + } +} diff --git a/public/assets/js/lib/views/stream/hootstream.js b/public/assets/js/lib/views/stream/hootstream.js index f748417..f4e48c4 100644 --- a/public/assets/js/lib/views/stream/hootstream.js +++ b/public/assets/js/lib/views/stream/hootstream.js @@ -7,6 +7,7 @@ var HootStream = View.extend({ this.parent = parent; this.$hootevents = this.$("#hootevents"); this.hootTemplate = this.$(".hootTemplate").html(); + this.threadTemplate = this.$(".threadTemplate").html(); this.lastlogTemplate = this.$(".lastlogTemplate").html(); this.fileTemplate = this.$(".fileTemplate").html(); }, @@ -64,16 +65,20 @@ var HootStream = View.extend({ hidden, className, showAvatar, + template, + ...options }) { - console.log(hoot, comment); - return this.render(this.hootTemplate, { + // console.log(hoot, comment); + return this.render(template || this.hootTemplate, { username, className: className ? `hoot ${className}` : "hoot", image: profile_image(username), showAvatar: showAvatar === false ? 0 : 1, - hoot: hoot || tidy_urls(comment, true), + hoot: + hoot || "<div class='hootText'>" + tidy_urls(comment, true) + "</div>", age: get_age(date), age_opacity: get_age_opacity(date), + ...options, }); }, @@ -88,45 +93,34 @@ var HootStream = View.extend({ 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, 10)), - ...this.renderHoots({ hoots: comments.slice(0, 1), tag: "first_post" }), - ...this.renderHoots({ hoots: comments.slice(1).slice(-5) }), - "<div class='divider'></div>", - ]; - // say "in ... " - // audio player OR recent file list - // recent 3 comments - } + const postedToday = +new Date() / 1000 - thread.lastmodified < 86400; + const age_opacity = get_age_opacity(thread.lastmodified); + return [ + "<div class='divider dark'></div>", + this.renderHoot({ + template: this.threadTemplate, + hoot: `<a class="threadLink" href="/details/${thread.id}">${thread.title}</a>`, + keyword_link: thread.keyword + ? `<a class="keywordLink" href="/stream/${thread.keyword}">${thread.keyword}</a>` + : "", + username: thread.username, + className: postedToday ? "isRecent" : "", + date: thread.lastmodified, + file_count: `${files.length || 0} f.`, + file_opacity: age_opacity * get_size_opacity(files.length), + comment_count: `${comments.length || 0} c.`, + comment_opacity: age_opacity * get_size_opacity(files.length), + }), + this.renderFiles(postedToday ? files : files.slice(0, 10)), + ...this.renderHoots({ hoots: comments.slice(0, 1), tag: "first_post" }), + ...this.renderHoots({ + hoots: postedToday ? comments.slice(1) : comments.slice(1).slice(-5), + }), + "<div class='divider'></div>", + ]; + // say "in ... " + // audio player OR recent file list + // recent 3 comments }, renderFiles: function (files) { @@ -154,9 +148,9 @@ var HootStream = View.extend({ age: get_age(file.date), age_opacity: get_age_opacity(file.date), date_class, - // date: datetime[0], + date: datetime[0], // time: datetime[1], - size_class: size[0], + // size_class: size[0], size: size[1], }); }, diff --git a/public/assets/js/util/format.js b/public/assets/js/util/format.js index e6104e5..7058cea 100644 --- a/public/assets/js/util/format.js +++ b/public/assets/js/util/format.js @@ -257,28 +257,40 @@ function get_age(t, long) { age /= 12; return r(age) + (long ? " years" : "y"); } -const ages = { - newest: 60 * 10, - newer: 60 * 60, - new: 60 * 60 * 8, - cool: 60 * 60 * 24, - cold: 60 * 60 * 24 * 7, - colder: 60 * 60 * 24 * 7 * 4, - coldest: 60 * 60 * 24 * 7 * 4 * 12, -}; +const age_scale = [ + [0, 1.0], + [60 * 10, 1.0], + [60 * 60, 0.95], + [60 * 60 * 8, 0.9], + [60 * 60 * 24, 0.85], + [60 * 60 * 24 * 7, 0.65], + [60 * 60 * 24 * 7 * 4, 0.55], + [60 * 60 * 24 * 7 * 4 * 12, 0.5], +]; function get_age_opacity(t) { var age = Math.abs(Date.now() / 1000 - t); - if (age < ages.newest) return 1; - if (age < ages.newer) - return lerp(norm(age, ages.newest, ages.newer), 1.0, 0.95); - if (age < ages.new) return lerp(norm(age, ages.newer, ages.new), 0.95, 0.9); - if (age < ages.cool) return lerp(norm(age, ages.new, ages.cool), 0.9, 0.85); - if (age < ages.cold) return lerp(norm(age, ages.cool, ages.cold), 0.85, 0.6); - if (age < ages.colder) - return lerp(norm(age, ages.cold, ages.colder), 0.6, 0.5); - if (age < ages.coldest) - return lerp(norm(age, ages.colder, ages.coldest), 0.5, 0.4); - return 0.39; + return get_scale_opacity(age, age_scale); +} +const size_scale = [ + [0, 0.0], + [1, 0.7], + [5, 0.8], + [10, 0.9], + [15, 0.95], + [20, 0.99], +]; +function get_size_opacity(n) { + return get_scale_opacity(n, size_scale); +} +function get_scale_opacity(value, scale) { + for (let i = 1; i < scale.length; i++) { + const [max_value, max_lerp] = scale[i]; + if (value < max_value) { + const [min_value, min_lerp] = scale[i - 1]; + return lerp(norm(value, min_value, max_value), min_lerp, max_lerp); + } + } + return scale[scale.length - 1][1]; } function tidy_urls(s, short_urls) { |
