styling fixes
This commit is contained in:
@@ -159,7 +159,7 @@ export default function CommentBlock(props: CommentBlockProps) {
|
||||
|
||||
const isAnonymous = () => props.privilegeLevel === "anonymous";
|
||||
|
||||
const replyIconColor = () => "#fb923c";
|
||||
const replyIconColor = () => "var(--color-peach)";
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -169,13 +169,13 @@ export default function CommentBlock(props: CommentBlockProps) {
|
||||
onClick={collapseCommentToggle}
|
||||
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>
|
||||
</Show>
|
||||
|
||||
{/* Expanded state */}
|
||||
<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">
|
||||
{/* Vote buttons column */}
|
||||
<div
|
||||
@@ -191,8 +191,8 @@ export default function CommentBlock(props: CommentBlockProps) {
|
||||
<div
|
||||
class={`h-5 w-5 ${
|
||||
hasUpvoted()
|
||||
? "fill-emerald-500"
|
||||
: `fill-black hover:fill-emerald-500 dark:fill-white ${
|
||||
? "fill-green"
|
||||
: `fill-text hover:fill-green ${
|
||||
isAnonymous() ? "tooltip z-50" : ""
|
||||
}`
|
||||
}`}
|
||||
@@ -218,8 +218,8 @@ export default function CommentBlock(props: CommentBlockProps) {
|
||||
<div
|
||||
class={`h-5 w-5 ${
|
||||
hasDownvoted()
|
||||
? "fill-rose-500"
|
||||
: `fill-black hover:fill-rose-500 dark:fill-white ${
|
||||
? "fill-red"
|
||||
: `fill-text hover:fill-red ${
|
||||
isAnonymous() ? "tooltip z-50" : ""
|
||||
}`
|
||||
}`}
|
||||
@@ -239,7 +239,7 @@ export default function CommentBlock(props: CommentBlockProps) {
|
||||
{/* Collapse toggle line */}
|
||||
<button onClick={collapseCommentToggle} class="z-0 px-2">
|
||||
<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` }}
|
||||
/>
|
||||
</button>
|
||||
@@ -292,7 +292,7 @@ export default function CommentBlock(props: CommentBlockProps) {
|
||||
<TrashIcon
|
||||
height={24}
|
||||
width={24}
|
||||
stroke="red"
|
||||
stroke="var(--color-red)"
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
</Show>
|
||||
|
||||
@@ -45,10 +45,10 @@ export default function CommentDeletionPrompt(
|
||||
|
||||
return (
|
||||
<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
|
||||
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={() => {}}>
|
||||
<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">
|
||||
Comment Deletion
|
||||
</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">
|
||||
{/* Comment body will be passed as prop */}
|
||||
</div>
|
||||
@@ -132,9 +132,9 @@ export default function CommentDeletionPrompt(
|
||||
disabled={props.commentDeletionLoading || !isDeleteEnabled()}
|
||||
class={`${
|
||||
props.commentDeletionLoading || !isDeleteEnabled()
|
||||
? "bg-zinc-400"
|
||||
: "border-orange-500 bg-orange-400 hover:bg-orange-500"
|
||||
} rounded border px-4 py-2 text-white shadow-md transition-all duration-300 ease-in-out active:scale-90`}
|
||||
? "bg-surface2 opacity-50"
|
||||
: "border-red bg-red hover:brightness-125"
|
||||
} rounded border px-4 py-2 text-base shadow-md transition-all duration-300 ease-in-out active:scale-90`}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
|
||||
@@ -21,7 +21,7 @@ export default function CommentInputBlock(props: CommentInputBlockProps) {
|
||||
if (props.privilegeLevel === "user" || props.privilegeLevel === "admin") {
|
||||
return (
|
||||
<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}>
|
||||
<div class="textarea-group blog">
|
||||
<textarea
|
||||
@@ -43,9 +43,9 @@ export default function CommentInputBlock(props: CommentInputBlockProps) {
|
||||
disabled={props.commentSubmitLoading}
|
||||
class={`${
|
||||
props.commentSubmitLoading
|
||||
? "bg-zinc-400"
|
||||
: "border-orange-500 bg-orange-400 hover:bg-orange-500"
|
||||
} rounded border px-4 py-2 font-light text-white shadow-md transition-all duration-300 ease-in-out active:scale-90`}
|
||||
? "bg-surface2 opacity-50"
|
||||
: "border-sapphire bg-blue hover:brightness-125"
|
||||
} rounded border px-4 py-2 text-base font-light shadow-md transition-all duration-300 ease-in-out active:scale-90`}
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
|
||||
@@ -130,7 +130,16 @@ export default function CommentSectionWrapper(
|
||||
|
||||
// Helper functions
|
||||
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(
|
||||
JSON.stringify({
|
||||
action: "channelUpdate",
|
||||
@@ -139,25 +148,49 @@ export default function CommentSectionWrapper(
|
||||
invoker_id: props.currentUserID
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error sending channel update:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// Comment creation
|
||||
const newComment = async (commentBody: string, parentCommentID?: number) => {
|
||||
setCommentSubmitLoading(true);
|
||||
|
||||
if (!props.currentUserID) {
|
||||
console.warn("Cannot create comment: user not authenticated");
|
||||
setCommentSubmitLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (commentBody && socket) {
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
action: "commentCreation",
|
||||
commentBody: commentBody,
|
||||
postType: "blog",
|
||||
postID: props.id,
|
||||
parentCommentID: parentCommentID,
|
||||
invokerID: props.currentUserID
|
||||
})
|
||||
);
|
||||
try {
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
action: "commentCreation",
|
||||
commentBody: commentBody,
|
||||
postType: "blog",
|
||||
postID: props.id,
|
||||
parentCommentID: parentCommentID,
|
||||
invokerID: props.currentUserID
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error sending comment creation:", error);
|
||||
// Fallback to HTTP API on WebSocket error
|
||||
await fallbackCommentCreation(commentBody, parentCommentID);
|
||||
}
|
||||
} else {
|
||||
// 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 res = await fetch(
|
||||
`${domain}/api/database/comments/create/blog/${props.id}`,
|
||||
@@ -179,6 +212,9 @@ export default function CommentSectionWrapper(
|
||||
commentParent: parentCommentID
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error in fallback comment creation:", error);
|
||||
setCommentSubmitLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -237,17 +273,29 @@ export default function CommentSectionWrapper(
|
||||
// Comment updating
|
||||
const editComment = async (body: string, comment_id: number) => {
|
||||
setCommentEditLoading(true);
|
||||
|
||||
if (!props.currentUserID) {
|
||||
console.warn("Cannot edit comment: user not authenticated");
|
||||
setCommentEditLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (socket) {
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
action: "commentUpdate",
|
||||
commentBody: body,
|
||||
postType: "blog",
|
||||
postID: props.id,
|
||||
commentID: comment_id,
|
||||
invokerID: props.currentUserID
|
||||
})
|
||||
);
|
||||
try {
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
action: "commentUpdate",
|
||||
commentBody: body,
|
||||
postType: "blog",
|
||||
postID: props.id,
|
||||
commentID: comment_id,
|
||||
invokerID: props.currentUserID
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error sending comment update:", error);
|
||||
setCommentEditLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -290,17 +338,29 @@ export default function CommentSectionWrapper(
|
||||
deletionType: DeletionType
|
||||
) => {
|
||||
setCommentDeletionLoading(true);
|
||||
|
||||
if (!props.currentUserID) {
|
||||
console.warn("Cannot delete comment: user not authenticated");
|
||||
setCommentDeletionLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (socket) {
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
action: "commentDeletion",
|
||||
deleteType: deletionType,
|
||||
commentID: commentID,
|
||||
invokerID: props.currentUserID,
|
||||
postType: "blog",
|
||||
postID: props.id
|
||||
})
|
||||
);
|
||||
try {
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
action: "commentDeletion",
|
||||
deleteType: deletionType,
|
||||
commentID: commentID,
|
||||
invokerID: props.currentUserID,
|
||||
postType: "blog",
|
||||
postID: props.id
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error sending comment deletion:", error);
|
||||
setCommentDeletionLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -393,17 +453,26 @@ export default function CommentSectionWrapper(
|
||||
|
||||
// Reaction handling
|
||||
const commentReaction = (reactionType: ReactionType, commentID: number) => {
|
||||
if (!props.currentUserID) {
|
||||
console.warn("Cannot react to comment: user not authenticated");
|
||||
return;
|
||||
}
|
||||
|
||||
if (socket) {
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
action: "commentReaction",
|
||||
postType: "blog",
|
||||
postID: props.id,
|
||||
commentID: commentID,
|
||||
invokerID: props.currentUserID,
|
||||
reactionType: reactionType
|
||||
})
|
||||
);
|
||||
try {
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
action: "commentReaction",
|
||||
postType: "blog",
|
||||
postID: props.id,
|
||||
commentID: commentID,
|
||||
invokerID: props.currentUserID,
|
||||
reactionType: reactionType
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error sending comment reaction:", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ export default function CommentSorting(props: CommentSortingProps) {
|
||||
{(topLevelComment) => (
|
||||
<div
|
||||
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
|
||||
when={showingBlock().get(topLevelComment.id)}
|
||||
|
||||
@@ -43,7 +43,7 @@ export default function CommentSortingSelect(props: CommentSortingSelectProps) {
|
||||
<button
|
||||
type="button"
|
||||
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="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}
|
||||
height={24}
|
||||
width={24}
|
||||
class="fill-zinc-900 dark:fill-white"
|
||||
class="fill-text"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<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}>
|
||||
{(sort) => (
|
||||
<button
|
||||
@@ -65,8 +65,8 @@ export default function CommentSortingSelect(props: CommentSortingSelectProps) {
|
||||
onClick={() => handleSelect(sort.val)}
|
||||
class={`relative w-full cursor-default py-2 pr-4 pl-10 text-left select-none ${
|
||||
props.selectedSorting.val === sort.val
|
||||
? "bg-orange-100 text-orange-900"
|
||||
: "text-zinc-900 hover:bg-orange-50 dark:text-white dark:hover:bg-zinc-800"
|
||||
? "bg-peach text-base brightness-75"
|
||||
: "text-text hover:brightness-125"
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
@@ -79,12 +79,12 @@ export default function CommentSortingSelect(props: CommentSortingSelectProps) {
|
||||
{sort.label}
|
||||
</span>
|
||||
<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
|
||||
strokeWidth={1}
|
||||
height={24}
|
||||
width={24}
|
||||
class="stroke-zinc-900 dark:stroke-white"
|
||||
class="stroke-text"
|
||||
/>
|
||||
</span>
|
||||
</Show>
|
||||
|
||||
@@ -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
|
||||
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={() => {}}>
|
||||
<Xmark strokeWidth={0.5} color="white" height={50} width={50} />
|
||||
<Xmark
|
||||
strokeWidth={0.5}
|
||||
color="var(--color-text)"
|
||||
height={50}
|
||||
width={50}
|
||||
/>
|
||||
</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
|
||||
</div>
|
||||
<form onSubmit={editCommentWrapper}>
|
||||
@@ -40,7 +45,7 @@ export default function EditCommentModal(props: EditCommentModalProps) {
|
||||
ref={bodyRef}
|
||||
placeholder=" "
|
||||
value={props.commentBody}
|
||||
class="underlinedInput w-full bg-transparent text-blue-300"
|
||||
class="underlinedInput text-blue w-full bg-transparent"
|
||||
rows={4}
|
||||
/>
|
||||
<span class="bar" />
|
||||
@@ -51,17 +56,17 @@ export default function EditCommentModal(props: EditCommentModalProps) {
|
||||
type="submit"
|
||||
disabled={props.editCommentLoading}
|
||||
class={`${
|
||||
props.editCommentLoading ? "bg-zinc-400" : ""
|
||||
} 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`}
|
||||
props.editCommentLoading
|
||||
? "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
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<Show when={showNoChange()}>
|
||||
<div class="text-center text-red-500 italic">
|
||||
No change detected
|
||||
</div>
|
||||
<div class="text-red text-center italic">No change detected</div>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -49,7 +49,7 @@ export default function ReactionBar(props: ReactionBarProps) {
|
||||
<div
|
||||
class={`${
|
||||
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`}
|
||||
>
|
||||
@@ -57,7 +57,7 @@ export default function ReactionBar(props: ReactionBarProps) {
|
||||
{({ type, Component }) => (
|
||||
<Show when={shouldShowEmoji(type)}>
|
||||
<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}>
|
||||
{getReactionCount(type)}
|
||||
</Show>
|
||||
|
||||
@@ -348,29 +348,50 @@ export default function TextEditor(props: TextEditorProps) {
|
||||
|
||||
const title = window.prompt("Section title:", "Click to expand");
|
||||
|
||||
if (title !== null) {
|
||||
instance
|
||||
.chain()
|
||||
.focus()
|
||||
.insertContent({
|
||||
type: "details",
|
||||
content: [
|
||||
{
|
||||
type: "detailsSummary",
|
||||
content: [{ type: "text", text: title }]
|
||||
},
|
||||
{
|
||||
type: "detailsContent",
|
||||
content: [
|
||||
{
|
||||
type: "paragraph",
|
||||
content: [{ type: "text", text: "Add your content here..." }]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
.run();
|
||||
if (title !== null && title.trim() !== "") {
|
||||
const content = {
|
||||
type: "details",
|
||||
attrs: { open: true },
|
||||
content: [
|
||||
{
|
||||
type: "detailsSummary",
|
||||
content: [{ type: "text", text: title }]
|
||||
},
|
||||
{
|
||||
type: "detailsContent",
|
||||
content: [
|
||||
{
|
||||
type: "paragraph"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user