chore:linting,formatting,type fixing, ....
This commit is contained in:
@@ -25,7 +25,17 @@ export default defineConfig(
|
||||
rules: {
|
||||
// typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects.
|
||||
// see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors
|
||||
'no-undef': 'off'
|
||||
'no-undef': 'off',
|
||||
// Disable no-navigation-without-resolve as we're using resolveRoute from $app/paths
|
||||
'svelte/no-navigation-without-resolve': 'off',
|
||||
// Allow unused vars that start with underscore
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
await apiSync.deleteComment(commentId, findId);
|
||||
}
|
||||
|
||||
function canDeleteComment(comment: any): boolean {
|
||||
function canDeleteComment(comment: { user: { id: string } }): boolean {
|
||||
return Boolean(
|
||||
currentUserId && (comment.user.id === currentUserId || comment.user.id === 'current-user')
|
||||
);
|
||||
@@ -65,7 +65,7 @@
|
||||
|
||||
{#snippet loadingSkeleton()}
|
||||
<div class="loading-skeleton">
|
||||
{#each Array(3) as _}
|
||||
{#each Array(3) as _, index (index)}
|
||||
<div class="comment-skeleton">
|
||||
<Skeleton class="avatar-skeleton" />
|
||||
<div class="content-skeleton">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { ProfilePanel } from '$lib';
|
||||
import { resolveRoute } from '$app/paths';
|
||||
|
||||
type User = {
|
||||
id: string;
|
||||
@@ -12,7 +13,7 @@
|
||||
|
||||
<header class="app-header">
|
||||
<div class="header-content">
|
||||
<h1 class="app-title"><a href="/">Serengo</a></h1>
|
||||
<h1 class="app-title"><a href={resolveRoute('/')}>Serengo</a></h1>
|
||||
<div class="profile-container">
|
||||
<ProfilePanel
|
||||
username={user.username}
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
isLoading: false
|
||||
});
|
||||
|
||||
let apiSync: any = null;
|
||||
let apiSync: typeof import('$lib/stores/api-sync').apiSync | null = null;
|
||||
|
||||
// Initialize API sync and subscribe to global state
|
||||
onMount(async () => {
|
||||
@@ -65,13 +65,15 @@
|
||||
|
||||
// Subscribe to global state for this find
|
||||
const globalLikeState = apiSync.subscribeFindLikes(findId);
|
||||
globalLikeState.subscribe((state: any) => {
|
||||
globalLikeState.subscribe(
|
||||
(state: { isLiked: boolean; likeCount: number; isLoading: boolean }) => {
|
||||
likeState.set({
|
||||
isLiked: state.isLiked,
|
||||
likeCount: state.likeCount,
|
||||
isLoading: state.isLoading
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize API sync:', error);
|
||||
}
|
||||
@@ -81,8 +83,7 @@
|
||||
async function toggleLike() {
|
||||
if (!apiSync || !browser) return;
|
||||
|
||||
const currentState = likeState;
|
||||
if (currentState && (currentState as any).isLoading) return;
|
||||
if ($likeState.isLoading) return;
|
||||
|
||||
try {
|
||||
await apiSync.toggleLike(findId);
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
*/
|
||||
|
||||
let permissionStatus = $state<NotificationPermission>('default');
|
||||
let subscriptionStatus = $state<'idle' | 'subscribing' | 'subscribed' | 'error'>('idle');
|
||||
let errorMessage = $state<string>('');
|
||||
let showPrompt = $state<boolean>(false);
|
||||
let isSupported = $state<boolean>(false);
|
||||
|
||||
@@ -57,8 +55,6 @@
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[NotificationManager] Error initializing notifications:', error);
|
||||
subscriptionStatus = 'error';
|
||||
errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,13 +71,9 @@
|
||||
await subscribeToNotifications();
|
||||
} else {
|
||||
console.log('[NotificationManager] Permission not granted');
|
||||
subscriptionStatus = 'error';
|
||||
errorMessage = 'Notification permission was not granted';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[NotificationManager] Error enabling notifications:', error);
|
||||
subscriptionStatus = 'error';
|
||||
errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +86,6 @@
|
||||
async function subscribeToNotifications() {
|
||||
try {
|
||||
console.log('[NotificationManager] subscribeToNotifications called');
|
||||
subscriptionStatus = 'subscribing';
|
||||
|
||||
// Get or register service worker
|
||||
let registration = await navigator.serviceWorker.getRegistration();
|
||||
@@ -161,12 +152,9 @@
|
||||
throw new Error('Failed to save subscription to server');
|
||||
}
|
||||
|
||||
subscriptionStatus = 'subscribed';
|
||||
console.log('[NotificationManager] Successfully subscribed to push notifications!');
|
||||
} catch (error) {
|
||||
console.error('[NotificationManager] Error subscribing to push notifications:', error);
|
||||
subscriptionStatus = 'error';
|
||||
errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,17 +36,16 @@
|
||||
|
||||
isLoading = true;
|
||||
try {
|
||||
const params = new URLSearchParams({
|
||||
action: 'autocomplete',
|
||||
query: query.trim()
|
||||
});
|
||||
const searchParams = new URL('/api/places', window.location.origin).searchParams;
|
||||
searchParams.set('action', 'autocomplete');
|
||||
searchParams.set('query', query.trim());
|
||||
|
||||
if ($coordinates) {
|
||||
params.set('lat', $coordinates.latitude.toString());
|
||||
params.set('lng', $coordinates.longitude.toString());
|
||||
searchParams.set('lat', $coordinates.latitude.toString());
|
||||
searchParams.set('lng', $coordinates.longitude.toString());
|
||||
}
|
||||
|
||||
const response = await fetch(`/api/places?${params}`);
|
||||
const response = await fetch(`/api/places?${searchParams}`);
|
||||
if (response.ok) {
|
||||
suggestions = await response.json();
|
||||
showSuggestions = true;
|
||||
@@ -179,7 +178,7 @@
|
||||
<div class="suggestion-content">
|
||||
<span class="suggestion-name">{suggestion.description}</span>
|
||||
<div class="suggestion-types">
|
||||
{#each suggestion.types.slice(0, 2) as type}
|
||||
{#each suggestion.types.slice(0, 2) as type, index (index)}
|
||||
<span class="suggestion-type">{type.replace(/_/g, ' ')}</span>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { enhance } from '$app/forms';
|
||||
import { resolveRoute } from '$app/paths';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -81,7 +82,7 @@
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem class="friends-item">
|
||||
<a href="/friends" class="friends-link">Friends</a>
|
||||
<a href={resolveRoute('/friends')} class="friends-link">Friends</a>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem class="notification-settings-item" onclick={openNotificationSettingsSheet}>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { cn, type WithElementRef } from '$lib/utils.js';
|
||||
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements';
|
||||
import { type VariantProps, tv } from 'tailwind-variants';
|
||||
import { resolveRoute } from '$app/paths';
|
||||
|
||||
export const buttonVariants = tv({
|
||||
base: "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium outline-none transition-all focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
||||
@@ -58,7 +59,7 @@
|
||||
bind:this={ref}
|
||||
data-slot="button"
|
||||
class={cn(buttonVariants({ variant, size }), className)}
|
||||
href={disabled ? undefined : href}
|
||||
href={disabled ? undefined : resolveRoute(href)}
|
||||
aria-disabled={disabled}
|
||||
role={disabled ? 'link' : undefined}
|
||||
tabindex={disabled ? -1 : undefined}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { enhance } from '$app/forms';
|
||||
import { goto } from '$app/navigation';
|
||||
import { resolveRoute } from '$app/paths';
|
||||
import { Button } from '$lib/components/button/index.js';
|
||||
import * as Card from '$lib/components/card/index.js';
|
||||
import { Label } from '$lib/components/label/index.js';
|
||||
@@ -62,7 +63,11 @@
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<Button variant="outline" class="mt-4 w-full" onclick={() => goto('/login/google')}>
|
||||
<Button
|
||||
variant="outline"
|
||||
class="mt-4 w-full"
|
||||
onclick={() => goto(resolveRoute('/login/google'))}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M12.48 10.92v3.28h7.84c-.24 1.84-.853 3.187-1.787 4.133-1.147 1.147-2.933 2.4-6.053 2.4-4.827 0-8.6-3.893-8.6-8.72s3.773-8.72 8.6-8.72c2.6 0 4.507 1.027 5.907 2.347l2.307-2.307C18.747 1.44 16.133 0 12.48 0 5.867 0 .307 5.387.307 12s5.56 12 12.173 12c3.573 0 6.267-1.173 8.373-3.36 2.16-2.16 2.84-5.213 2.84-7.667 0-.76-.053-1.467-.173-2.053H12.48z"
|
||||
|
||||
@@ -4,7 +4,11 @@ import type { NotificationInsert } from './db/schema';
|
||||
import { eq, and, desc } from 'drizzle-orm';
|
||||
import { nanoid } from 'nanoid';
|
||||
|
||||
export type NotificationType = 'friend_request' | 'friend_accepted' | 'find_liked' | 'find_commented';
|
||||
export type NotificationType =
|
||||
| 'friend_request'
|
||||
| 'friend_accepted'
|
||||
| 'find_liked'
|
||||
| 'find_commented';
|
||||
|
||||
export interface CreateNotificationData {
|
||||
userId: string;
|
||||
@@ -106,10 +110,7 @@ export class NotificationService {
|
||||
*/
|
||||
async markAsRead(notificationIds: string[]): Promise<void> {
|
||||
for (const id of notificationIds) {
|
||||
await db
|
||||
.update(notification)
|
||||
.set({ isRead: true })
|
||||
.where(eq(notification.id, id));
|
||||
await db.update(notification).set({ isRead: true }).where(eq(notification.id, id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,20 +118,14 @@ export class NotificationService {
|
||||
* Mark a single notification as read
|
||||
*/
|
||||
async markOneAsRead(notificationId: string): Promise<void> {
|
||||
await db
|
||||
.update(notification)
|
||||
.set({ isRead: true })
|
||||
.where(eq(notification.id, notificationId));
|
||||
await db.update(notification).set({ isRead: true }).where(eq(notification.id, notificationId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark all notifications as read for a user
|
||||
*/
|
||||
async markAllAsRead(userId: string): Promise<void> {
|
||||
await db
|
||||
.update(notification)
|
||||
.set({ isRead: true })
|
||||
.where(eq(notification.userId, userId));
|
||||
await db.update(notification).set({ isRead: true }).where(eq(notification.userId, userId));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,11 +4,7 @@ import { notificationSubscription } from './db/schema';
|
||||
import type { NotificationSubscriptionInsert } from './db/schema';
|
||||
import { eq, and } from 'drizzle-orm';
|
||||
import { nanoid } from 'nanoid';
|
||||
import {
|
||||
VAPID_PUBLIC_KEY,
|
||||
VAPID_PRIVATE_KEY,
|
||||
VAPID_SUBJECT
|
||||
} from '$env/static/private';
|
||||
import { VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, VAPID_SUBJECT } from '$env/static/private';
|
||||
|
||||
// Initialize web-push with VAPID keys
|
||||
if (!VAPID_PUBLIC_KEY || !VAPID_PRIVATE_KEY || !VAPID_SUBJECT) {
|
||||
@@ -88,7 +84,10 @@ export class PushService {
|
||||
await db
|
||||
.delete(notificationSubscription)
|
||||
.where(
|
||||
and(eq(notificationSubscription.userId, userId), eq(notificationSubscription.endpoint, endpoint))
|
||||
and(
|
||||
eq(notificationSubscription.userId, userId),
|
||||
eq(notificationSubscription.endpoint, endpoint)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -100,7 +99,10 @@ export class PushService {
|
||||
.select()
|
||||
.from(notificationSubscription)
|
||||
.where(
|
||||
and(eq(notificationSubscription.userId, userId), eq(notificationSubscription.isActive, true))
|
||||
and(
|
||||
eq(notificationSubscription.userId, userId),
|
||||
eq(notificationSubscription.isActive, true)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ export const POST: RequestHandler = async ({ params, locals, request }) => {
|
||||
const commentId = crypto.randomUUID();
|
||||
const now = new Date();
|
||||
|
||||
const [newComment] = await db
|
||||
await db
|
||||
.insert(findComment)
|
||||
.values({
|
||||
id: commentId,
|
||||
@@ -115,7 +115,10 @@ export const POST: RequestHandler = async ({ params, locals, request }) => {
|
||||
|
||||
if (findData.length > 0 && findData[0].userId !== session.userId) {
|
||||
const findOwner = findData[0];
|
||||
const shouldNotify = await notificationService.shouldNotify(findOwner.userId, 'find_commented');
|
||||
const shouldNotify = await notificationService.shouldNotify(
|
||||
findOwner.userId,
|
||||
'find_commented'
|
||||
);
|
||||
|
||||
if (shouldNotify) {
|
||||
// Get commenter's username
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { json } from '@sveltejs/kit';
|
||||
import { db } from '$lib/server/db';
|
||||
import { findComment, user } from '$lib/server/db/schema';
|
||||
import { eq, and } from 'drizzle-orm';
|
||||
import { findComment } from '$lib/server/db/schema';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import type { RequestHandler } from './$types';
|
||||
|
||||
export const DELETE: RequestHandler = async ({ params, locals }) => {
|
||||
|
||||
@@ -41,7 +41,10 @@ export const PATCH: RequestHandler = async ({ locals, request }) => {
|
||||
} else if (Array.isArray(notificationIds) && notificationIds.length > 0) {
|
||||
await notificationService.markAsRead(notificationIds);
|
||||
} else {
|
||||
return json({ error: 'Invalid request: provide notificationIds or markAll' }, { status: 400 });
|
||||
return json(
|
||||
{ error: 'Invalid request: provide notificationIds or markAll' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
return json({ success: true });
|
||||
@@ -66,7 +69,10 @@ export const DELETE: RequestHandler = async ({ locals, request }) => {
|
||||
} else if (notificationId) {
|
||||
await notificationService.deleteNotification(notificationId, user.id);
|
||||
} else {
|
||||
return json({ error: 'Invalid request: provide notificationId or deleteAll' }, { status: 400 });
|
||||
return json(
|
||||
{ error: 'Invalid request: provide notificationId or deleteAll' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
return json({ success: true });
|
||||
|
||||
@@ -134,10 +134,10 @@
|
||||
// Search users when query changes with debounce
|
||||
let searchTimeout: ReturnType<typeof setTimeout>;
|
||||
$effect(() => {
|
||||
// Track searchQuery dependency explicitly
|
||||
searchQuery;
|
||||
if (searchQuery) {
|
||||
clearTimeout(searchTimeout);
|
||||
searchTimeout = setTimeout(searchUsers, 300);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user