eslint autofix

This commit is contained in:
2026-04-04 17:01:59 -04:00
parent 61ea96928e
commit e18aa8c632
16 changed files with 79 additions and 78 deletions
+1
View File
@@ -72,6 +72,7 @@ export default defineConfig([
{
rules: {
"@typescript-eslint/no-explicit-any": "off",
"@/no-trailing-spaces": "warn",
"@typescript-eslint/no-unused-vars": [
"warn",
{
+3 -3
View File
@@ -18,9 +18,9 @@
}
</script>
<AuthGate
reconnect={handleReconnect}
{showServerSettings}
<AuthGate
reconnect={handleReconnect}
{showServerSettings}
onToggleServerSettings={handleToggleServerSettings}
{reconnectKey}
>
+2 -2
View File
@@ -28,7 +28,7 @@
<i class="fas fa-circle-notch fa-spin" style="font-size: 3rem; color: var(--brand); margin-bottom: 20px;"></i>
<h1>Connecting to SpacetimeDB...</h1>
<p style="color: var(--text-muted); margin-top: 8px; margin-bottom: 24px;">Establishing a secure connection to the chat server.</p>
<div style="background-color: var(--background-tertiary); padding: 16px; border-radius: 8px; margin-bottom: 24px; text-align: left; font-size: 0.8rem; border: 1px solid var(--background-modifier-accent); display: flex; flex-direction: column; gap: 12px;">
<div class="connection-detail">
<div style="color: var(--text-muted); font-weight: 800; text-transform: uppercase; font-size: 0.65rem; margin-bottom: 4px; letter-spacing: 0.05em;">Host</div>
@@ -41,7 +41,7 @@
</div>
{#if onCancel}
<button
<button
onclick={onCancel}
class="btn-secondary"
style="width: 100%;"
+1 -1
View File
@@ -74,7 +74,7 @@
</div>
{:else if shouldShowContent}
{#key reconnectKey}
<SpacetimeProvider
<SpacetimeProvider
onReconnectTrigger={reconnect}
onCancel={() => {
userWantsToConnect = false;
+6 -6
View File
@@ -3,13 +3,13 @@
import type { ChatService } from "../services/chat.svelte";
import type * as Types from "../../module_bindings/types";
let {
user,
size = "medium",
let {
user,
size = "medium",
class: className = "",
isTalking = false
}: {
user: Types.User | null | undefined,
isTalking = false
}: {
user: Types.User | null | undefined,
size?: "tiny" | "small" | "medium" | "large",
class?: string,
isTalking?: boolean
+7 -7
View File
@@ -19,7 +19,7 @@
const saved = localStorage.getItem("recent_emojis");
if (saved) {
try {
recentEmojis = JSON.parse(saved).map((id: any) =>
recentEmojis = JSON.parse(saved).map((id: any) =>
typeof id === 'string' && id.startsWith('custom:') ? BigInt(id.split(':')[1]) : id
);
} catch (e) {
@@ -32,20 +32,20 @@
let newRecent = [emoji, ...recentEmojis.filter(e => e !== emoji)];
newRecent = newRecent.slice(0, 7);
recentEmojis = newRecent;
localStorage.setItem("recent_emojis", JSON.stringify(newRecent.map(e =>
localStorage.setItem("recent_emojis", JSON.stringify(newRecent.map(e =>
typeof e === 'bigint' ? `custom:${e.toString()}` : e
)));
}
const filteredEmojis = $derived.by(() => {
const lowerSearch = searchTerm.toLowerCase();
const standard = EMOJIS.filter(e =>
e.name.toLowerCase().includes(lowerSearch) ||
const standard = EMOJIS.filter(e =>
e.name.toLowerCase().includes(lowerSearch) ||
e.char.includes(lowerSearch)
);
const custom = chat.customEmojis.filter(e =>
const custom = chat.customEmojis.filter(e =>
e.name.toLowerCase().includes(lowerSearch)
);
@@ -175,7 +175,7 @@
{#each categories as cat}
{@const catStandard = EMOJIS.filter(e => e.category === cat.id)}
{@const catCustom = cat.id === 'custom' ? chat.customEmojis : []}
{#if catStandard.length > 0 || catCustom.length > 0}
<div class="picker-section" id="cat-{cat.id}">
<div class="section-title">{cat.name}</div>
+6 -6
View File
@@ -3,10 +3,10 @@
import type { ChatService } from "../services/chat.svelte";
import { optimizeImage } from "../utils";
let {
activeChannelId,
activeThreadId,
isFullyAuthenticated
let {
activeChannelId,
activeThreadId,
isFullyAuthenticated
} = $props<{
activeChannelId: bigint | null,
activeThreadId: bigint | null,
@@ -27,7 +27,7 @@
// We only care about messageText changes here
const text = messageText;
const channelId = activeChannelId;
if (text.trim().length > 0 && channelId) {
// Start typing if not already
if (!untrack(() => isTyping)) {
@@ -91,7 +91,7 @@
// Fallback to raw if optimization fails
const buffer = await file.arrayBuffer();
const data = new Uint8Array(buffer);
let fileName = file.name || "image.png";
if (fileName === "image.png" || fileName === "blob") {
const now = new Date();
+7 -7
View File
@@ -36,7 +36,7 @@
const parts = tooltip.targetKey.split(":");
const msgId = BigInt(parts[0]);
const emojiKey = parts.slice(1).join(":");
const exists = chat.messageReactions.some(r =>
const exists = chat.messageReactions.some(r =>
r.messageId === msgId && (r.emoji || `custom:${r.customEmojiId}`) === emojiKey
);
if (!exists) {
@@ -51,7 +51,7 @@
const maxShow = 3;
const displayed = usernames.slice(0, maxShow);
const remaining = usernames.length - maxShow;
let emojiName = "";
if (group[0].emoji) {
emojiName = EMOJIS.find(e => e.char === group[0].emoji)?.name || "emoji";
@@ -215,12 +215,12 @@
<i class="far fa-smile"></i>
</button>
{#if activePickerMessageId === msg.id && pickerPos}
<div
<div
use:portal
class="emoji-picker-popover"
class="emoji-picker-popover"
style="position: fixed; left: {pickerPos.x}px; top: {pickerPos.y}px; transform: translateX(-100%) {pickerPos.placement === 'top' ? 'translateY(-100%)' : ''}; z-index: 1000;"
>
<EmojiPicker
<EmojiPicker
onSelect={(emoji, customId) => {
chat.handleToggleReaction(msg.id, emoji, customId);
activePickerMessageId = null;
@@ -313,7 +313,7 @@
{@const hasReacted = group.some(r => r.identity.isEqual(chat.identity))}
{@const emoji = group[0].emoji}
{@const customEmojiId = group[0].customEmojiId}
<button
<button
class="reaction-badge {hasReacted ? 'active' : ''}"
onclick={() => {
chat.handleToggleReaction(msg.id, emoji, customEmojiId);
@@ -354,7 +354,7 @@
</div>
{#if tooltip.visible}
<div
<div
use:portal
class="message-reaction-tooltip"
style="left: {tooltip.x}px; top: {tooltip.y}px;"
+14 -14
View File
@@ -45,7 +45,7 @@
// User removed their avatar
chat.handleSetAvatar(undefined);
}
onClose();
};
@@ -126,8 +126,8 @@
<div class="settings-sidebar">
<div class="sidebar-header">User Settings</div>
{#each categories as cat}
<button
class="sidebar-item {activeCategory === cat.id ? 'active' : ''}"
<button
class="sidebar-item {activeCategory === cat.id ? 'active' : ''}"
onclick={() => activeCategory = cat.id}
>
<i class={cat.icon}></i>
@@ -192,7 +192,7 @@
</button>
</div>
</div>
<div class="avatar-actions">
<button class="btn-secondary small" onclick={() => {
avatarPreview = null;
@@ -232,9 +232,9 @@
<input type="file" accept="image/*" onchange={onEmojiFileChange} style="display: none;" />
</label>
</div>
<button
type="submit"
class="btn-success"
<button
type="submit"
class="btn-success"
style="align-self: flex-end; height: 38px; padding: 0 20px;"
disabled={!newEmojiFile || !newEmojiName.trim() || isEmojiUploading}
>
@@ -277,23 +277,23 @@
<div class="voice-sensitivity-box">
<div class="label-with-action">
<label for="voice-threshold-slider">Input Sensitivity</label>
<button
class="test-mic-btn {webrtc.isTestingMic ? 'active' : ''}"
<button
class="test-mic-btn {webrtc.isTestingMic ? 'active' : ''}"
onclick={() => webrtc.toggleMicTest()}
>
<i class="fas {webrtc.isTestingMic ? 'fa-stop' : 'fa-microphone'}"></i>
{webrtc.isTestingMic ? "Stop Testing" : "Test Mic"}
</button>
</div>
<div class="voice-meter-container">
<div class="voice-meter-bg">
<div
class="voice-meter-fill"
<div
class="voice-meter-fill"
style="width: {normalizedLevel}%; background-color: {webrtc.currentInputLevel > webrtc.voiceThreshold ? 'var(--status-positive)' : 'var(--brand)'}; opacity: {webrtc.currentInputLevel > 0 ? 1 : 0};"
></div>
<div
class="voice-threshold-line"
<div
class="voice-threshold-line"
style="left: {normalizedThreshold}%;"
></div>
</div>
@@ -26,7 +26,7 @@
$effect(() => {
const text = threadMessageText;
const channelId = activeChannelId;
if (text.trim().length > 0 && channelId) {
// Start typing if not already
if (!untrack(() => isTyping)) {
+6 -6
View File
@@ -47,7 +47,7 @@
const parts = tooltip.targetKey.split(":");
const msgId = BigInt(parts[0]);
const emojiKey = parts.slice(1).join(":");
const exists = chat.messageReactions.some(r =>
const exists = chat.messageReactions.some(r =>
r.messageId === msgId && (r.emoji || `custom:${r.customEmojiId}`) === emojiKey
);
if (!exists) {
@@ -62,7 +62,7 @@
const maxShow = 3;
const displayed = usernames.slice(0, maxShow);
const remaining = usernames.length - maxShow;
let emojiName = "";
if (group[0].emoji) {
emojiName = EMOJIS.find(e => e.char === group[0].emoji)?.name || "emoji";
@@ -299,12 +299,12 @@ $effect(() => {
<i class="far fa-smile"></i>
</button>
{#if activePickerMessageId === msg.id && pickerPos}
<div
<div
use:portal
class="emoji-picker-popover"
class="emoji-picker-popover"
style="position: fixed; left: {pickerPos.x}px; top: {pickerPos.y}px; transform: {pickerPos.placement === 'top' ? 'translateY(-100%)' : ''}; z-index: 1000;"
>
<EmojiPicker
<EmojiPicker
onSelect={(emoji, customId) => {
chat.handleToggleReaction(msg.id, emoji, customId);
activePickerMessageId = null;
@@ -327,7 +327,7 @@ $effect(() => {
</div>
{#if tooltip.visible}
<div
<div
use:portal
class="message-reaction-tooltip"
style="left: {tooltip.x}px; top: {tooltip.y}px;"
+12 -12
View File
@@ -9,17 +9,17 @@
import { getContext } from "svelte";
import type { ChatService } from "../services/chat.svelte";
let {
activeThreadId,
setActiveThreadId,
let {
activeThreadId,
setActiveThreadId,
pendingThreadParentMessageId,
setPendingThreadParentMessageId,
activeChannelId,
activeServer,
isFullyAuthenticated,
users,
identity,
allThreads,
activeChannelId,
activeServer,
isFullyAuthenticated,
users,
identity,
allThreads,
allMessages,
allImages
}: {
@@ -80,8 +80,8 @@
{threadName}
</span>
</div>
<button
class="close-btn"
<button
class="close-btn"
onclick={handleClose}
aria-label="Close thread view"
>
@@ -106,7 +106,7 @@
{threadMessages}
{users}
{identity}
images={allImages}
images={allImages}
/>
<div class="typing-indicator" style="margin-left: 12px; margin-top: 4px;">
+4 -4
View File
@@ -43,7 +43,7 @@ export class ChatService {
for (const user of usersWithAvatars) {
const avatarId = user.avatarId!;
const idStr = avatarId.toString();
// Skip if already in memory
if (this.#avatarUrls.has(idStr)) continue;
@@ -55,7 +55,7 @@ export class ChatService {
async #loadAvatar(id: bigint) {
const idStr = id.toString();
// 1. Check persistent cache
const cachedUrl = await imageCache.get(id);
if (cachedUrl) {
@@ -409,13 +409,13 @@ export class ChatService {
if (!this.activeChannelId) return [];
const myId = this.identity;
const activeChannelId = this.activeChannelId;
return this.typingActivity
.filter((ta) => {
const isSameChannel = ta.channelId === activeChannelId;
const isNotMe = myId ? !ta.identity.isEqual(myId) : true;
const currentlyTyping = ta.isTyping;
return isSameChannel && isNotMe && currentlyTyping;
})
.map((ta) => {
+4 -4
View File
@@ -25,7 +25,7 @@ export class ImageCacheService {
async get(id: bigint): Promise<string | null> {
const idStr = id.toString();
// 1. Check in-memory cache
if (this.#inMemoryCache.has(idStr)) {
return this.#inMemoryCache.get(idStr)!;
@@ -33,7 +33,7 @@ export class ImageCacheService {
// 2. Check IndexedDB
if (!this.#db) await this.#initDB();
return new Promise((resolve) => {
const transaction = this.#db!.transaction("images", "readonly");
const store = transaction.objectStore("images");
@@ -63,7 +63,7 @@ export class ImageCacheService {
// Save to IndexedDB
if (!this.#db) await this.#initDB();
const transaction = this.#db!.transaction("images", "readwrite");
const store = transaction.objectStore("images");
store.put({ blob }, idStr);
@@ -75,7 +75,7 @@ export class ImageCacheService {
async getBlob(id: bigint): Promise<Blob | null> {
const idStr = id.toString();
if (!this.#db) await this.#initDB();
return new Promise((resolve) => {
const transaction = this.#db!.transaction("images", "readonly");
const store = transaction.objectStore("images");
+4 -4
View File
@@ -94,16 +94,16 @@ export class MessagingService {
// 4. Surgical Image & Reaction Sync
// We only sync images and reactions for messages that are currently in our server-scoped view
// This uses the index on message_id for both tables
const visibleMsgSubquery = threadId
const visibleMsgSubquery = threadId
? `(SELECT id FROM message WHERE thread_id = ${threadId} OR id = (SELECT parent_message_id FROM thread WHERE id = ${threadId}))`
: `(SELECT id FROM message WHERE channel_id = ${channelId} AND thread_id IS NULL ORDER BY sent DESC LIMIT ${limit})`;
queries.push(`SELECT * FROM message_image WHERE message_id IN ${visibleMsgSubquery}`);
queries.push(`SELECT * FROM message_reaction WHERE message_id IN ${visibleMsgSubquery}`);
// Image Sync: Message Images + User Avatars
const userSubquery = `(SELECT identity FROM user WHERE online = true OR identity IN (SELECT identity FROM server_member WHERE server_id = ${serverId}) OR identity IN (SELECT sender FROM message WHERE id IN ${visibleMsgSubquery}))`;
queries.push(`
SELECT * FROM image WHERE
id IN (SELECT image_id FROM message_image WHERE message_id IN ${visibleMsgSubquery}) OR
@@ -114,7 +114,7 @@ export class MessagingService {
// Only pull users who are: Online OR Members of this server OR Senders/Reactors/Typers of visible messages
const reactorSubquery = `(SELECT identity FROM message_reaction WHERE message_id IN ${visibleMsgSubquery})`;
const typerSubquery = `(SELECT identity FROM typing_activity WHERE channel_id IN (SELECT id FROM channel WHERE server_id = ${serverId}))`;
queries.push(`
SELECT * FROM user WHERE
online = true OR
+1 -1
View File
@@ -13,7 +13,7 @@ export class UIService {
showDiscoveryModal = $state(false);
authError = $state("");
viewingImageId = $state<bigint | null>(null);
// Track collapsed state of embeds by key: `${messageId}-${index}`
embedCollapsedStates = new SvelteMap<string, boolean>();
}