styling fixes

This commit is contained in:
Michael Freno
2025-12-21 01:00:57 -05:00
parent 291971a1d7
commit 200037e7a0
9 changed files with 197 additions and 102 deletions

View File

@@ -159,7 +159,7 @@ export default function CommentBlock(props: CommentBlockProps) {
const isAnonymous = () => props.privilegeLevel === "anonymous"; const isAnonymous = () => props.privilegeLevel === "anonymous";
const replyIconColor = () => "#fb923c"; const replyIconColor = () => "var(--color-peach)";
return ( return (
<> <>
@@ -169,13 +169,13 @@ export default function CommentBlock(props: CommentBlockProps) {
onClick={collapseCommentToggle} onClick={collapseCommentToggle}
class="ml-5 w-full px-2 lg:w-3/4" class="ml-5 w-full px-2 lg:w-3/4"
> >
<div class="my-auto mt-1 mr-2 h-8 border-l-2 border-black dark:border-white" /> <div class="border-text my-auto mt-1 mr-2 h-8 border-l-2" />
</button> </button>
</Show> </Show>
{/* Expanded state */} {/* Expanded state */}
<Show when={!commentCollapsed()}> <Show when={!commentCollapsed()}>
<div class="z-[500] transition-all duration-300 ease-in-out"> <div class="z-500 transition-all duration-300 ease-in-out">
<div class="my-4 flex w-full overflow-x-hidden overflow-y-hidden lg:w-3/4"> <div class="my-4 flex w-full overflow-x-hidden overflow-y-hidden lg:w-3/4">
{/* Vote buttons column */} {/* Vote buttons column */}
<div <div
@@ -191,8 +191,8 @@ export default function CommentBlock(props: CommentBlockProps) {
<div <div
class={`h-5 w-5 ${ class={`h-5 w-5 ${
hasUpvoted() hasUpvoted()
? "fill-emerald-500" ? "fill-green"
: `fill-black hover:fill-emerald-500 dark:fill-white ${ : `fill-text hover:fill-green ${
isAnonymous() ? "tooltip z-50" : "" isAnonymous() ? "tooltip z-50" : ""
}` }`
}`} }`}
@@ -218,8 +218,8 @@ export default function CommentBlock(props: CommentBlockProps) {
<div <div
class={`h-5 w-5 ${ class={`h-5 w-5 ${
hasDownvoted() hasDownvoted()
? "fill-rose-500" ? "fill-red"
: `fill-black hover:fill-rose-500 dark:fill-white ${ : `fill-text hover:fill-red ${
isAnonymous() ? "tooltip z-50" : "" isAnonymous() ? "tooltip z-50" : ""
}` }`
}`} }`}
@@ -239,7 +239,7 @@ export default function CommentBlock(props: CommentBlockProps) {
{/* Collapse toggle line */} {/* Collapse toggle line */}
<button onClick={collapseCommentToggle} class="z-0 px-2"> <button onClick={collapseCommentToggle} class="z-0 px-2">
<div <div
class="border-l-2 border-black transition-all duration-300 ease-in-out dark:border-white" class="border-text border-l-2 transition-all duration-300 ease-in-out"
style={{ height: `${toggleHeight()}px` }} style={{ height: `${toggleHeight()}px` }}
/> />
</button> </button>
@@ -292,7 +292,7 @@ export default function CommentBlock(props: CommentBlockProps) {
<TrashIcon <TrashIcon
height={24} height={24}
width={24} width={24}
stroke="red" stroke="var(--color-red)"
strokeWidth={1.5} strokeWidth={1.5}
/> />
</Show> </Show>

View File

@@ -45,10 +45,10 @@ export default function CommentDeletionPrompt(
return ( return (
<div class="flex justify-center"> <div class="flex justify-center">
<div class="fixed top-48 z-50 h-fit w-11/12 sm:w-4/5 md:w-2/3"> <div class="fixed top-48 z-100 h-fit w-11/12 sm:w-4/5 md:w-2/3">
<div <div
id="delete_prompt" id="delete_prompt"
class="fade-in rounded-md bg-red-400 px-8 py-4 shadow-lg dark:bg-red-900" class="fade-in bg-red rounded-md px-8 py-4 shadow-lg brightness-110"
> >
<button class="absolute right-4" onClick={() => {}}> <button class="absolute right-4" onClick={() => {}}>
<Xmark strokeWidth={0.5} color="white" height={50} width={50} /> <Xmark strokeWidth={0.5} color="white" height={50} width={50} />
@@ -56,7 +56,7 @@ export default function CommentDeletionPrompt(
<div class="py-4 text-center text-3xl tracking-wide"> <div class="py-4 text-center text-3xl tracking-wide">
Comment Deletion Comment Deletion
</div> </div>
<div class="mx-auto w-3/4 rounded bg-zinc-50 px-6 py-4 dark:bg-zinc-800"> <div class="bg-surface0 mx-auto w-3/4 rounded px-6 py-4">
<div class="flex overflow-x-auto overflow-y-hidden select-text"> <div class="flex overflow-x-auto overflow-y-hidden select-text">
{/* Comment body will be passed as prop */} {/* Comment body will be passed as prop */}
</div> </div>
@@ -132,9 +132,9 @@ export default function CommentDeletionPrompt(
disabled={props.commentDeletionLoading || !isDeleteEnabled()} disabled={props.commentDeletionLoading || !isDeleteEnabled()}
class={`${ class={`${
props.commentDeletionLoading || !isDeleteEnabled() props.commentDeletionLoading || !isDeleteEnabled()
? "bg-zinc-400" ? "bg-surface2 opacity-50"
: "border-orange-500 bg-orange-400 hover:bg-orange-500" : "border-red bg-red hover:brightness-125"
} rounded border px-4 py-2 text-white shadow-md transition-all duration-300 ease-in-out active:scale-90`} } rounded border px-4 py-2 text-base shadow-md transition-all duration-300 ease-in-out active:scale-90`}
> >
Delete Delete
</button> </button>

View File

@@ -21,7 +21,7 @@ export default function CommentInputBlock(props: CommentInputBlockProps) {
if (props.privilegeLevel === "user" || props.privilegeLevel === "admin") { if (props.privilegeLevel === "user" || props.privilegeLevel === "admin") {
return ( return (
<div class="flex w-full justify-center select-none"> <div class="flex w-full justify-center select-none">
<div class="h-fit w-3/4 md:w-1/2"> <div class="h-fit w-3/4">
<form onSubmit={newCommentWrapper}> <form onSubmit={newCommentWrapper}>
<div class="textarea-group blog"> <div class="textarea-group blog">
<textarea <textarea
@@ -43,9 +43,9 @@ export default function CommentInputBlock(props: CommentInputBlockProps) {
disabled={props.commentSubmitLoading} disabled={props.commentSubmitLoading}
class={`${ class={`${
props.commentSubmitLoading props.commentSubmitLoading
? "bg-zinc-400" ? "bg-surface2 opacity-50"
: "border-orange-500 bg-orange-400 hover:bg-orange-500" : "border-sapphire bg-blue hover:brightness-125"
} rounded border px-4 py-2 font-light text-white shadow-md transition-all duration-300 ease-in-out active:scale-90`} } rounded border px-4 py-2 text-base font-light shadow-md transition-all duration-300 ease-in-out active:scale-90`}
> >
Submit Submit
</button> </button>

View File

@@ -130,7 +130,16 @@ export default function CommentSectionWrapper(
// Helper functions // Helper functions
const updateChannel = () => { const updateChannel = () => {
if (socket && socket.readyState === WebSocket.OPEN) { if (!socket || socket.readyState !== WebSocket.OPEN) {
return;
}
if (!props.currentUserID || !props.id) {
console.warn("Cannot update channel: missing userID or postID");
return;
}
try {
socket.send( socket.send(
JSON.stringify({ JSON.stringify({
action: "channelUpdate", action: "channelUpdate",
@@ -139,13 +148,23 @@ export default function CommentSectionWrapper(
invoker_id: props.currentUserID invoker_id: props.currentUserID
}) })
); );
} catch (error) {
console.error("Error sending channel update:", error);
} }
}; };
// Comment creation // Comment creation
const newComment = async (commentBody: string, parentCommentID?: number) => { const newComment = async (commentBody: string, parentCommentID?: number) => {
setCommentSubmitLoading(true); setCommentSubmitLoading(true);
if (!props.currentUserID) {
console.warn("Cannot create comment: user not authenticated");
setCommentSubmitLoading(false);
return;
}
if (commentBody && socket) { if (commentBody && socket) {
try {
socket.send( socket.send(
JSON.stringify({ JSON.stringify({
action: "commentCreation", action: "commentCreation",
@@ -156,8 +175,22 @@ export default function CommentSectionWrapper(
invokerID: props.currentUserID invokerID: props.currentUserID
}) })
); );
} catch (error) {
console.error("Error sending comment creation:", error);
// Fallback to HTTP API on WebSocket error
await fallbackCommentCreation(commentBody, parentCommentID);
}
} else { } else {
// Fallback to HTTP API if WebSocket unavailable // Fallback to HTTP API if WebSocket unavailable
await fallbackCommentCreation(commentBody, parentCommentID);
}
};
const fallbackCommentCreation = async (
commentBody: string,
parentCommentID?: number
) => {
try {
const domain = import.meta.env.VITE_DOMAIN; const domain = import.meta.env.VITE_DOMAIN;
const res = await fetch( const res = await fetch(
`${domain}/api/database/comments/create/blog/${props.id}`, `${domain}/api/database/comments/create/blog/${props.id}`,
@@ -179,6 +212,9 @@ export default function CommentSectionWrapper(
commentParent: parentCommentID commentParent: parentCommentID
}); });
} }
} catch (error) {
console.error("Error in fallback comment creation:", error);
setCommentSubmitLoading(false);
} }
}; };
@@ -237,7 +273,15 @@ export default function CommentSectionWrapper(
// Comment updating // Comment updating
const editComment = async (body: string, comment_id: number) => { const editComment = async (body: string, comment_id: number) => {
setCommentEditLoading(true); setCommentEditLoading(true);
if (!props.currentUserID) {
console.warn("Cannot edit comment: user not authenticated");
setCommentEditLoading(false);
return;
}
if (socket) { if (socket) {
try {
socket.send( socket.send(
JSON.stringify({ JSON.stringify({
action: "commentUpdate", action: "commentUpdate",
@@ -248,6 +292,10 @@ export default function CommentSectionWrapper(
invokerID: props.currentUserID invokerID: props.currentUserID
}) })
); );
} catch (error) {
console.error("Error sending comment update:", error);
setCommentEditLoading(false);
}
} }
}; };
@@ -290,7 +338,15 @@ export default function CommentSectionWrapper(
deletionType: DeletionType deletionType: DeletionType
) => { ) => {
setCommentDeletionLoading(true); setCommentDeletionLoading(true);
if (!props.currentUserID) {
console.warn("Cannot delete comment: user not authenticated");
setCommentDeletionLoading(false);
return;
}
if (socket) { if (socket) {
try {
socket.send( socket.send(
JSON.stringify({ JSON.stringify({
action: "commentDeletion", action: "commentDeletion",
@@ -301,6 +357,10 @@ export default function CommentSectionWrapper(
postID: props.id postID: props.id
}) })
); );
} catch (error) {
console.error("Error sending comment deletion:", error);
setCommentDeletionLoading(false);
}
} }
}; };
@@ -393,7 +453,13 @@ export default function CommentSectionWrapper(
// Reaction handling // Reaction handling
const commentReaction = (reactionType: ReactionType, commentID: number) => { const commentReaction = (reactionType: ReactionType, commentID: number) => {
if (!props.currentUserID) {
console.warn("Cannot react to comment: user not authenticated");
return;
}
if (socket) { if (socket) {
try {
socket.send( socket.send(
JSON.stringify({ JSON.stringify({
action: "commentReaction", action: "commentReaction",
@@ -404,6 +470,9 @@ export default function CommentSectionWrapper(
reactionType: reactionType reactionType: reactionType
}) })
); );
} catch (error) {
console.error("Error sending comment reaction:", error);
}
} }
}; };

View File

@@ -40,7 +40,7 @@ export default function CommentSorting(props: CommentSortingProps) {
{(topLevelComment) => ( {(topLevelComment) => (
<div <div
onClick={() => checkForDoubleClick(topLevelComment.id)} onClick={() => checkForDoubleClick(topLevelComment.id)}
class="mt-4 max-w-full rounded bg-white py-2 pl-2 shadow select-none sm:pl-4 md:pl-8 lg:pl-12 dark:bg-zinc-900" class="bg-crust mt-4 max-w-full rounded py-2 pl-2 shadow-xl select-none sm:pl-4 md:pl-8 lg:pl-12"
> >
<Show <Show
when={showingBlock().get(topLevelComment.id)} when={showingBlock().get(topLevelComment.id)}

View File

@@ -43,7 +43,7 @@ export default function CommentSortingSelect(props: CommentSortingSelectProps) {
<button <button
type="button" type="button"
onClick={() => setIsOpen(!isOpen())} onClick={() => setIsOpen(!isOpen())}
class="focus-visible:ring-opacity-75 relative w-full cursor-default rounded-lg bg-white py-2 pr-10 pl-3 text-left shadow-md focus:outline-none focus-visible:border-orange-600 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 sm:text-sm dark:bg-zinc-900" class="focus-visible:border-peach focus-visible:ring-offset-peach bg-surface0 focus-visible:ring-opacity-75 relative w-full cursor-default rounded-lg py-2 pr-10 pl-3 text-left shadow-md focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 sm:text-sm"
> >
<span class="block truncate">{selectedLabel()}</span> <span class="block truncate">{selectedLabel()}</span>
<span class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"> <span class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
@@ -51,13 +51,13 @@ export default function CommentSortingSelect(props: CommentSortingSelectProps) {
strokeWidth={1.5} strokeWidth={1.5}
height={24} height={24}
width={24} width={24}
class="fill-zinc-900 dark:fill-white" class="fill-text"
/> />
</span> </span>
</button> </button>
<Show when={isOpen()}> <Show when={isOpen()}>
<div class="ring-opacity-5 absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black transition duration-100 ease-in focus:outline-none sm:text-sm dark:bg-zinc-900"> <div class="ring-opacity-5 bg-surface0 ring-overlay0 absolute mt-1 max-h-60 w-full overflow-auto rounded-md py-1 text-base shadow-lg ring-1 focus:outline-none sm:text-sm">
<For each={SORTING_OPTIONS}> <For each={SORTING_OPTIONS}>
{(sort) => ( {(sort) => (
<button <button
@@ -65,8 +65,8 @@ export default function CommentSortingSelect(props: CommentSortingSelectProps) {
onClick={() => handleSelect(sort.val)} onClick={() => handleSelect(sort.val)}
class={`relative w-full cursor-default py-2 pr-4 pl-10 text-left select-none ${ class={`relative w-full cursor-default py-2 pr-4 pl-10 text-left select-none ${
props.selectedSorting.val === sort.val props.selectedSorting.val === sort.val
? "bg-orange-100 text-orange-900" ? "bg-peach text-base brightness-75"
: "text-zinc-900 hover:bg-orange-50 dark:text-white dark:hover:bg-zinc-800" : "text-text hover:brightness-125"
}`} }`}
> >
<span <span
@@ -79,12 +79,12 @@ export default function CommentSortingSelect(props: CommentSortingSelectProps) {
{sort.label} {sort.label}
</span> </span>
<Show when={props.selectedSorting.val === sort.val}> <Show when={props.selectedSorting.val === sort.val}>
<span class="absolute inset-y-0 left-0 flex items-center pl-3 text-orange-600"> <span class="text-peach absolute inset-y-0 left-0 flex items-center pl-3">
<Check <Check
strokeWidth={1} strokeWidth={1}
height={24} height={24}
width={24} width={24}
class="stroke-zinc-900 dark:stroke-white" class="stroke-text"
/> />
</span> </span>
</Show> </Show>

View File

@@ -25,12 +25,17 @@ export default function EditCommentModal(props: EditCommentModalProps) {
<div class="fixed top-48 h-fit w-11/12 sm:w-4/5 md:w-2/3"> <div class="fixed top-48 h-fit w-11/12 sm:w-4/5 md:w-2/3">
<div <div
id="edit_prompt" id="edit_prompt"
class="fade-in z-50 rounded-md bg-zinc-600 px-8 py-4 shadow-lg dark:bg-zinc-800" class="fade-in bg-surface1 z-50 rounded-md px-8 py-4 shadow-lg"
> >
<button class="absolute right-4" onClick={() => {}}> <button class="absolute right-4" onClick={() => {}}>
<Xmark strokeWidth={0.5} color="white" height={50} width={50} /> <Xmark
strokeWidth={0.5}
color="var(--color-text)"
height={50}
width={50}
/>
</button> </button>
<div class="py-4 text-center text-3xl tracking-wide text-zinc-50"> <div class="text-text py-4 text-center text-3xl tracking-wide">
Edit Comment Edit Comment
</div> </div>
<form onSubmit={editCommentWrapper}> <form onSubmit={editCommentWrapper}>
@@ -40,7 +45,7 @@ export default function EditCommentModal(props: EditCommentModalProps) {
ref={bodyRef} ref={bodyRef}
placeholder=" " placeholder=" "
value={props.commentBody} value={props.commentBody}
class="underlinedInput w-full bg-transparent text-blue-300" class="underlinedInput text-blue w-full bg-transparent"
rows={4} rows={4}
/> />
<span class="bar" /> <span class="bar" />
@@ -51,17 +56,17 @@ export default function EditCommentModal(props: EditCommentModalProps) {
type="submit" type="submit"
disabled={props.editCommentLoading} disabled={props.editCommentLoading}
class={`${ class={`${
props.editCommentLoading ? "bg-zinc-400" : "" props.editCommentLoading
} rounded border px-4 py-2 text-white shadow-md transition-all duration-300 ease-in-out hover:border-blue-500 hover:bg-blue-400 active:scale-90`} ? "bg-surface2 opacity-50"
: "border-sapphire bg-blue hover:brightness-125"
} rounded border px-4 py-2 text-base shadow-md transition-all duration-300 ease-in-out active:scale-90`}
> >
Submit Submit
</button> </button>
</div> </div>
</form> </form>
<Show when={showNoChange()}> <Show when={showNoChange()}>
<div class="text-center text-red-500 italic"> <div class="text-red text-center italic">No change detected</div>
No change detected
</div>
</Show> </Show>
</div> </div>
</div> </div>

View File

@@ -49,7 +49,7 @@ export default function ReactionBar(props: ReactionBarProps) {
<div <div
class={`${ class={`${
props.showingReactionOptions props.showingReactionOptions
? "bg-zinc-50 px-2 py-4 shadow-inner dark:bg-zinc-700" ? "bg-surface0 px-2 py-4 shadow-inner brightness-90"
: "" : ""
} fade-in scrollYDisabled ml-2 flex min-h-[1.5rem] w-48 max-w-[1/4] flex-row overflow-scroll rounded-md py-1 sm:w-56 md:w-fit md:overflow-hidden`} } fade-in scrollYDisabled ml-2 flex min-h-[1.5rem] w-48 max-w-[1/4] flex-row overflow-scroll rounded-md py-1 sm:w-56 md:w-fit md:overflow-hidden`}
> >
@@ -57,7 +57,7 @@ export default function ReactionBar(props: ReactionBarProps) {
{({ type, Component }) => ( {({ type, Component }) => (
<Show when={shouldShowEmoji(type)}> <Show when={shouldShowEmoji(type)}>
<div class="fade-in mx-1 flex"> <div class="fade-in mx-1 flex">
<div class={hasUserReacted(type) ? "text-green-500" : ""}> <div class={hasUserReacted(type) ? "text-green" : ""}>
<Show when={getReactionCount(type) > 0}> <Show when={getReactionCount(type) > 0}>
{getReactionCount(type)} {getReactionCount(type)}
</Show> </Show>

View File

@@ -348,12 +348,10 @@ export default function TextEditor(props: TextEditorProps) {
const title = window.prompt("Section title:", "Click to expand"); const title = window.prompt("Section title:", "Click to expand");
if (title !== null) { if (title !== null && title.trim() !== "") {
instance const content = {
.chain()
.focus()
.insertContent({
type: "details", type: "details",
attrs: { open: true },
content: [ content: [
{ {
type: "detailsSummary", type: "detailsSummary",
@@ -363,14 +361,37 @@ export default function TextEditor(props: TextEditorProps) {
type: "detailsContent", type: "detailsContent",
content: [ content: [
{ {
type: "paragraph", type: "paragraph"
content: [{ type: "text", text: "Add your content here..." }]
} }
] ]
} }
] ]
}) };
.run();
const { from } = instance.state.selection;
instance.chain().focus().insertContent(content).run();
// Move cursor to the paragraph inside detailsContent
// Structure: details (from+1) > detailsSummary > detailsContent > paragraph
// We need to position inside the paragraph which is roughly from + title.length + 3 nodes deep
setTimeout(() => {
const { state } = instance;
let targetPos = from;
// Navigate through the document to find the paragraph inside detailsContent
state.doc.nodesBetween(from, from + 200, (node, pos) => {
if (node.type.name === "detailsContent") {
// Position cursor at the start of the first child (paragraph)
targetPos = pos + 1;
return false; // Stop iteration
}
});
if (targetPos > from) {
instance.commands.setTextSelection(targetPos);
instance.commands.focus();
}
}, 10);
} }
}; };