address warnings
This commit is contained in:
@@ -16,7 +16,8 @@
|
||||
} = $props();
|
||||
|
||||
// 1. Initialize the provider instance for this specific builder
|
||||
const db = createSpacetimeDBProvider(builder);
|
||||
// We untrack the builder here because SpacetimeProvider handles remounting on builder changes.
|
||||
const db = createSpacetimeDBProvider(untrack(() => builder));
|
||||
|
||||
// 1.1 Connection Timeout
|
||||
// If we stay in "connecting" state for too long without an identity or error,
|
||||
@@ -49,7 +50,7 @@
|
||||
// 2. Handshake Synchronization
|
||||
// Ensures session persistence happens when a valid identity and token are available
|
||||
let lastPersistedToken = $state<string | undefined>(undefined);
|
||||
let lastUsedOidcTokenForUpgrade = $state<string | undefined>(oidcToken);
|
||||
let lastUsedOidcTokenForUpgrade = $state<string | undefined>(untrack(() => oidcToken));
|
||||
|
||||
$effect(() => {
|
||||
const conn = $db.connection;
|
||||
|
||||
@@ -341,103 +341,6 @@
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.ios-switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 34px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.ios-switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: var(--background-accent);
|
||||
transition: .4s;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
left: 2px;
|
||||
bottom: 2px;
|
||||
background-color: var(--header-primary);
|
||||
transition: .4s;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
input:checked + .slider {
|
||||
background-color: var(--status-positive);
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
transform: translateX(14px);
|
||||
}
|
||||
|
||||
.hero-header {
|
||||
margin-bottom: 32px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logo-container {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
margin-bottom: 16px;
|
||||
filter: drop-shadow(0 0 10px var(--brand));
|
||||
}
|
||||
|
||||
.logo-svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: var(--brand);
|
||||
}
|
||||
|
||||
.zep-blimp {
|
||||
animation: float 4s infinite ease-in-out;
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
.blimp-top, .blimp-bottom {
|
||||
fill: none;
|
||||
stroke: currentColor;
|
||||
stroke-width: 6;
|
||||
stroke-linecap: round;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.blimp-filling {
|
||||
fill: none;
|
||||
stroke: url(#logoGrad);
|
||||
stroke-width: 8;
|
||||
stroke-linecap: round;
|
||||
filter: drop-shadow(0 0 8px var(--brand));
|
||||
animation: energy-pulse 2s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.tagline {
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-muted);
|
||||
margin-top: 4px;
|
||||
letter-spacing: 1px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% { transform: translateY(0) rotate(-2deg); }
|
||||
50% { transform: translateY(-10px) rotate(2deg); }
|
||||
|
||||
@@ -201,6 +201,8 @@
|
||||
<button
|
||||
class="icon-btn notification-toggle {chat.isChannelNotificationsEnabled(chat.activeChannelId) ? 'subscribed' : ''}"
|
||||
onclick={() => chat.toggleChannelNotifications(chat.activeChannelId!)}
|
||||
title={chat.isChannelNotificationsEnabled(chat.activeChannelId) ? "Mute Channel" : "Unmute Channel"}
|
||||
aria-label="Toggle notifications"
|
||||
>
|
||||
<i class="fas {chat.isChannelNotificationsEnabled(chat.activeChannelId) ? 'fa-bell' : 'fa-bell-slash'}"></i>
|
||||
</button>
|
||||
@@ -217,6 +219,8 @@
|
||||
<button
|
||||
class="icon-btn notification-toggle {chat.isChannelNotificationsEnabled(chat.activeChannelId!) ? 'subscribed' : ''}"
|
||||
onclick={() => chat.toggleChannelNotifications(chat.activeChannelId!)}
|
||||
title={chat.isChannelNotificationsEnabled(chat.activeChannelId!) ? "Mute DM" : "Unmute DM"}
|
||||
aria-label="Toggle notifications"
|
||||
>
|
||||
<i class="fas {chat.isChannelNotificationsEnabled(chat.activeChannelId!) ? 'fa-bell' : 'fa-bell-slash'}"></i>
|
||||
</button>
|
||||
|
||||
@@ -83,7 +83,9 @@
|
||||
style="color: {disabled ? 'var(--text-muted)' : 'var(--text-normal)'}; cursor: {disabled ? 'not-allowed' : 'text'};"
|
||||
/>
|
||||
{#if options.length > 0}
|
||||
<i class="fas fa-chevron-down chevron"
|
||||
<button
|
||||
type="button"
|
||||
class="chevron-btn"
|
||||
style="opacity: {disabled ? 0.3 : 1}; cursor: {disabled ? 'not-allowed' : 'pointer'};"
|
||||
onclick={(e) => {
|
||||
if (disabled) return;
|
||||
@@ -96,7 +98,10 @@
|
||||
handleFocus();
|
||||
}
|
||||
}}
|
||||
></i>
|
||||
aria-label="Toggle dropdown"
|
||||
>
|
||||
<i class="fas fa-chevron-down chevron"></i>
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
{#if isOpen && filteredOptions.length > 0}
|
||||
@@ -146,9 +151,18 @@
|
||||
box-shadow: 0 0 0 2px var(--brand);
|
||||
}
|
||||
|
||||
.chevron {
|
||||
.chevron-btn {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.chevron {
|
||||
font-size: 0.8rem;
|
||||
color: var(--interactive-normal);
|
||||
cursor: pointer;
|
||||
|
||||
@@ -85,14 +85,14 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="emoji-picker" onclick={(e) => e.stopPropagation()}>
|
||||
<div class="emoji-picker" onclick={(e) => e.stopPropagation()} role="dialog" aria-label="Emoji Picker" tabindex="-1">
|
||||
<div class="picker-header">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search emojis..."
|
||||
bind:value={searchTerm}
|
||||
class="search-input"
|
||||
autofocus
|
||||
aria-label="Search emojis"
|
||||
/>
|
||||
</div>
|
||||
<div class="picker-content">
|
||||
|
||||
@@ -12,11 +12,17 @@
|
||||
const bannerUrl = $derived(user.bannerId ? chat.getBannerUrl(user) : null);
|
||||
const isMe = $derived(chat.identity?.isEqual(user.identity));
|
||||
let isEditingStatus = $state(false);
|
||||
let statusText = $state(user.status || "");
|
||||
|
||||
let _localStatus = $state<string | null>(null);
|
||||
let statusText = $derived.by({
|
||||
get: () => _localStatus ?? user.status ?? "",
|
||||
set: (v: string) => { _localStatus = v; }
|
||||
});
|
||||
|
||||
async function handleStatusSubmit(e: Event) {
|
||||
e.preventDefault();
|
||||
await chat.handleSetStatus(statusText.trim() || undefined);
|
||||
_localStatus = null; // Reset to sync with backend next time
|
||||
isEditingStatus = false;
|
||||
}
|
||||
</script>
|
||||
@@ -52,10 +58,9 @@
|
||||
bind:value={statusText}
|
||||
placeholder="Set a status..."
|
||||
maxlength="128"
|
||||
autofocus
|
||||
onblur={() => { if (!statusText) isEditingStatus = false; }}
|
||||
/>
|
||||
<button type="submit" style="display: none;"></button>
|
||||
<button type="submit" style="display: none;" aria-label="Save status"></button>
|
||||
</form>
|
||||
{:else}
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
<button
|
||||
class="server-icon {chat.activeServerId === null ? 'active' : ''}"
|
||||
onclick={() => (chat.activeServerId = null)}
|
||||
aria-label="Direct Messages"
|
||||
title="Direct Messages"
|
||||
>
|
||||
<i class="fas fa-comment-dots"></i>
|
||||
</button>
|
||||
@@ -45,6 +47,8 @@
|
||||
class="server-icon {chat.activeServerId === server.id ? 'active' : ''}"
|
||||
onclick={() => (chat.activeServerId = server.id)}
|
||||
style={avatarUrl ? `background-image: url(${avatarUrl}); background-size: cover; background-position: center; border: none;` : ""}
|
||||
aria-label={server.name}
|
||||
title={server.name}
|
||||
>
|
||||
{#if !avatarUrl}
|
||||
{server.name.substring(0, 2).toUpperCase()}
|
||||
@@ -59,6 +63,8 @@
|
||||
class="server-icon"
|
||||
onclick={() => (chat.showCreateServerModal = true)}
|
||||
style="cursor: {chat.isFullyAuthenticated ? 'pointer' : 'not-allowed'};"
|
||||
aria-label="Create Server"
|
||||
title="Create Server"
|
||||
>
|
||||
<i class="fas fa-plus"></i>
|
||||
</button>
|
||||
@@ -69,6 +75,8 @@
|
||||
class="server-icon"
|
||||
onclick={() => (chat.showDiscoveryModal = true)}
|
||||
style="color: var(--status-positive);"
|
||||
aria-label="Discover Servers"
|
||||
title="Discover Servers"
|
||||
>
|
||||
<i class="fas fa-search"></i>
|
||||
</button>
|
||||
@@ -86,6 +94,8 @@
|
||||
class="server-icon"
|
||||
onclick={onShowServerSettings}
|
||||
style="color: var(--brand); margin-bottom: 12px;"
|
||||
aria-label="Connection Settings"
|
||||
title="Connection Settings"
|
||||
>
|
||||
<i class="fas fa-network-wired"></i>
|
||||
</button>
|
||||
|
||||
@@ -543,30 +543,6 @@
|
||||
z-index: 6000; /* Above settings */
|
||||
}
|
||||
|
||||
.danger-modal {
|
||||
background-color: var(--background-primary);
|
||||
max-width: 440px;
|
||||
width: 90%;
|
||||
padding: 24px;
|
||||
border-radius: 8px;
|
||||
box-shadow: var(--elevation-high);
|
||||
border: 1px solid var(--background-modifier-accent);
|
||||
animation: modal-pop 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
|
||||
.danger-modal h2 {
|
||||
color: var(--status-danger);
|
||||
margin: 0 0 16px 0;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.danger-modal p {
|
||||
color: var(--text-normal);
|
||||
line-height: 1.5;
|
||||
margin-bottom: 24px;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
className?: string;
|
||||
}>();
|
||||
|
||||
const style = circle
|
||||
const style = $derived(circle
|
||||
? `width: ${width}; height: ${width}; border-radius: 50%;`
|
||||
: `width: ${width}; height: ${height}; border-radius: ${borderRadius};`;
|
||||
: `width: ${width}; height: ${height}; border-radius: ${borderRadius};`);
|
||||
</script>
|
||||
|
||||
<div
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { getContext, onMount } from "svelte";
|
||||
import { getContext, onMount, untrack } from "svelte";
|
||||
import type { ChatService } from "../services/chat.svelte";
|
||||
import type { WebRTCService } from "../services/webrtc/webrtc.svelte";
|
||||
import type * as Types from "../../module_bindings/types";
|
||||
@@ -17,11 +17,16 @@
|
||||
const webrtc = getContext<WebRTCService>("webrtc");
|
||||
|
||||
let menuRef = $state<HTMLDivElement | null>(null);
|
||||
let adjustedX = $state(x);
|
||||
let adjustedY = $state(y);
|
||||
let adjustedX = $state(untrack(() => x));
|
||||
let adjustedY = $state(untrack(() => y));
|
||||
|
||||
$effect(() => {
|
||||
adjustedX = x;
|
||||
adjustedY = y;
|
||||
});
|
||||
|
||||
// Voice specific states
|
||||
const peerIdHex = user.identity.toHexString();
|
||||
const peerIdHex = $derived(user.identity.toHexString());
|
||||
const voiceState = $derived(chat.userStates.find(s => s.identity.isEqual(user.identity)));
|
||||
const isMe = $derived(chat.identity?.isEqual(user.identity));
|
||||
const isLocalUserInVoice = $derived(!!chat.connectedVoiceChannel);
|
||||
|
||||
@@ -252,10 +252,6 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.watch-btn.active {
|
||||
background-color: #f23f43;
|
||||
}
|
||||
|
||||
.fullscreen-btn {
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
color: white;
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
chat.isVoiceChannel = false;
|
||||
chat.showCreateChannelModal = true;
|
||||
}}
|
||||
title="Create Text Channel"
|
||||
aria-label="Create Text Channel"
|
||||
>
|
||||
<i class="fas fa-plus"></i>
|
||||
</button>
|
||||
|
||||
@@ -64,6 +64,8 @@
|
||||
chat.isVoiceChannel = true;
|
||||
chat.showCreateChannelModal = true;
|
||||
}}
|
||||
title="Create Voice Channel"
|
||||
aria-label="Create Voice Channel"
|
||||
>
|
||||
<i class="fas fa-plus"></i>
|
||||
</button>
|
||||
|
||||
@@ -301,19 +301,4 @@
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.btn-clear {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
font-size: 1.1rem;
|
||||
opacity: 0.6;
|
||||
transition: opacity 0.1s;
|
||||
}
|
||||
|
||||
.btn-clear:hover {
|
||||
opacity: 1;
|
||||
color: var(--text-normal);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -89,21 +89,6 @@
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.form-group select {
|
||||
padding: 10px;
|
||||
background-color: var(--background-tertiary);
|
||||
color: var(--text-normal);
|
||||
border: 1px solid var(--background-modifier-accent);
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form-group select:hover {
|
||||
background-color: var(--background-modifier-hover);
|
||||
}
|
||||
|
||||
.voice-sensitivity-box {
|
||||
background-color: var(--background-secondary);
|
||||
padding: 16px;
|
||||
|
||||
@@ -156,7 +156,7 @@
|
||||
</Input>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Emoji Image</label>
|
||||
<div class="label-heading">Emoji Image</div>
|
||||
<label class="custom-file-upload">
|
||||
<i class="fas fa-image"></i>
|
||||
<span class="file-name-text">{newEmojiFile ? newEmojiFile.name : "Select Image"}</span>
|
||||
@@ -364,7 +364,7 @@
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
.form-group label, .label-heading {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-muted);
|
||||
@@ -464,26 +464,4 @@
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background-color: var(--status-positive);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 20px;
|
||||
border-radius: 4px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.1s;
|
||||
}
|
||||
|
||||
.btn-success:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.btn-success:disabled {
|
||||
background-color: var(--background-modifier-accent);
|
||||
color: var(--text-muted);
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -86,17 +86,6 @@
|
||||
color: var(--text-muted);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.form-group select {
|
||||
padding: 10px;
|
||||
background-color: var(--background-tertiary);
|
||||
color: var(--text-normal);
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.help-text {
|
||||
font-size: 0.75rem;
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Your Public Key</label>
|
||||
<div class="label-heading">Your Public Key</div>
|
||||
<div class="key-display">
|
||||
<pre>{chat.myPublicKey}</pre>
|
||||
<Button variant="secondary" size="small" class="copy-btn" onclick={copyPublicKey}>
|
||||
@@ -179,7 +179,7 @@
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
.form-group label, .label-heading {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
color: var(--header-secondary);
|
||||
|
||||
@@ -276,7 +276,7 @@ export class ChannelAudioWebRTCService {
|
||||
// Map local track to the transceiver created by the offer
|
||||
const transceivers = pc.getTransceivers();
|
||||
const audioTrack = this.localStream?.getAudioTracks()[0];
|
||||
const audioTransceiver = transceivers.find(t => t.receiver.track.kind === 'audio');
|
||||
const audioTransceiver = transceivers.find((t: RTCRtpTransceiver) => t.receiver.track.kind === 'audio');
|
||||
|
||||
if (audioTransceiver && audioTrack) {
|
||||
await audioTransceiver.sender.replaceTrack(audioTrack);
|
||||
|
||||
@@ -288,8 +288,8 @@ export class ScreenSharingWebRTCService {
|
||||
const videoTrack = this.localScreenStream?.getVideoTracks()[0];
|
||||
const audioTrack = this.localScreenStream?.getAudioTracks()[0];
|
||||
|
||||
const videoTransceiver = transceivers.find(t => t.receiver.track.kind === 'video');
|
||||
const audioTransceiver = transceivers.find(t => t.receiver.track.kind === 'audio');
|
||||
const videoTransceiver = transceivers.find((t: RTCRtpTransceiver) => t.receiver.track.kind === 'video');
|
||||
const audioTransceiver = transceivers.find((t: RTCRtpTransceiver) => t.receiver.track.kind === 'audio');
|
||||
|
||||
if (videoTransceiver && videoTrack) {
|
||||
await videoTransceiver.sender.replaceTrack(videoTrack);
|
||||
|
||||
Reference in New Issue
Block a user