feat:ReplyByEmail button

simplified and updated component
This commit is contained in:
cromelex 2025-05-15 23:50:35 +01:00
parent e8b3397f8c
commit 93aed112af

View File

@ -2,17 +2,13 @@ import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } fro
import { classNames } from "../util/lang" import { classNames } from "../util/lang"
interface ReplyByEmailOptions { interface ReplyByEmailOptions {
username?: string email: string
domain?: string
includeTitles?: string[] includeTitles?: string[]
excludeTitles?: string[] excludeTitles?: string[]
buttonLabel?: string buttonLabel?: string
} }
// Default options will be used if not provided in the layout file const defaultOptions: Partial<ReplyByEmailOptions> = {
const defaultOptions: ReplyByEmailOptions = {
username: "Y29udGFjdA==", // "contact" encoded in base64, as in contact@example.com
domain: "ZXhhbXBsZS5jb20=", // "example.com" encoded in base64, as in contact@example.com
includeTitles: [], includeTitles: [],
excludeTitles: [], excludeTitles: [],
buttonLabel: "Reply by email" buttonLabel: "Reply by email"
@ -21,17 +17,15 @@ const defaultOptions: ReplyByEmailOptions = {
const ReplyByEmail: QuartzComponent = ({ const ReplyByEmail: QuartzComponent = ({
fileData, fileData,
displayClass, displayClass,
username, email,
domain,
includeTitles, includeTitles,
excludeTitles, excludeTitles,
buttonLabel buttonLabel
}: QuartzComponentProps & ReplyByEmailOptions) => { }: QuartzComponentProps & ReplyByEmailOptions) => {
const title = fileData.frontmatter?.title const title = fileData.frontmatter?.title
// Use provided values or defaults const encodedEmail = btoa(email)
const encodedPart1 = username || defaultOptions.username
const encodedPart2 = domain || defaultOptions.domain
const includeList = includeTitles || defaultOptions.includeTitles const includeList = includeTitles || defaultOptions.includeTitles
const excludeList = excludeTitles || defaultOptions.excludeTitles const excludeList = excludeTitles || defaultOptions.excludeTitles
const label = buttonLabel || defaultOptions.buttonLabel const label = buttonLabel || defaultOptions.buttonLabel
@ -47,15 +41,22 @@ const ReplyByEmail: QuartzComponent = ({
if (shouldDisplay) { if (shouldDisplay) {
return ( return (
<div class="center-wrapper"> <div class="center-wrapper">
<button <button
class={classNames(displayClass, "reply-by-email-button")} class={classNames(displayClass, "reply-by-email-button")}
data-username={encodedPart1} data-email={encodedEmail}
data-domain={encodedPart2} data-title={encodeURIComponent(title)}
data-title={encodeURIComponent(title)} onclick={`
const encodedEmail = this.getAttribute('data-email');
const email = atob(encodedEmail);
const title = this.getAttribute('data-title');
const mailtoLink = 'mailto:' + email + '?subject=' + title;
window.location.href = mailtoLink;
return false;
`}
> >
{label} {label}
</button> </button>
</div> </div>
) )
} else { } else {
return null return null
@ -87,61 +88,22 @@ ReplyByEmail.css = `
} }
` `
// Script that works with SPA navigation
ReplyByEmail.beforeDOMLoaded = `
// Function to attach email button handlers
function attachEmailHandlers() {
document.querySelectorAll('.reply-by-email-button').forEach(function(button) {
// Remove existing event listeners first to prevent duplicates
button.removeEventListener('click', handleEmailButtonClick);
// Add fresh event listener
button.addEventListener('click', handleEmailButtonClick);
});
}
// Handler function for the email button click
function handleEmailButtonClick(e) {
e.preventDefault();
// Get data attributes
const username = atob(this.getAttribute('data-username'));
const domain = atob(this.getAttribute('data-domain'));
const title = this.getAttribute('data-title');
// Create email address and mailto link
const email = username + '@' + domain;
const mailtoLink = 'mailto:' + email + '?subject=' + title;
// Open email client
window.location.href = mailtoLink;
}
// Initial attachment when the page loads
document.addEventListener('DOMContentLoaded', attachEmailHandlers);
// Re-attach handlers after SPA navigation
document.addEventListener('nav', function() {
// Small delay to ensure the new buttons are in the DOM
setTimeout(attachEmailHandlers, 10);
});
`
export default ((opts?: ReplyByEmailOptions) => { export default ((opts?: ReplyByEmailOptions) => {
// Component constructor that accepts options if (!opts?.email) {
throw new Error("ReplyByEmail component requires an email parameter")
}
const component: QuartzComponent = (props) => { const component: QuartzComponent = (props) => {
return ReplyByEmail({ return ReplyByEmail({
...props, ...props,
username: opts?.username, email: opts.email,
domain: opts?.domain,
includeTitles: opts?.includeTitles, includeTitles: opts?.includeTitles,
excludeTitles: opts?.excludeTitles, excludeTitles: opts?.excludeTitles,
buttonLabel: opts?.buttonLabel buttonLabel: opts?.buttonLabel
}) })
} }
// Pass through the CSS and beforeDOMLoaded
component.css = ReplyByEmail.css component.css = ReplyByEmail.css
component.beforeDOMLoaded = ReplyByEmail.beforeDOMLoaded
return component return component
}) satisfies QuartzComponentConstructor }) satisfies QuartzComponentConstructor