removed excess comments

This commit is contained in:
Michael Freno
2026-01-04 11:14:54 -05:00
parent b81de6441b
commit 7e89e6dda2
68 changed files with 72 additions and 941 deletions

View File

@@ -2,7 +2,6 @@ import { createTRPCProxyClient, httpBatchLink, loggerLink } from "@trpc/client";
import type { AppRouter } from "~/server/api/root";
const getBaseUrl = () => {
// Browser: use relative URL
if (typeof window !== "undefined") return "";
const domain = import.meta.env.VITE_DOMAIN;
@@ -27,12 +26,10 @@ function getCSRFToken(): string | undefined {
export const api = createTRPCProxyClient<AppRouter>({
links: [
// Only enable logging in development mode
...(process.env.NODE_ENV === "development"
? [
loggerLink({
enabled: (opts) => {
// Suppress 401 UNAUTHORIZED errors from logs
const is401 =
opts.direction === "down" &&
opts.result instanceof Error &&
@@ -42,7 +39,6 @@ export const api = createTRPCProxyClient<AppRouter>({
})
]
: []),
// identifies what url will handle trpc requests
httpBatchLink({
url: `${getBaseUrl()}/api/trpc`,
headers: () => {

View File

@@ -4,11 +4,7 @@
*/
/**
* Safe fetch wrapper that suppresses console errors for expected 401 responses
* Use this instead of direct fetch() calls when 401s are expected (e.g., auth checks)
* @param input - URL or Request object
* @param init - Fetch options
* @returns Promise<Response>
* Fetch wrapper for auth checks where 401s are expected and should not trigger console errors
*/
export async function safeFetch(
input: RequestInfo | URL,
@@ -18,17 +14,12 @@ export async function safeFetch(
const response = await fetch(input, init);
return response;
} catch (error) {
// Re-throw the error - this is for actual network failures
throw error;
}
}
/**
* Inserts soft hyphens (&shy;) into long words to enable manual hyphenation
* Works with Typewriter component since it uses actual characters
* @param text - The text to add hyphens to
* @param minWordLength - Minimum word length to hyphenate (default 8)
* @returns Text with soft hyphens inserted
* Inserts soft hyphens (&shy;) for manual hyphenation. Uses actual characters for Typewriter compatibility.
*/
export function insertSoftHyphens(
text: string,
@@ -37,26 +28,20 @@ export function insertSoftHyphens(
return text
.split(" ")
.map((word) => {
// Skip short words
if (word.length < minWordLength) return word;
// Common English hyphenation patterns
const patterns = [
// Prefixes (break after)
{
pattern:
/^(un|re|in|dis|en|non|pre|pro|anti|de|mis|over|sub|super|trans|under)(.+)/i,
split: 1
},
// Suffixes (break before)
{
pattern:
/(.+)(ing|tion|sion|ness|ment|able|ible|ful|less|ship|hood|ward|like)$/i,
split: 1
},
// Double consonants (break between)
{ pattern: /(.+[aeiou])([bcdfghjklmnpqrstvwxyz])\2(.+)/i, split: 2 },
// Compound words with common parts
{
pattern:
/(.+)(stand|work|time|place|where|thing|back|over|under|out)$/i,
@@ -68,16 +53,13 @@ export function insertSoftHyphens(
const match = word.match(pattern);
if (match) {
if (split === 1) {
// Break after first capture group
return match[1] + "\u00AD" + match[2];
} else if (split === 2) {
// Break between doubled consonants
return match[1] + match[2] + "\u00AD" + match[2] + match[3];
}
}
}
// Fallback: Insert soft hyphen every 6-8 characters in very long words
if (word.length > 12) {
const chunks: string[] = [];
for (let i = 0; i < word.length; i += 6) {

View File

@@ -1,24 +1,8 @@
/**
* Comment System Utility Functions
*
* Shared utility functions for:
* - Comment sorting algorithms
* - Comment filtering and tree building
* - Debouncing
*/
import type { Comment, CommentReaction, SortingMode } from "~/types/comment";
import { getSQLFormattedDate } from "./date-utils";
export { getSQLFormattedDate };
// ============================================================================
// Comment Tree Utilities
// ============================================================================
/**
* Gets all child comments for a given parent comment ID
*/
export function getChildComments(
parentCommentID: number,
allComments: Comment[] | undefined
@@ -30,9 +14,6 @@ export function getChildComments(
);
}
/**
* Counts the total number of comments including all nested children
*/
export function getTotalCommentCount(
topLevelComments: Comment[],
allComments: Comment[]
@@ -40,10 +21,6 @@ export function getTotalCommentCount(
return allComments.length;
}
/**
* Gets the nesting level of a comment in the tree
* Top-level comments (parent_comment_id = -1 or null) are level 0
*/
export function getCommentLevel(
comment: Comment,
allComments: Comment[]
@@ -66,17 +43,9 @@ export function getCommentLevel(
return level;
}
// ============================================================================
// Comment Sorting Algorithms
// ============================================================================
/**
* @deprecated Server-side sorting is now implemented in the blog post route.
* Comments are sorted by SQL queries for better performance.
* This function remains for backward compatibility only.
*
* Calculates "hot" score for a comment based on votes and time
* Uses logarithmic decay for older comments
* @deprecated Server-side SQL sorting preferred for performance
* Logarithmic decay formula: score / log(age + 2)
*/
function calculateHotScore(
upvotes: number,
@@ -88,17 +57,11 @@ function calculateHotScore(
const commentTime = new Date(date).getTime();
const ageInHours = (now - commentTime) / (1000 * 60 * 60);
// Logarithmic decay: score / log(age + 2)
// Adding 2 prevents division by zero for very new comments
return score / Math.log10(ageInHours + 2);
}
/**
* @deprecated Server-side sorting is now implemented in the blog post route.
* Use SQL-based sorting instead for better performance.
* This function remains for backward compatibility only.
*
* Counts upvotes for a comment from reaction map
* @deprecated Server-side SQL sorting preferred for performance
*/
function getUpvoteCount(
commentID: number,
@@ -111,11 +74,7 @@ function getUpvoteCount(
}
/**
* @deprecated Server-side sorting is now implemented in the blog post route.
* Use SQL-based sorting instead for better performance.
* This function remains for backward compatibility only.
*
* Counts downvotes for a comment from reaction map
* @deprecated Server-side SQL sorting preferred for performance
*/
function getDownvoteCount(
commentID: number,
@@ -128,18 +87,7 @@ function getDownvoteCount(
}
/**
* @deprecated Server-side sorting is now implemented in the blog post route.
* Comments are now sorted by SQL queries in src/routes/blog/[title]/index.tsx
* for better performance and reduced client-side processing.
* This function remains for backward compatibility only.
*
* Sorts comments based on the selected sorting mode
*
* Modes:
* - newest: Most recent first
* - oldest: Oldest first
* - highest_rated: Most upvotes minus downvotes
* - hot: Combines votes and recency (Reddit-style)
* @deprecated Use server-side SQL sorting in routes/blog/[title]/index.tsx for better performance
*/
export function sortComments(
comments: Comment[],
@@ -190,20 +138,10 @@ export function sortComments(
}
}
// ============================================================================
// Validation Utilities
// ============================================================================
/**
* Validates that a comment body meets requirements
*/
export function isValidCommentBody(body: string): boolean {
return body.trim().length > 0 && body.length <= 10000;
}
/**
* Checks if a user can modify (edit/delete) a comment
*/
export function canModifyComment(
userID: string,
commenterID: string,
@@ -214,9 +152,6 @@ export function canModifyComment(
return userID === commenterID;
}
/**
* Checks if a user can delete with database-level deletion
*/
export function canDatabaseDelete(
privilegeLevel: "admin" | "user" | "anonymous"
): boolean {

View File

@@ -12,7 +12,6 @@ export function createWindowWidth(debounceMs?: number): Accessor<number> {
const [width, setWidth] = createSignal(initialWidth);
onMount(() => {
// Sync to actual client width immediately on mount to avoid hydration mismatch
setWidth(window.innerWidth);
let timeoutId: ReturnType<typeof setTimeout> | undefined;
@@ -86,7 +85,6 @@ export async function resizeImage(
img.onload = () => {
let { width, height } = img;
// Calculate new dimensions maintaining aspect ratio
if (width > maxWidth || height > maxHeight) {
const aspectRatio = width / height;
@@ -102,10 +100,8 @@ export async function resizeImage(
canvas.width = width;
canvas.height = height;
// Draw image on canvas with new dimensions
ctx.drawImage(img, 0, 0, width, height);
// Convert canvas to blob
canvas.toBlob(
(blob) => {
if (blob) {
@@ -123,7 +119,6 @@ export async function resizeImage(
reject(new Error("Failed to load image"));
};
// Load image from file
const reader = new FileReader();
reader.onload = (e) => {
if (e.target?.result) {

View File

@@ -14,7 +14,6 @@ export default async function AddImageToS3(
try {
const filename = (file as File).name;
// Get pre-signed URL from tRPC endpoint
const { uploadURL, key } = await api.misc.getPreSignedURL.mutate({
type,
title,
@@ -23,11 +22,9 @@ export default async function AddImageToS3(
console.log("url: " + uploadURL, "key: " + key);
// Extract content type from filename extension
const ext = /^.+\.([^.]+)$/.exec(filename);
const contentType = ext ? `image/${ext[1]}` : "application/octet-stream";
// Upload original file to S3 using pre-signed URL
const uploadResponse = await fetch(uploadURL, {
method: "PUT",
headers: {
@@ -40,19 +37,12 @@ export default async function AddImageToS3(
throw new Error("Failed to upload file to S3");
}
// For blog cover images, also create and upload a thumbnail
if (type === "blog") {
try {
// Create thumbnail (max 200x200px for sidebar display)
const thumbnail = await resizeImage(file, 200, 200, 0.8);
// Generate thumbnail filename: insert "-small" before extension
const thumbnailFilename = filename.replace(
/(\.[^.]+)$/,
"-small$1"
);
const thumbnailFilename = filename.replace(/(\.[^.]+)$/, "-small$1");
// Get pre-signed URL for thumbnail
const { uploadURL: thumbnailUploadURL } =
await api.misc.getPreSignedURL.mutate({
type,
@@ -60,24 +50,21 @@ export default async function AddImageToS3(
filename: thumbnailFilename
});
// Upload thumbnail to S3
const thumbnailUploadResponse = await fetch(thumbnailUploadURL, {
method: "PUT",
headers: {
"Content-Type": "image/jpeg" // Thumbnails are always JPEG
"Content-Type": "image/jpeg"
},
body: thumbnail
});
if (!thumbnailUploadResponse.ok) {
console.error("Failed to upload thumbnail to S3");
// Don't fail the entire upload if thumbnail fails
} else {
console.log("Thumbnail uploaded successfully");
}
} catch (thumbnailError) {
console.error("Thumbnail creation/upload failed:", thumbnailError);
// Don't fail the entire upload if thumbnail fails
}
}

View File

@@ -18,7 +18,6 @@ export interface CommandContext {
}
export const createTerminalCommands = (context: CommandContext) => {
// Define available routes
const routes = [
{ path: "/", name: "home" },
{ path: "/blog", name: "blog" },
@@ -99,7 +98,6 @@ export const createTerminalCommands = (context: CommandContext) => {
clear: {
action: () => {
context.addToHistory("clear", "", "info");
// Clear will be handled by the component
},
description: "Clear terminal history"
},
@@ -231,7 +229,6 @@ export const createTerminalCommands = (context: CommandContext) => {
}
};
// Add all cd variants for each route
routes.forEach((route) => {
commands[`cd ${route.name}`] = {
action: () => context.navigate(route.path),

View File

@@ -8,14 +8,11 @@ import { VALIDATION_CONFIG } from "~/config";
* Validate email format
*/
export function isValidEmail(email: string): boolean {
// Basic email format check
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
return false;
}
// Additional checks for invalid patterns
// Reject consecutive dots
if (email.includes("..")) {
return false;
}
@@ -39,24 +36,20 @@ export function validatePassword(password: string): {
const errors: string[] = [];
let includesSpecial = false;
// Minimum length from config
if (password.length < VALIDATION_CONFIG.MIN_PASSWORD_LENGTH) {
errors.push(
`Password must be at least ${VALIDATION_CONFIG.MIN_PASSWORD_LENGTH} characters long`
);
}
// Require uppercase letter (if configured)
if (VALIDATION_CONFIG.PASSWORD_REQUIRE_UPPERCASE && !/[A-Z]/.test(password)) {
errors.push("Password must contain at least one uppercase letter");
}
// Require lowercase letter (always required for balanced security)
if (!/[a-z]/.test(password)) {
errors.push("Password must contain at least one lowercase letter");
}
// Require number (if configured)
if (VALIDATION_CONFIG.PASSWORD_REQUIRE_NUMBER && !/[0-9]/.test(password)) {
errors.push("Password must contain at least one number");
}
@@ -64,12 +57,11 @@ export function validatePassword(password: string): {
if (/[^A-Za-z0-9]/.test(password)) {
includesSpecial = true;
}
// Require special character (if configured)
if (VALIDATION_CONFIG.PASSWORD_REQUIRE_SPECIAL && !includesSpecial) {
errors.push("Password must contain at least one special character");
}
// Check for common weak passwords
const commonPasswords = [
"password",
"1234",
@@ -94,7 +86,6 @@ export function validatePassword(password: string): {
}
}
// Calculate password strength
let strength: PasswordStrength = "weak";
if (errors.length === 0) {