mirror of
https://github.com/basecamp/once-campfire.git
synced 2026-08-28 09:32:37 +09:00
Polish
This commit is contained in:
@@ -10,7 +10,11 @@ class Autocompletable::UsersController < ApplicationController
|
||||
|
||||
private
|
||||
def find_autocompletable_users
|
||||
query.present? ? users_scope.active.filtered_by(query) : users_scope.active
|
||||
if query.present?
|
||||
users_scope.active.filtered_by(query)
|
||||
else
|
||||
users_scope.active
|
||||
end
|
||||
end
|
||||
|
||||
# The rich text editor's mentions prompt filters with `filter`, the
|
||||
|
||||
@@ -4,12 +4,10 @@ class ContentFilters::RemoveSoloUnfurledLinkText < ActionText::Content::Filter
|
||||
end
|
||||
|
||||
def apply
|
||||
if fragment.find_all("div").any?
|
||||
# Trix-era bodies: one <div> wrapping the link text and the embed
|
||||
fragment.replace("div") { |node| node.tap { |n| n.inner_html = unfurled_links.first.to_s } }
|
||||
if trix_body?
|
||||
remove_link_text_from_wrapping_div
|
||||
else
|
||||
# Lexxy bodies: the link sits in its own <p>, the embed follows it
|
||||
fragment.replace("p") { |node| node.at_css("action-text-attachment") ? node : nil }
|
||||
remove_link_paragraphs
|
||||
end
|
||||
end
|
||||
|
||||
@@ -41,4 +39,20 @@ class ContentFilters::RemoveSoloUnfurledLinkText < ActionText::Content::Filter
|
||||
def twitter_url?(url)
|
||||
url.present? && TWITTER_DOMAINS.any? { |domain| url.strip.include?(domain) }
|
||||
end
|
||||
|
||||
def trix_body?
|
||||
fragment.find_all("div").any?
|
||||
end
|
||||
|
||||
def remove_link_text_from_wrapping_div
|
||||
fragment.replace("div") { |node| node.tap { |n| n.inner_html = unfurled_links.first.to_s } }
|
||||
end
|
||||
|
||||
def remove_link_paragraphs
|
||||
fragment.replace("p") do |node|
|
||||
if node.at_css("action-text-attachment")
|
||||
node
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -8,7 +8,6 @@ const OPENGRAPH_EMBED_CONTENT_TYPE = "application/vnd.actiontext.opengraph-embed
|
||||
const UNFURLED_TWITTER_AVATAR_CSS_CLASS = "cf-twitter-avatar"
|
||||
const TWITTER_AVATAR_URL_PREFIX = "https://pbs.twimg.com/profile_images"
|
||||
|
||||
// Unfurls URLs pasted into the rich text editor into OpenGraph preview attachments
|
||||
export default class extends Controller {
|
||||
#abortController
|
||||
|
||||
@@ -44,14 +43,14 @@ export default class extends Controller {
|
||||
if (title && href) return { title, href, image, description }
|
||||
}
|
||||
} catch {
|
||||
// Ignore aborted or failed requests, like the previous implementation did
|
||||
// Ignore aborted and failed requests
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
#opengraphEmbedHTML({ title, href, image, description }) {
|
||||
return `<actiontext-opengraph-embed class="${this.#isTwitterAvatar(image) ? UNFURLED_TWITTER_AVATAR_CSS_CLASS : ""}">
|
||||
return `<actiontext-opengraph-embed class="${this.#embedClass(image)}">
|
||||
<div class="og-embed gap">
|
||||
<div class="og-embed__content">
|
||||
<div class="og-embed__title">
|
||||
@@ -59,12 +58,28 @@ export default class extends Controller {
|
||||
</div>
|
||||
<div class="og-embed__description">${escapeHTML(truncateString(description, 560))}</div>
|
||||
</div>
|
||||
${image ? `<div class="og-embed__image"><img src="${escapeHTML(image)}" class="image center" alt="" /></div>` : ""}
|
||||
${this.#imageHTML(image)}
|
||||
</div>
|
||||
</actiontext-opengraph-embed>`
|
||||
}
|
||||
|
||||
#embedClass(image) {
|
||||
if (this.#isTwitterAvatar(image)) {
|
||||
return UNFURLED_TWITTER_AVATAR_CSS_CLASS
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
#isTwitterAvatar(image) {
|
||||
return !!image?.startsWith(TWITTER_AVATAR_URL_PREFIX)
|
||||
return Boolean(image) && image.startsWith(TWITTER_AVATAR_URL_PREFIX)
|
||||
}
|
||||
|
||||
#imageHTML(image) {
|
||||
if (image) {
|
||||
return `<div class="og-embed__image"><img src="${escapeHTML(image)}" class="image center" alt="" /></div>`
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ export default class ClientMessage {
|
||||
body,
|
||||
messageTimestamp: Math.floor(now.getTime()),
|
||||
messageDatetime: now.toISOString(),
|
||||
messageClasses: this.#containsOnlyEmoji(this.#plainTextFromNode(node)) ? "message--emoji" : "",
|
||||
messageClasses: this.#messageClassesFromNode(node),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -52,6 +52,14 @@ export default class ClientMessage {
|
||||
}
|
||||
}
|
||||
|
||||
#messageClassesFromNode(node) {
|
||||
if (this.#containsOnlyEmoji(this.#plainTextFromNode(node))) {
|
||||
return "message--emoji"
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#isPlayCommand(node) {
|
||||
return this.#matchPlayCommand(node)
|
||||
@@ -62,7 +70,11 @@ export default class ClientMessage {
|
||||
}
|
||||
|
||||
#plainTextFromNode(node) {
|
||||
return this.#isRichText(node) ? node.toString()?.trim() : node
|
||||
if (this.#isRichText(node)) {
|
||||
return node.toString().trim()
|
||||
} else {
|
||||
return node
|
||||
}
|
||||
}
|
||||
|
||||
#isRichText(node) {
|
||||
|
||||
@@ -77,14 +77,15 @@ export default class MessageFormatter {
|
||||
|
||||
#highlightCodeBlock(block) {
|
||||
this.#normalizeLineBreaks(block)
|
||||
if (!this.#isPlainText(block)) return
|
||||
|
||||
const language = block.dataset.language
|
||||
if (language && window.hljs.getLanguage(language)) {
|
||||
block.classList.add(`language-${language}`)
|
||||
if (this.#isPlainText(block)) {
|
||||
const language = block.dataset.language
|
||||
if (language && window.hljs.getLanguage(language)) {
|
||||
block.classList.add(`language-${language}`)
|
||||
}
|
||||
|
||||
window.hljs.highlightElement(block)
|
||||
}
|
||||
|
||||
window.hljs.highlightElement(block)
|
||||
}
|
||||
|
||||
// Lexxy breaks code block lines with <br>, Trix-era blocks used newlines
|
||||
|
||||
Reference in New Issue
Block a user