Adopt exact Old Reddit UI with tabs, score column, and sort selector

This commit is contained in:
2026-09-13 14:15:28 -07:00
parent e61a7abb70
commit 382fafdf7a

View File

@@ -1,7 +1,7 @@
// ==UserScript== // ==UserScript==
// @name Reddit Comments on YouTube // @name Reddit Comments on YouTube
// @version 1.0.1 // @version 1.0.2
// @description View Reddit discussion threads and comments directly above YouTube comments. // @description View Reddit discussion threads and comments in Old Reddit UI directly above YouTube comments.
// @match https://www.youtube.com/* // @match https://www.youtube.com/*
// @run-at document_idle // @run-at document_idle
// ==/UserScript== // ==/UserScript==
@@ -13,6 +13,7 @@ const DESC_SELECTOR = '#description, #description-inline-expander, ytd-text-inli
let currentVideoId = null; let currentVideoId = null;
let activePostId = null; let activePostId = null;
let cachedPosts = []; let cachedPosts = [];
let currentSort = 'best';
const ensureStyles = () => { const ensureStyles = () => {
if (document.getElementById(STYLE_ID)) return; if (document.getElementById(STYLE_ID)) return;
@@ -21,208 +22,348 @@ const ensureStyles = () => {
style.textContent = ` style.textContent = `
#${MOUNT_ID} { #${MOUNT_ID} {
margin: 16px 0 24px; margin: 16px 0 24px;
font-family: Roboto, Arial, sans-serif; font-family: Verdana, Arial, Helvetica, sans-serif;
color: var(--yt-spec-text-primary, #0f0f0f); font-size: 12px;
color: #222;
}
html[dark] #${MOUNT_ID} {
color: #d7dadc;
} }
.os-tabs { .os-tabs {
display: flex; display: flex;
gap: 8px; list-style: none;
margin: 0;
padding: 0;
overflow-x: auto; overflow-x: auto;
padding: 4px 0 12px; border-bottom: 1px solid #ddd;
border-bottom: 1px solid var(--yt-spec-outline, rgba(0,0,0,0.1)); }
scrollbar-width: thin; html[dark] .os-tabs {
border-bottom-color: #383838;
} }
.os-tab { .os-tab {
display: inline-flex; background: #f1f1f1;
border: 1px solid #ddd;
border-bottom: none;
display: flex;
flex: 1;
min-width: 120px;
font-size: 12px;
justify-content: center;
align-items: center; align-items: center;
gap: 6px;
padding: 6px 12px; padding: 6px 12px;
border-radius: 16px;
border: 1px solid var(--yt-spec-outline, rgba(0,0,0,0.1));
background: var(--yt-spec-badge-chip-background, rgba(0,0,0,0.05));
color: var(--yt-spec-text-secondary, #606060);
cursor: pointer; cursor: pointer;
font-size: 13px; color: #333;
font-weight: 500; user-select: none;
white-space: nowrap; text-decoration: none;
transition: background 0.2s, border-color 0.2s; margin-right: -1px;
}
html[dark] .os-tab {
background: #202020;
border-color: #383838;
color: #aaa;
} }
.os-tab:hover { .os-tab:hover {
background: var(--yt-spec-button-chip-background-hover, rgba(0,0,0,0.1)); background: #fff;
}
html[dark] .os-tab:hover {
background: #181818;
color: #eee;
} }
.os-tab.os-active { .os-tab.os-active {
background: #ff4500; background: #fff;
color: #000;
font-weight: bold;
border-bottom: 1px solid #fff;
margin-bottom: -1px;
}
html[dark] .os-tab.os-active {
background: #0f0f0f;
color: #fff; color: #fff;
border-color: #ff4500; border-bottom: 1px solid #0f0f0f;
} }
.os-tab .os-badge { .os-tab .os-count {
font-size: 10px; color: #888;
padding: 1px 5px; font-weight: normal;
background: rgba(255,255,255,0.25); margin-left: 4px;
border-radius: 8px;
} }
.os-tab:not(.os-active) .os-badge { .os-tab.os-official {
background: var(--yt-spec-outline, rgba(0,0,0,0.1)); color: #ff4500;
color: var(--yt-spec-text-primary, #0f0f0f);
} }
.os-header { .os-box {
border: 1px solid #ddd;
border-top: none;
padding: 12px 14px 16px;
background: #fff;
}
html[dark] .os-box {
border-color: #383838;
background: #0f0f0f;
}
.os-post-entry {
display: flex; display: flex;
justify-content: space-between;
align-items: flex-start; align-items: flex-start;
margin: 14px 0 12px; margin-bottom: 12px;
gap: 12px; }
.os-post-score {
font-size: 15px;
font-weight: bold;
color: #555;
min-width: 32px;
padding-top: 1px;
text-align: center;
margin-right: 8px;
}
html[dark] .os-post-score {
color: #aaa;
}
.os-post-main {
flex: 1;
} }
.os-post-title { .os-post-title {
font-size: 15px; font-size: 15px;
font-weight: 600; color: #0000ff;
line-height: 1.4;
color: var(--yt-spec-text-primary, #0f0f0f);
text-decoration: none; text-decoration: none;
line-height: 1.35;
font-weight: normal;
}
html[dark] .os-post-title {
color: #4f9feb;
} }
.os-post-title:hover { .os-post-title:hover {
text-decoration: underline; text-decoration: underline;
} }
.os-post-meta { .os-post-tagline {
display: flex;
align-items: center;
gap: 8px;
margin-top: 4px;
font-size: 12px;
color: var(--yt-spec-text-secondary, #606060);
}
.os-official-tag {
background: #0079d3;
color: #fff;
font-size: 10px; font-size: 10px;
font-weight: 600; color: #888;
padding: 2px 6px; margin: 4px 0;
border-radius: 4px;
} }
.os-comment-list { .os-post-tagline a {
color: #369;
text-decoration: none;
}
html[dark] .os-post-tagline a {
color: #2693e6;
}
.os-post-tagline a:hover {
text-decoration: underline;
}
.os-actions {
display: flex; display: flex;
flex-direction: column; gap: 8px;
gap: 12px; font-size: 10px;
font-weight: bold;
color: #888;
list-style: none;
margin: 0;
padding: 0;
}
.os-actions a {
color: #888;
text-decoration: none;
}
.os-actions a:hover {
text-decoration: underline;
}
.os-sort-bar {
font-size: 11px;
color: #333;
margin: 10px 0 12px 40px;
}
html[dark] .os-sort-bar {
color: #aaa;
}
.os-sort-bar select {
font-size: 11px;
margin-left: 4px;
background: inherit;
color: inherit;
border: 1px solid #ccc;
border-radius: 2px;
padding: 1px 3px;
}
html[dark] .os-sort-bar select {
border-color: #555;
background: #222;
}
.os-comment-tree {
margin-left: 10px;
} }
.os-comment { .os-comment {
font-size: 13px; margin-top: 10px;
line-height: 1.5;
} }
.os-comment-tagline { .os-tagline {
font-size: 10px;
color: #888;
display: flex; display: flex;
align-items: center; align-items: center;
gap: 6px; gap: 4px;
font-size: 12px; flex-wrap: wrap;
color: var(--yt-spec-text-secondary, #606060);
margin-bottom: 4px; margin-bottom: 4px;
} }
.os-collapse { .os-collapse {
color: #369;
background: none; background: none;
border: none; border: none;
color: var(--yt-spec-text-secondary, #606060);
cursor: pointer; cursor: pointer;
font-family: monospace; font-family: monospace;
font-size: 12px; font-size: 11px;
padding: 0 4px; padding: 0 2px;
font-weight: bold;
}
html[dark] .os-collapse {
color: #2693e6;
} }
.os-author { .os-author {
font-weight: 600; font-weight: bold;
color: var(--yt-spec-text-primary, #0f0f0f); color: #369;
text-decoration: none; text-decoration: none;
} }
html[dark] .os-author {
color: #2693e6;
}
.os-author:hover {
text-decoration: underline;
}
.os-author.os-op { .os-author.os-op {
color: #0079d3; color: #0055df;
}
.os-op-tag {
color: #0055df;
font-weight: bold;
font-size: 10px;
margin-left: 2px;
}
html[dark] .os-op-tag, html[dark] .os-author.os-op {
color: #5296dd;
}
.os-flair {
background: #f5f5f5;
border: 1px solid #ddd;
border-radius: 2px;
color: #555;
font-size: 9px;
padding: 0 3px;
}
html[dark] .os-flair {
background: #282828;
border-color: #4a4a4a;
color: #ccc;
} }
.os-score { .os-score {
font-weight: 500; font-weight: bold;
color: #888;
}
.os-time {
color: #aaa;
} }
.os-body { .os-body {
color: var(--yt-spec-text-primary, #0f0f0f); color: #222;
font-size: 13px;
line-height: 1.45;
margin-left: 18px;
word-break: break-word; word-break: break-word;
overflow-wrap: anywhere; overflow-wrap: anywhere;
} }
html[dark] .os-body {
color: #d7dadc;
}
.os-body p { margin: 4px 0; } .os-body p { margin: 4px 0; }
.os-body a { color: var(--yt-spec-brand-link-text, #065fd4); text-decoration: none; } .os-body a { color: #0079d3; text-decoration: none; }
html[dark] .os-body a { color: #4f9feb; }
.os-body a:hover { text-decoration: underline; } .os-body a:hover { text-decoration: underline; }
.os-body blockquote { .os-body blockquote {
margin: 4px 0; border-left: 2px solid #c5c1ad;
margin: 4px 0 4px 4px;
padding-left: 8px; padding-left: 8px;
border-left: 3px solid var(--yt-spec-outline, rgba(0,0,0,0.2)); color: #555;
color: var(--yt-spec-text-secondary, #606060); }
html[dark] .os-body blockquote {
border-left-color: #666;
color: #999;
} }
.os-body pre, .os-body code { .os-body pre, .os-body code {
background: var(--yt-spec-badge-chip-background, rgba(0,0,0,0.05)); background: #f8f8f8;
border-radius: 4px; border: 1px solid #e5e5e5;
font-size: 12px; border-radius: 2px;
padding: 2px 4px; font-size: 11px;
padding: 1px 3px;
} }
.os-replies { html[dark] .os-body pre, html[dark] .os-body code {
margin-left: 12px; background: #1c1c1c;
padding-left: 12px; border-color: #333;
border-left: 2px solid var(--yt-spec-outline, rgba(0,0,0,0.1)); }
.os-comment-actions {
margin: 2px 0 6px 18px;
display: flex; display: flex;
flex-direction: column; gap: 8px;
gap: 10px; font-size: 10px;
margin-top: 8px; font-weight: bold;
color: #888;
}
.os-comment-actions a {
color: #888;
text-decoration: none;
}
.os-comment-actions a:hover {
text-decoration: underline;
}
.os-children {
border-left: 1px dotted #ddf;
margin-left: 14px;
padding-left: 6px;
}
html[dark] .os-children {
border-left-color: #3a3a3a;
} }
.os-collapsed > .os-body, .os-collapsed > .os-body,
.os-collapsed > .os-replies, .os-collapsed > .os-comment-actions,
.os-collapsed > .os-load-more { .os-collapsed > .os-children {
display: none !important; display: none !important;
} }
.os-collapsed .os-tagline {
font-style: italic;
}
.os-load-more { .os-load-more {
background: none; background: none;
border: 1px solid var(--yt-spec-outline, rgba(0,0,0,0.1)); border: none;
border-radius: 12px; color: #369;
padding: 4px 10px; font-weight: bold;
color: var(--yt-spec-brand-link-text, #065fd4); font-size: 11px;
font-size: 12px;
cursor: pointer; cursor: pointer;
margin-top: 6px; margin: 8px 0 8px 18px;
align-self: flex-start; padding: 0;
display: block;
}
html[dark] .os-load-more {
color: #2693e6;
} }
.os-load-more:hover { .os-load-more:hover {
background: var(--yt-spec-badge-chip-background, rgba(0,0,0,0.05)); text-decoration: underline;
} }
.os-status { .os-status {
padding: 10px 14px; padding: 10px;
font-size: 13px; color: #888;
border-radius: 8px; font-style: italic;
background: var(--yt-spec-badge-chip-background, rgba(0,0,0,0.04));
color: var(--yt-spec-text-secondary, #606060);
margin-bottom: 8px;
}
.os-retry-btn {
margin-left: 8px;
color: var(--yt-spec-brand-link-text, #065fd4);
background: none;
border: none;
cursor: pointer;
font-size: 13px;
text-decoration: underline;
} }
`; `;
document.head.append(style); document.head.append(style);
}; };
const formatScore = n => { const formatScore = n => (typeof n === 'number' ? n.toLocaleString() : '0');
if (typeof n !== 'number') return '0';
if (Math.abs(n) >= 1e4) return `${(n / 1e3).toFixed(1)}k`;
if (Math.abs(n) >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
return String(n);
};
const timeAgo = utc => { const timeAgo = utc => {
const s = Math.max(0, Math.floor(Date.now() / 1e3 - utc)); const s = Math.max(0, Math.floor(Date.now() / 1e3 - utc));
if (s < 60) return `${s}s ago`; if (s < 60) return `${s} seconds ago`;
const m = Math.floor(s / 60); const m = Math.floor(s / 60);
if (m < 60) return `${m}m ago`; if (m < 60) return `${m} minute${m === 1 ? '' : 's'} ago`;
const h = Math.floor(m / 60); const h = Math.floor(m / 60);
if (h < 24) return `${h}h ago`; if (h < 24) return `${h} hour${h === 1 ? '' : 's'} ago`;
const d = Math.floor(h / 24); const d = Math.floor(h / 24);
if (d < 30) return `${d}d ago`; if (d < 30) return `${d} day${d === 1 ? '' : 's'} ago`;
const mo = Math.floor(d / 30); const mo = Math.floor(d / 30);
if (mo < 12) return `${mo}mo ago`; if (mo < 12) return `${mo} month${mo === 1 ? '' : 's'} ago`;
return `${Math.floor(mo / 12)}y ago`; const y = Math.floor(mo / 12);
return `${y} year${y === 1 ? '' : 's'} ago`;
}; };
const decodeHtml = str => { const decodeHtml = str => {
@@ -287,9 +428,9 @@ const waitForMount = async (maxAttempts = 40) => {
return null; return null;
}; };
const renderComments = (children, postAuthor) => { const renderComments = (children, postAuthor, permalink) => {
const container = document.createElement('div'); const container = document.createElement('div');
container.className = 'os-comment-list'; container.className = 'os-comment-tree';
for (const child of children) { for (const child of children) {
if (child.kind === 'more') { if (child.kind === 'more') {
@@ -297,66 +438,107 @@ const renderComments = (children, postAuthor) => {
if (!moreIds?.length) continue; if (!moreIds?.length) continue;
const btn = document.createElement('button'); const btn = document.createElement('button');
btn.className = 'os-load-more'; btn.className = 'os-load-more';
btn.textContent = `Load more comments (${count || moreIds.length})`; btn.textContent = `load more comments (${count || moreIds.length})`;
btn.onclick = async () => { btn.onclick = async () => {
btn.disabled = true; btn.disabled = true;
btn.textContent = 'Loading...'; btn.textContent = 'loading...';
try { try {
const res = await OpenScript.fetch( const res = await OpenScript.fetch(
`https://www.reddit.com/api/morechildren.json?api_type=json&link_id=${activePostId}&children=${moreIds.slice(0, 20).join(',')}&sort=best` `https://www.reddit.com/api/morechildren.json?api_type=json&link_id=${activePostId}&children=${moreIds.slice(0, 20).join(',')}&sort=${currentSort}`
); );
if (!res.ok) throw new Error(`HTTP ${res.status}`); if (!res.ok) throw new Error(`HTTP ${res.status}`);
const json = await res.json(); const json = await res.json();
const items = json?.json?.data?.things?.map(t => t.data) || []; const items = json?.json?.data?.things?.map(t => t.data) || [];
btn.replaceWith(renderFlatComments(items, postAuthor)); btn.replaceWith(renderFlatComments(items, postAuthor, permalink));
} catch { } catch {
btn.textContent = 'Failed to load comments'; btn.textContent = 'failed to load comments';
} }
}; };
container.append(btn); container.append(btn);
continue; continue;
} }
const { id, author, score, created_utc, body_html, replies } = child.data; const { id, author, score, created_utc, body_html, replies, author_flair_text } = child.data;
if (!body_html) continue; if (!body_html) continue;
const isDeleted = author === '[deleted]';
const isOp = author === postAuthor;
const el = document.createElement('div'); const el = document.createElement('div');
el.className = 'os-comment'; el.className = 'os-comment';
el.id = `os-c-${id}`; el.id = `os-c-${id}`;
const tagline = document.createElement('div'); const tagline = document.createElement('div');
tagline.className = 'os-comment-tagline'; tagline.className = 'os-tagline';
const toggle = document.createElement('button'); const toggle = document.createElement('button');
toggle.className = 'os-collapse'; toggle.className = 'os-collapse';
toggle.textContent = '[]'; toggle.textContent = '[-]';
toggle.onclick = () => { toggle.onclick = () => {
const isCol = el.classList.toggle('os-collapsed'); const isCol = el.classList.toggle('os-collapsed');
toggle.textContent = isCol ? '[+]' : '[]'; toggle.textContent = isCol ? '[+]' : '[-]';
}; };
tagline.append(toggle);
if (isDeleted) {
const del = document.createElement('span');
del.className = 'os-author';
del.textContent = '[deleted]';
tagline.append(del);
} else {
const authorLink = document.createElement('a'); const authorLink = document.createElement('a');
authorLink.className = `os-author${author === postAuthor ? ' os-op' : ''}`; authorLink.className = `os-author${isOp ? ' os-op' : ''}`;
authorLink.href = `https://www.reddit.com/u/${author}`; authorLink.href = `https://www.reddit.com/u/${author}`;
authorLink.target = '_blank'; authorLink.target = '_blank';
authorLink.rel = 'noopener noreferrer'; authorLink.rel = 'noopener noreferrer';
authorLink.textContent = author; authorLink.textContent = author;
tagline.append(authorLink);
const metaSpan = document.createElement('span'); if (isOp) {
metaSpan.className = 'os-score'; const opTag = document.createElement('span');
metaSpan.textContent = `${formatScore(score)} points • ${timeAgo(created_utc)}`; opTag.className = 'os-op-tag';
opTag.textContent = '[S]';
tagline.append(opTag);
}
}
tagline.append(toggle, authorLink, metaSpan); if (author_flair_text) {
const flair = document.createElement('span');
flair.className = 'os-flair';
flair.textContent = decodeHtml(author_flair_text);
tagline.append(flair);
}
const meta = document.createElement('span');
meta.className = 'os-score';
const ptText = Math.abs(score) === 1 ? 'point' : 'points';
meta.textContent = `${score} ${ptText}`;
const time = document.createElement('span');
time.className = 'os-time';
time.textContent = timeAgo(created_utc);
tagline.append(meta, time);
const body = document.createElement('div'); const body = document.createElement('div');
body.className = 'os-body'; body.className = 'os-body';
body.innerHTML = sanitizeHtml(body_html); body.innerHTML = sanitizeHtml(body_html);
el.append(tagline, body); const actions = document.createElement('div');
actions.className = 'os-comment-actions';
const permLink = document.createElement('a');
permLink.href = `https://www.reddit.com${permalink}${id}`;
permLink.target = '_blank';
permLink.rel = 'noopener noreferrer';
permLink.textContent = 'permalink';
actions.append(permLink);
el.append(tagline, body, actions);
if (replies?.data?.children?.length) { if (replies?.data?.children?.length) {
const sub = renderComments(replies.data.children, postAuthor); const sub = renderComments(replies.data.children, postAuthor, permalink);
sub.className = 'os-replies'; sub.className = 'os-children';
el.append(sub); el.append(sub);
} }
@@ -366,71 +548,112 @@ const renderComments = (children, postAuthor) => {
return container; return container;
}; };
const renderFlatComments = (items, postAuthor) => { const renderFlatComments = (items, postAuthor, permalink) => {
const wrap = document.createDocumentFragment(); const wrap = document.createDocumentFragment();
for (const item of items) { for (const item of items) {
if (!item.body_html) continue; if (!item.body_html) continue;
const isOp = item.author === postAuthor;
const isDeleted = item.author === '[deleted]';
const el = document.createElement('div'); const el = document.createElement('div');
el.className = 'os-comment'; el.className = 'os-comment';
const tagline = document.createElement('div'); const tagline = document.createElement('div');
tagline.className = 'os-comment-tagline'; tagline.className = 'os-tagline';
const toggle = document.createElement('button');
toggle.className = 'os-collapse';
toggle.textContent = '[-]';
toggle.onclick = () => {
const isCol = el.classList.toggle('os-collapsed');
toggle.textContent = isCol ? '[+]' : '[-]';
};
tagline.append(toggle);
if (isDeleted) {
const del = document.createElement('span');
del.className = 'os-author';
del.textContent = '[deleted]';
tagline.append(del);
} else {
const authorLink = document.createElement('a'); const authorLink = document.createElement('a');
authorLink.className = `os-author${item.author === postAuthor ? ' os-op' : ''}`; authorLink.className = `os-author${isOp ? ' os-op' : ''}`;
authorLink.href = `https://www.reddit.com/u/${item.author}`; authorLink.href = `https://www.reddit.com/u/${item.author}`;
authorLink.target = '_blank'; authorLink.target = '_blank';
authorLink.rel = 'noopener noreferrer'; authorLink.rel = 'noopener noreferrer';
authorLink.textContent = item.author; authorLink.textContent = item.author;
tagline.append(authorLink);
if (isOp) {
const opTag = document.createElement('span');
opTag.className = 'os-op-tag';
opTag.textContent = '[S]';
tagline.append(opTag);
}
}
const meta = document.createElement('span'); const meta = document.createElement('span');
meta.className = 'os-score'; meta.className = 'os-score';
meta.textContent = `${formatScore(item.score)} points • ${timeAgo(item.created_utc)}`; const ptText = Math.abs(item.score) === 1 ? 'point' : 'points';
meta.textContent = `${item.score} ${ptText}`;
tagline.append(authorLink, meta); const time = document.createElement('span');
time.className = 'os-time';
time.textContent = timeAgo(item.created_utc);
tagline.append(meta, time);
const body = document.createElement('div'); const body = document.createElement('div');
body.className = 'os-body'; body.className = 'os-body';
body.innerHTML = sanitizeHtml(item.body_html); body.innerHTML = sanitizeHtml(item.body_html);
el.append(tagline, body); const actions = document.createElement('div');
actions.className = 'os-comment-actions';
const permLink = document.createElement('a');
permLink.href = `https://www.reddit.com${permalink}${item.id}`;
permLink.target = '_blank';
permLink.rel = 'noopener noreferrer';
permLink.textContent = 'permalink';
actions.append(permLink);
el.append(tagline, body, actions);
wrap.append(el); wrap.append(el);
} }
return wrap; return wrap;
}; };
const loadPostComments = async (post, postContainer) => { const loadPostComments = async (post, container) => {
postContainer.innerHTML = '<div class="os-status">Loading Reddit comments...</div>'; container.innerHTML = '<div class="os-status">loading comments...</div>';
try { try {
const res = await OpenScript.fetch(`https://www.reddit.com/comments/${post.id}.json?sort=best`); const res = await OpenScript.fetch(`https://www.reddit.com/comments/${post.id}.json?sort=${currentSort}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`); if (!res.ok) throw new Error(`HTTP ${res.status}`);
const [, commentsData] = await res.json(); const [, commentsData] = await res.json();
const children = commentsData?.data?.children || []; const children = commentsData?.data?.children || [];
postContainer.innerHTML = ''; container.innerHTML = '';
if (!children.length) { if (!children.length) {
postContainer.innerHTML = '<div class="os-status">No comments in this thread yet.</div>'; container.innerHTML = '<div class="os-status">no comments (yet)</div>';
return; return;
} }
postContainer.append(renderComments(children, post.author)); container.append(renderComments(children, post.author, post.permalink));
} catch (err) { } catch (err) {
postContainer.innerHTML = `<div class="os-status">Unable to load Reddit comments (${err.message}).</div>`; container.innerHTML = `<div class="os-status">unable to load comments (${err.message}).</div>`;
} }
}; };
const renderThread = (post, mount) => { const renderThread = (post, box) => {
let contentArea = mount.querySelector('.os-content-area'); box.innerHTML = '';
if (!contentArea) {
contentArea = document.createElement('div');
contentArea.className = 'os-content-area';
mount.append(contentArea);
}
contentArea.innerHTML = '';
const header = document.createElement('div'); const entry = document.createElement('div');
header.className = 'os-header'; entry.className = 'os-post-entry';
const scoreDiv = document.createElement('div');
scoreDiv.className = 'os-post-score';
scoreDiv.textContent = post.score;
const mainDiv = document.createElement('div');
mainDiv.className = 'os-post-main';
const left = document.createElement('div');
const title = document.createElement('a'); const title = document.createElement('a');
title.className = 'os-post-title'; title.className = 'os-post-title';
title.href = `https://www.reddit.com${post.permalink}`; title.href = `https://www.reddit.com${post.permalink}`;
@@ -438,19 +661,54 @@ const renderThread = (post, mount) => {
title.rel = 'noopener noreferrer'; title.rel = 'noopener noreferrer';
title.textContent = decodeHtml(post.title); title.textContent = decodeHtml(post.title);
const meta = document.createElement('div'); const tagline = document.createElement('div');
meta.className = 'os-post-meta'; tagline.className = 'os-post-tagline';
meta.textContent = `r/${post.subreddit} • Posted by u/${post.author} ${timeAgo(post.created_utc)}${formatScore(post.score)} points`; tagline.innerHTML = `
submitted ${timeAgo(post.created_utc)} by
<a href="https://www.reddit.com/u/${post.author}" target="_blank" rel="noopener noreferrer">${post.author}</a>
to <a href="https://www.reddit.com/r/${post.subreddit}" target="_blank" rel="noopener noreferrer">r/${post.subreddit}</a>
`;
left.append(title, meta); const actions = document.createElement('ul');
header.append(left); actions.className = 'os-actions';
contentArea.append(header); const permLi = document.createElement('li');
const permA = document.createElement('a');
permA.href = `https://www.reddit.com${post.permalink}`;
permA.target = '_blank';
permA.rel = 'noopener noreferrer';
permA.textContent = `${post.num_comments} comments`;
permLi.append(permA);
actions.append(permLi);
const commentsArea = document.createElement('div'); mainDiv.append(title, tagline, actions);
commentsArea.className = 'os-comments-area'; entry.append(scoreDiv, mainDiv);
contentArea.append(commentsArea); box.append(entry);
loadPostComments(post, commentsArea); const sortBar = document.createElement('div');
sortBar.className = 'os-sort-bar';
sortBar.innerHTML = `
sorted by:
<select>
<option value="best">best</option>
<option value="top">top</option>
<option value="new">new</option>
<option value="controversial">controversial</option>
<option value="old">old</option>
<option value="qa">q&a</option>
</select>
`;
const select = sortBar.querySelector('select');
select.value = currentSort;
select.onchange = e => {
currentSort = e.target.value;
loadPostComments(post, commentsContainer);
};
box.append(sortBar);
const commentsContainer = document.createElement('div');
box.append(commentsContainer);
loadPostComments(post, commentsContainer);
}; };
const renderTabs = (posts, mount) => { const renderTabs = (posts, mount) => {
@@ -464,39 +722,31 @@ const renderTabs = (posts, mount) => {
tabs.className = 'os-tabs'; tabs.className = 'os-tabs';
mount.append(tabs); mount.append(tabs);
const box = document.createElement('div');
box.className = 'os-box';
mount.append(box);
const activePost = posts.find(p => p.name === activePostId) || posts[0]; const activePost = posts.find(p => p.name === activePostId) || posts[0];
activePostId = activePost.name; activePostId = activePost.name;
posts.forEach(post => { posts.forEach(post => {
const isOfficial = officialSub && post.subreddit.toLowerCase() === officialSub; const isOfficial = officialSub && post.subreddit.toLowerCase() === officialSub;
const tab = document.createElement('div'); const tab = document.createElement('div');
tab.className = `os-tab${post.name === activePostId ? ' os-active' : ''}`; tab.className = `os-tab${post.name === activePostId ? ' os-active' : ''}${isOfficial ? ' os-official' : ''}`;
tab.textContent = `r/${post.subreddit}`; tab.innerHTML = `${post.subreddit} <span class="os-count">(${post.num_comments})</span>`;
if (isOfficial) {
const off = document.createElement('span');
off.className = 'os-official-tag';
off.textContent = 'Official';
tab.append(off);
}
const badge = document.createElement('span');
badge.className = 'os-badge';
badge.textContent = `${formatScore(post.score)} ↑ • ${post.num_comments} 💬`;
tab.append(badge);
tab.onclick = () => { tab.onclick = () => {
if (activePostId === post.name) return; if (activePostId === post.name) return;
activePostId = post.name; activePostId = post.name;
tabs.querySelectorAll('.os-tab').forEach(t => t.classList.remove('os-active')); tabs.querySelectorAll('.os-tab').forEach(t => t.classList.remove('os-active'));
tab.classList.add('os-active'); tab.classList.add('os-active');
renderThread(post, mount); renderThread(post, box);
}; };
tabs.append(tab); tabs.append(tab);
}); });
renderThread(activePost, mount); renderThread(activePost, box);
}; };
const updateForVideo = async videoId => { const updateForVideo = async videoId => {
@@ -506,7 +756,7 @@ const updateForVideo = async videoId => {
const initialMount = getMount(); const initialMount = getMount();
if (initialMount) { if (initialMount) {
initialMount.innerHTML = '<div class="os-status">Searching Reddit for discussion threads...</div>'; initialMount.innerHTML = '<div class="os-box"><div class="os-status">searching reddit...</div></div>';
} }
try { try {
@@ -523,7 +773,7 @@ const updateForVideo = async videoId => {
if (!mount || videoId !== currentVideoId) return; if (!mount || videoId !== currentVideoId) return;
if (!posts.length) { if (!posts.length) {
mount.innerHTML = '<div class="os-status">No Reddit discussions found for this video.</div>'; mount.innerHTML = '';
return; return;
} }
@@ -532,13 +782,7 @@ const updateForVideo = async videoId => {
} catch (err) { } catch (err) {
const mount = await waitForMount(); const mount = await waitForMount();
if (mount && videoId === currentVideoId) { if (mount && videoId === currentVideoId) {
mount.innerHTML = ` mount.innerHTML = `<div class="os-box"><div class="os-status">could not search reddit (${err.message})</div></div>`;
<div class="os-status">
Could not search Reddit (${err.message}).
<button class="os-retry-btn">Retry</button>
</div>
`;
mount.querySelector('.os-retry-btn')?.addEventListener('click', () => updateForVideo(videoId));
} }
} }
}; };