improve index usage
This commit is contained in:
@@ -84,9 +84,17 @@ const voice_activity = table(
|
||||
{
|
||||
name: "voice_activity",
|
||||
public: true,
|
||||
indexes: [
|
||||
{
|
||||
accessor: "by_channel_id",
|
||||
algorithm: "btree",
|
||||
columns: ["channel_id"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
identity: t.identity().primaryKey(),
|
||||
channel_id: t.u64(),
|
||||
is_talking: t.bool(),
|
||||
},
|
||||
);
|
||||
@@ -98,6 +106,11 @@ const watching = table(
|
||||
indexes: [
|
||||
{ accessor: "by_watcher", algorithm: "btree", columns: ["watcher"] },
|
||||
{ accessor: "by_watchee", algorithm: "btree", columns: ["watchee"] },
|
||||
{
|
||||
accessor: "by_channel_id",
|
||||
algorithm: "btree",
|
||||
columns: ["channel_id"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -116,6 +129,11 @@ const voice_sdp_offer = table(
|
||||
indexes: [
|
||||
{ accessor: "by_receiver", algorithm: "btree", columns: ["receiver"] },
|
||||
{ accessor: "by_sender", algorithm: "btree", columns: ["sender"] },
|
||||
{
|
||||
accessor: "by_channel_id",
|
||||
algorithm: "btree",
|
||||
columns: ["channel_id"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -134,6 +152,11 @@ const voice_sdp_answer = table(
|
||||
indexes: [
|
||||
{ accessor: "by_receiver", algorithm: "btree", columns: ["receiver"] },
|
||||
{ accessor: "by_sender", algorithm: "btree", columns: ["sender"] },
|
||||
{
|
||||
accessor: "by_channel_id",
|
||||
algorithm: "btree",
|
||||
columns: ["channel_id"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -152,6 +175,11 @@ const voice_ice_candidate = table(
|
||||
indexes: [
|
||||
{ accessor: "by_receiver", algorithm: "btree", columns: ["receiver"] },
|
||||
{ accessor: "by_sender", algorithm: "btree", columns: ["sender"] },
|
||||
{
|
||||
accessor: "by_channel_id",
|
||||
algorithm: "btree",
|
||||
columns: ["channel_id"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -171,6 +199,11 @@ const screen_sdp_offer = table(
|
||||
indexes: [
|
||||
{ accessor: "by_receiver", algorithm: "btree", columns: ["receiver"] },
|
||||
{ accessor: "by_sender", algorithm: "btree", columns: ["sender"] },
|
||||
{
|
||||
accessor: "by_channel_id",
|
||||
algorithm: "btree",
|
||||
columns: ["channel_id"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -189,6 +222,11 @@ const screen_sdp_answer = table(
|
||||
indexes: [
|
||||
{ accessor: "by_receiver", algorithm: "btree", columns: ["receiver"] },
|
||||
{ accessor: "by_sender", algorithm: "btree", columns: ["sender"] },
|
||||
{
|
||||
accessor: "by_channel_id",
|
||||
algorithm: "btree",
|
||||
columns: ["channel_id"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -207,6 +245,11 @@ const screen_ice_candidate = table(
|
||||
indexes: [
|
||||
{ accessor: "by_receiver", algorithm: "btree", columns: ["receiver"] },
|
||||
{ accessor: "by_sender", algorithm: "btree", columns: ["sender"] },
|
||||
{
|
||||
accessor: "by_channel_id",
|
||||
algorithm: "btree",
|
||||
columns: ["channel_id"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -296,17 +339,19 @@ export const set_name = spacetimedb.reducer(
|
||||
);
|
||||
|
||||
export const set_talking = spacetimedb.reducer(
|
||||
{ talking: t.bool() },
|
||||
(ctx, { talking }) => {
|
||||
{ talking: t.bool(), channelId: t.u64() },
|
||||
(ctx, { talking, channelId }) => {
|
||||
const activity = ctx.db.voice_activity.identity.find(ctx.sender);
|
||||
if (activity) {
|
||||
ctx.db.voice_activity.identity.update({
|
||||
identity: ctx.sender,
|
||||
channel_id: channelId,
|
||||
is_talking: talking,
|
||||
});
|
||||
} else {
|
||||
ctx.db.voice_activity.insert({
|
||||
identity: ctx.sender,
|
||||
channel_id: channelId,
|
||||
is_talking: talking,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@ const ChatContainer: React.FC = () => {
|
||||
watching={watching}
|
||||
peerStats={peerStats}
|
||||
setPeerAudioPreference={setPeerAudioPreference}
|
||||
voiceActivity={chat.voiceActivity}
|
||||
/>
|
||||
{/* Voice Connected Status Bar */}
|
||||
{chat.connectedVoiceChannel && (
|
||||
@@ -201,6 +202,9 @@ const ChatContainer: React.FC = () => {
|
||||
startWatching={startWatching}
|
||||
stopWatching={stopWatching}
|
||||
watching={watching}
|
||||
users={chat.users}
|
||||
voiceStates={chat.voiceStates}
|
||||
voiceActivity={chat.voiceActivity}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
@@ -212,6 +216,7 @@ const ChatContainer: React.FC = () => {
|
||||
identity={identity ?? null}
|
||||
handleStartThread={chat.handleStartThread}
|
||||
isFullyAuthenticated={chat.isFullyAuthenticated}
|
||||
allThreads={chat.allThreads}
|
||||
/>
|
||||
<MessageInput
|
||||
activeChannelId={chat.activeChannelId}
|
||||
@@ -232,6 +237,8 @@ const ChatContainer: React.FC = () => {
|
||||
isFullyAuthenticated={chat.isFullyAuthenticated}
|
||||
users={chat.users}
|
||||
identity={identity ?? null}
|
||||
allThreads={chat.allThreads}
|
||||
allMessages={chat.allMessages}
|
||||
/>
|
||||
) : (
|
||||
showMemberList && (
|
||||
@@ -243,6 +250,7 @@ const ChatContainer: React.FC = () => {
|
||||
voiceStates={chat.voiceStates}
|
||||
currentVoiceState={chat.currentVoiceState}
|
||||
connectedVoiceChannel={chat.connectedVoiceChannel}
|
||||
voiceActivity={chat.voiceActivity}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
@@ -256,7 +264,12 @@ const ChatContainer: React.FC = () => {
|
||||
/>
|
||||
)}
|
||||
|
||||
{showSettings && <SettingsPanel onClose={() => setShowSettings(false)} />}
|
||||
{showSettings && (
|
||||
<SettingsPanel
|
||||
currentUser={chat.currentUser}
|
||||
onClose={() => setShowSettings(false)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from "react";
|
||||
import { Identity } from "spacetimedb";
|
||||
import { tables } from "../../module_bindings";
|
||||
import { useTable, useSpacetimeDB } from "spacetimedb/react";
|
||||
import { useSpacetimeDB } from "spacetimedb/react";
|
||||
import * as Types from "../../module_bindings/types";
|
||||
import { WebRTCStats } from "../services";
|
||||
|
||||
@@ -18,6 +17,7 @@ interface ChannelListProps {
|
||||
currentVoiceState: Types.VoiceState | undefined;
|
||||
connectedVoiceChannel: Types.Channel | undefined;
|
||||
isFullyAuthenticated: boolean;
|
||||
voiceActivity: readonly Types.VoiceActivity[];
|
||||
|
||||
// Modal state and handlers
|
||||
showCreateChannelModal: boolean;
|
||||
@@ -211,6 +211,7 @@ export const ChannelList: React.FC<ChannelListProps> = ({
|
||||
currentVoiceState,
|
||||
connectedVoiceChannel,
|
||||
isFullyAuthenticated,
|
||||
voiceActivity,
|
||||
showCreateChannelModal,
|
||||
setShowCreateChannelModal,
|
||||
newChannelName,
|
||||
@@ -226,9 +227,6 @@ export const ChannelList: React.FC<ChannelListProps> = ({
|
||||
peerStats,
|
||||
setPeerAudioPreference,
|
||||
}) => {
|
||||
const [voiceActivity] = useTable(
|
||||
React.useMemo(() => tables.voice_activity, []),
|
||||
);
|
||||
const [hoveredPeer, setHoveredPeer] = React.useState<string | null>(null);
|
||||
const [showServerDropdown, setShowServerDropdown] = React.useState(false);
|
||||
const [contextMenu, setContextMenu] = React.useState<{
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
import React, { useMemo } from "react";
|
||||
import { Identity } from "spacetimedb";
|
||||
import type * as Types from "../../module_bindings/types";
|
||||
import { useTable } from "spacetimedb/react";
|
||||
import { tables } from "../../module_bindings";
|
||||
|
||||
// Helper function (extracted from App.tsx)
|
||||
const getUsername = (
|
||||
@@ -23,6 +21,7 @@ interface MemberListProps {
|
||||
voiceStates: readonly Types.VoiceState[];
|
||||
currentVoiceState: Types.VoiceState | undefined;
|
||||
connectedVoiceChannel: Types.Channel | undefined;
|
||||
voiceActivity: readonly Types.VoiceActivity[];
|
||||
}
|
||||
|
||||
function MemberList({
|
||||
@@ -33,8 +32,8 @@ function MemberList({
|
||||
voiceStates,
|
||||
currentVoiceState,
|
||||
connectedVoiceChannel,
|
||||
voiceActivity,
|
||||
}: MemberListProps) {
|
||||
const [voiceActivity] = useTable(useMemo(() => tables.voice_activity, []));
|
||||
// Categorize members into Online and Offline
|
||||
const onlineMembers = useMemo(
|
||||
() => activeServerMembers.filter((m) => m.online),
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
import React, { useRef, useEffect, useMemo } from "react";
|
||||
import { Identity } from "spacetimedb";
|
||||
import type * as Types from "../../module_bindings/types";
|
||||
import { useTable } from "spacetimedb/react";
|
||||
import { tables } from "../../module_bindings";
|
||||
|
||||
import RichText from "./RichText";
|
||||
|
||||
@@ -30,6 +28,7 @@ interface MessageListProps {
|
||||
identity: Identity | null;
|
||||
handleStartThread: (msg: Types.Message) => void;
|
||||
isFullyAuthenticated: boolean;
|
||||
allThreads: readonly Types.Thread[];
|
||||
}
|
||||
|
||||
function MessageList({
|
||||
@@ -40,6 +39,7 @@ function MessageList({
|
||||
identity,
|
||||
handleStartThread,
|
||||
isFullyAuthenticated,
|
||||
allThreads,
|
||||
}: MessageListProps) {
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@@ -48,9 +48,6 @@ function MessageList({
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
||||
}, [messages]);
|
||||
|
||||
// Fetch threads to display thread links
|
||||
const [allThreads] = useTable(useMemo(() => tables.thread, []));
|
||||
|
||||
return (
|
||||
<div className="message-list">
|
||||
{messages.map((msg) => {
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
import React, { useState, useMemo } from "react";
|
||||
import { Identity } from "spacetimedb";
|
||||
import type * as Types from "../../module_bindings/types";
|
||||
import { useTable, useReducer } from "spacetimedb/react"; // Assuming useTable and useReducer are available
|
||||
import { tables, reducers } from "../../module_bindings";
|
||||
|
||||
// Helper function (extracted from App.tsx)
|
||||
const getUsername = (userIdentity: Identity | null, users: Types.User[]) => {
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import React, { useState } from "react";
|
||||
import { useReducer, useSpacetimeDB, useTable } from "spacetimedb/react";
|
||||
import { reducers, tables } from "../../module_bindings";
|
||||
import { useReducer, useSpacetimeDB } from "spacetimedb/react";
|
||||
import { reducers } from "../../module_bindings";
|
||||
import * as Types from "../../module_bindings/types";
|
||||
|
||||
interface SettingsPanelProps {
|
||||
onClose: () => void;
|
||||
currentUser: Types.User | undefined;
|
||||
}
|
||||
|
||||
export const SettingsPanel: React.FC<SettingsPanelProps> = ({ onClose }) => {
|
||||
export const SettingsPanel: React.FC<SettingsPanelProps> = ({
|
||||
onClose,
|
||||
currentUser,
|
||||
}) => {
|
||||
const { identity } = useSpacetimeDB();
|
||||
const [users] = useTable(tables.user);
|
||||
const currentUser = users.find((u) => u.identity.isEqual(identity!));
|
||||
|
||||
const [name, setNameInput] = useState(currentUser?.name || "");
|
||||
const setNameReducer = useReducer(reducers.setName);
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
import React, { useState, useMemo } from "react";
|
||||
import { Identity } from "spacetimedb";
|
||||
import type * as Types from "../../module_bindings/types";
|
||||
import { useTable, useReducer } from "spacetimedb/react"; // Assuming useTable and useReducer are available
|
||||
import { tables, reducers } from "../../module_bindings";
|
||||
import { useReducer } from "spacetimedb/react"; // Assuming useTable and useReducer are available
|
||||
import { reducers } from "../../module_bindings";
|
||||
import ThreadMessageList from "./ThreadMessageList";
|
||||
import ThreadMessageInput from "./ThreadMessageInput";
|
||||
|
||||
@@ -15,6 +15,8 @@ interface ThreadViewProps {
|
||||
isFullyAuthenticated: boolean;
|
||||
users: readonly Types.User[];
|
||||
identity: Identity | null;
|
||||
allThreads: readonly Types.Thread[];
|
||||
allMessages: readonly Types.Message[];
|
||||
}
|
||||
|
||||
function ThreadView({
|
||||
@@ -25,15 +27,13 @@ function ThreadView({
|
||||
isFullyAuthenticated,
|
||||
users,
|
||||
identity,
|
||||
allThreads,
|
||||
allMessages,
|
||||
}: ThreadViewProps) {
|
||||
const sendMessageReducer = useReducer(
|
||||
useMemo(() => reducers.sendMessage, []),
|
||||
); // Assuming reducers are accessible
|
||||
|
||||
// Fetch all threads and messages
|
||||
const [allThreads] = useTable(useMemo(() => tables.thread, []));
|
||||
const [allMessages] = useTable(useMemo(() => tables.message, []));
|
||||
|
||||
const activeThread = useMemo(
|
||||
() => allThreads.find((t) => t.id === activeThreadId),
|
||||
[allThreads, activeThreadId],
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import { Identity } from "spacetimedb";
|
||||
import { useTable, useSpacetimeDB } from "spacetimedb/react";
|
||||
import { tables } from "../../module_bindings";
|
||||
import { useSpacetimeDB } from "spacetimedb/react";
|
||||
import * as Types from "../../module_bindings/types";
|
||||
|
||||
interface VideoGridProps {
|
||||
@@ -11,6 +10,9 @@ interface VideoGridProps {
|
||||
startWatching: (peerIdentity: Identity) => void;
|
||||
stopWatching: (peerIdentity: Identity) => void;
|
||||
watching: readonly Types.Watching[];
|
||||
users: readonly Types.User[];
|
||||
voiceStates: readonly Types.VoiceState[];
|
||||
voiceActivity: readonly Types.VoiceActivity[];
|
||||
}
|
||||
|
||||
const VideoTile = ({
|
||||
@@ -22,6 +24,7 @@ const VideoTile = ({
|
||||
isWatching,
|
||||
isSharing,
|
||||
isHero,
|
||||
users,
|
||||
}: {
|
||||
identity: Identity;
|
||||
stream?: MediaStream;
|
||||
@@ -31,10 +34,10 @@ const VideoTile = ({
|
||||
isWatching?: boolean;
|
||||
isSharing?: boolean;
|
||||
isHero?: boolean;
|
||||
users: readonly Types.User[];
|
||||
}) => {
|
||||
const videoRef = useRef<HTMLVideoElement>(null);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [users] = useTable(tables.user);
|
||||
const [isMuted, setIsMuted] = React.useState(false);
|
||||
const [volume, setVolume] = React.useState(1.0);
|
||||
const user = users.find((u) => u.identity.isEqual(identity));
|
||||
@@ -184,13 +187,11 @@ export const VideoGrid: React.FC<VideoGridProps> = ({
|
||||
startWatching,
|
||||
stopWatching,
|
||||
watching,
|
||||
users,
|
||||
voiceStates,
|
||||
voiceActivity,
|
||||
}) => {
|
||||
const { identity: localIdentity } = useSpacetimeDB();
|
||||
const [voiceStates] = useTable(tables.voice_state);
|
||||
const [voiceActivity] = useTable(
|
||||
React.useMemo(() => tables.voice_activity, []),
|
||||
);
|
||||
const [users] = useTable(tables.user);
|
||||
const [focusedIdentity, setFocusedIdentity] = React.useState<Identity | null>(
|
||||
null,
|
||||
);
|
||||
@@ -259,6 +260,7 @@ export const VideoGrid: React.FC<VideoGridProps> = ({
|
||||
isSharing={isLocal ? localSharing : vs.isSharingScreen}
|
||||
onToggleWatch={() => toggleWatch(vs.identity)}
|
||||
isHero={roleClass === "is-hero"}
|
||||
users={users}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -8,39 +8,44 @@ export const useSubscriptions = (
|
||||
identity: Identity | null,
|
||||
activeServerId: bigint | null,
|
||||
activeChannelId: bigint | null,
|
||||
connectedVoiceChannelId: bigint | null = null,
|
||||
) => {
|
||||
const [servers] = useTable(useMemo(() => tables.server, []));
|
||||
|
||||
const [serverMembers] = useTable(
|
||||
const [activeServerMembers] = useTable(
|
||||
useMemo(
|
||||
() =>
|
||||
activeServerId
|
||||
? tables.server_member.where((r) => r.serverId.eq(activeServerId))
|
||||
: tables.server_member,
|
||||
: tables.server_member.where((r) => r.id.eq(-1n)),
|
||||
[activeServerId],
|
||||
),
|
||||
);
|
||||
|
||||
// User's own memberships
|
||||
useTable(
|
||||
const [ownMemberships] = useTable(
|
||||
useMemo(
|
||||
() =>
|
||||
identity
|
||||
? tables.server_member.where((r) => r.identity.eq(identity))
|
||||
: tables.server_member,
|
||||
: tables.server_member.where((r) => r.id.eq(-1n)),
|
||||
[identity],
|
||||
),
|
||||
);
|
||||
|
||||
const [channels] = useTable(
|
||||
useMemo(
|
||||
() =>
|
||||
activeServerId
|
||||
? tables.channel.where((r) => r.serverId.eq(activeServerId))
|
||||
: tables.channel,
|
||||
[activeServerId],
|
||||
),
|
||||
);
|
||||
const serverMembers = useMemo(() => {
|
||||
const combined = [...(activeServerMembers || []), ...(ownMemberships || [])];
|
||||
// De-duplicate
|
||||
const seen = new Set();
|
||||
return combined.filter((m) => {
|
||||
const id = m.id.toString();
|
||||
if (seen.has(id)) return false;
|
||||
seen.add(id);
|
||||
return true;
|
||||
});
|
||||
}, [activeServerMembers, ownMemberships]);
|
||||
|
||||
const [channels] = useTable(useMemo(() => tables.channel, []));
|
||||
|
||||
const [users, isUsersReady] = useTable(useMemo(() => tables.user, []));
|
||||
|
||||
@@ -49,7 +54,7 @@ export const useSubscriptions = (
|
||||
() =>
|
||||
activeChannelId
|
||||
? tables.message.where((r) => r.channelId.eq(activeChannelId))
|
||||
: tables.message,
|
||||
: tables.message.where((r) => r.id.eq(-1n)),
|
||||
[activeChannelId],
|
||||
),
|
||||
);
|
||||
@@ -59,21 +64,33 @@ export const useSubscriptions = (
|
||||
() =>
|
||||
activeChannelId
|
||||
? tables.thread.where((r) => r.channelId.eq(activeChannelId))
|
||||
: tables.thread,
|
||||
: tables.thread.where((r) => r.id.eq(-1n)),
|
||||
[activeChannelId],
|
||||
),
|
||||
);
|
||||
|
||||
const [voiceStates] = useTable(useMemo(() => tables.voice_state, []));
|
||||
const [voiceActivity] = useTable(
|
||||
useMemo(
|
||||
() =>
|
||||
connectedVoiceChannelId
|
||||
? tables.voice_activity.where((va) =>
|
||||
va.channelId.eq(connectedVoiceChannelId),
|
||||
)
|
||||
: tables.voice_activity.where((va) => va.identity.eq(Identity.zero())),
|
||||
[connectedVoiceChannelId],
|
||||
),
|
||||
);
|
||||
|
||||
return {
|
||||
servers: servers as readonly Types.Server[],
|
||||
serverMembers: serverMembers as readonly Types.ServerMember[],
|
||||
channels: channels as readonly Types.Channel[],
|
||||
channels: (channels || []) as readonly Types.Channel[],
|
||||
users: users as readonly Types.User[],
|
||||
isUsersReady,
|
||||
allMessages: allMessages as readonly Types.Message[],
|
||||
allThreads: allThreads as readonly Types.Thread[],
|
||||
allMessages: (allMessages || []) as readonly Types.Message[],
|
||||
allThreads: (allThreads || []) as readonly Types.Thread[],
|
||||
voiceStates: voiceStates as readonly Types.VoiceState[],
|
||||
voiceActivity: voiceActivity as readonly Types.VoiceActivity[],
|
||||
};
|
||||
};
|
||||
|
||||
@@ -39,6 +39,7 @@ export interface ChatState {
|
||||
allMessages: readonly Types.Message[];
|
||||
allThreads: readonly Types.Thread[];
|
||||
voiceStates: readonly Types.VoiceState[];
|
||||
voiceActivity: readonly Types.VoiceActivity[];
|
||||
currentVoiceState: Types.VoiceState | undefined;
|
||||
connectedVoiceChannel: Types.Channel | undefined;
|
||||
onlineUsers: readonly Types.User[];
|
||||
@@ -119,21 +120,20 @@ export function useChat(): ChatState {
|
||||
|
||||
// 2. Setup navigation and subscriptions (order matters)
|
||||
// We need initial subscriptions to get server list, etc. even before we have an active server.
|
||||
const {
|
||||
servers,
|
||||
serverMembers,
|
||||
channels,
|
||||
users,
|
||||
allMessages,
|
||||
allThreads,
|
||||
voiceStates,
|
||||
} = useSubscriptions(identity || null, null, null);
|
||||
const bootstrapSubs = useSubscriptions(identity || null, null, null);
|
||||
|
||||
const navigation = useNavigation(
|
||||
identity || null,
|
||||
servers,
|
||||
channels,
|
||||
serverMembers,
|
||||
bootstrapSubs.servers,
|
||||
bootstrapSubs.channels,
|
||||
bootstrapSubs.serverMembers,
|
||||
);
|
||||
|
||||
const voiceStatus = useVoiceStatus(
|
||||
identity || null,
|
||||
bootstrapSubs.voiceStates,
|
||||
bootstrapSubs.channels,
|
||||
bootstrapSubs.users,
|
||||
);
|
||||
|
||||
// Now we can have specialized subscriptions based on navigation
|
||||
@@ -143,6 +143,7 @@ export function useChat(): ChatState {
|
||||
identity || null,
|
||||
navigation.activeServerId,
|
||||
navigation.activeChannelId,
|
||||
voiceStatus.connectedVoiceChannel?.id || null,
|
||||
);
|
||||
|
||||
const actions = useActions(
|
||||
@@ -151,34 +152,30 @@ export function useChat(): ChatState {
|
||||
navigation.activeThreadId,
|
||||
);
|
||||
|
||||
const voiceStatus = useVoiceStatus(
|
||||
identity || null,
|
||||
voiceStates,
|
||||
channels,
|
||||
users,
|
||||
);
|
||||
|
||||
const discovery = useDiscovery();
|
||||
|
||||
// Derived Data (Moved back to main orchestrator for now as they use multiple hook results)
|
||||
const isFullyAuthenticated = useMemo(() => {
|
||||
const user = users.find((u) =>
|
||||
const user = activeSubs.users.find((u) =>
|
||||
u.identity?.isEqual(identity || Identity.zero()),
|
||||
);
|
||||
if (!user) return false;
|
||||
const hasOidc = !!(user.issuer && user.subject);
|
||||
const hasCreds = !!(user.username && user.password);
|
||||
return hasOidc || hasCreds;
|
||||
}, [users, identity]);
|
||||
}, [activeSubs.users, identity]);
|
||||
|
||||
const currentUser = useMemo(
|
||||
() => users.find((u) => u.identity?.isEqual(identity || Identity.zero())),
|
||||
[users, identity],
|
||||
() =>
|
||||
activeSubs.users.find((u) =>
|
||||
u.identity?.isEqual(identity || Identity.zero()),
|
||||
),
|
||||
[activeSubs.users, identity],
|
||||
);
|
||||
|
||||
const channelMessages = useMemo(
|
||||
() =>
|
||||
allMessages
|
||||
activeSubs.allMessages
|
||||
.filter(
|
||||
(m) =>
|
||||
m.channelId === navigation.activeChannelId &&
|
||||
@@ -187,22 +184,22 @@ export function useChat(): ChatState {
|
||||
.sort((a, b) =>
|
||||
a.sent.microsSinceUnixEpoch < b.sent.microsSinceUnixEpoch ? -1 : 1,
|
||||
),
|
||||
[allMessages, navigation.activeChannelId],
|
||||
[activeSubs.allMessages, navigation.activeChannelId],
|
||||
);
|
||||
|
||||
const threadMessages = useMemo(
|
||||
() =>
|
||||
allMessages
|
||||
activeSubs.allMessages
|
||||
.filter((m) => m.threadId === navigation.activeThreadId)
|
||||
.sort((a, b) =>
|
||||
a.sent.microsSinceUnixEpoch < b.sent.microsSinceUnixEpoch ? -1 : 1,
|
||||
),
|
||||
[allMessages, navigation.activeThreadId],
|
||||
[activeSubs.allMessages, navigation.activeThreadId],
|
||||
);
|
||||
|
||||
const activeThread = useMemo(
|
||||
() => allThreads.find((t) => t.id === navigation.activeThreadId),
|
||||
[allThreads, navigation.activeThreadId],
|
||||
() => activeSubs.allThreads.find((t) => t.id === navigation.activeThreadId),
|
||||
[activeSubs.allThreads, navigation.activeThreadId],
|
||||
);
|
||||
|
||||
const isActiveChannelVoice = useMemo(
|
||||
@@ -217,31 +214,33 @@ export function useChat(): ChatState {
|
||||
|
||||
const textChannels = useMemo(
|
||||
() =>
|
||||
channels.filter(
|
||||
activeSubs.channels.filter(
|
||||
(c) =>
|
||||
c.serverId === navigation.activeServerId && c.kind.tag === "Text",
|
||||
),
|
||||
[channels, navigation.activeServerId],
|
||||
[activeSubs.channels, navigation.activeServerId],
|
||||
);
|
||||
|
||||
const voiceChannels = useMemo(
|
||||
() =>
|
||||
channels.filter(
|
||||
activeSubs.channels.filter(
|
||||
(c) =>
|
||||
c.serverId === navigation.activeServerId && c.kind.tag === "Voice",
|
||||
),
|
||||
[channels, navigation.activeServerId],
|
||||
[activeSubs.channels, navigation.activeServerId],
|
||||
);
|
||||
|
||||
const activeServerMembers = useMemo(() => {
|
||||
if (!navigation.activeServerId) return [];
|
||||
const memberIdentities = new Set(
|
||||
serverMembers
|
||||
activeSubs.serverMembers
|
||||
.filter((m) => m.serverId === navigation.activeServerId)
|
||||
.map((m) => m.identity.toHexString()),
|
||||
);
|
||||
return users.filter((u) => memberIdentities.has(u.identity.toHexString()));
|
||||
}, [serverMembers, users, navigation.activeServerId]);
|
||||
return activeSubs.users.filter((u) =>
|
||||
memberIdentities.has(u.identity.toHexString()),
|
||||
);
|
||||
}, [activeSubs.serverMembers, activeSubs.users, navigation.activeServerId]);
|
||||
|
||||
return {
|
||||
...navigation,
|
||||
@@ -260,11 +259,13 @@ export function useChat(): ChatState {
|
||||
textChannels,
|
||||
voiceChannels,
|
||||
activeServerMembers,
|
||||
voiceActivity: activeSubs.voiceActivity,
|
||||
|
||||
// Helper functions
|
||||
getUsername: useCallback(
|
||||
(userIdentity: Identity | null) => getUsername(userIdentity, users),
|
||||
[users],
|
||||
(userIdentity: Identity | null) =>
|
||||
getUsername(userIdentity, activeSubs.users),
|
||||
[activeSubs.users],
|
||||
),
|
||||
formatTime: useCallback((ts: any) => formatTime(ts), []),
|
||||
};
|
||||
|
||||
@@ -11,9 +11,33 @@ export const useChannelAudioWebRTC = (
|
||||
isDeafened: boolean,
|
||||
) => {
|
||||
const [voiceStates] = useTable(tables.voice_state);
|
||||
const [offers] = useTable(tables.voice_sdp_offer);
|
||||
const [answers] = useTable(tables.voice_sdp_answer);
|
||||
const [iceCandidates] = useTable(tables.voice_ice_candidate);
|
||||
const [offers] = useTable(
|
||||
useMemo(
|
||||
() =>
|
||||
identity
|
||||
? tables.voice_sdp_offer.where((o) => o.receiver.eq(identity))
|
||||
: tables.voice_sdp_offer.where(() => false),
|
||||
[identity],
|
||||
),
|
||||
);
|
||||
const [answers] = useTable(
|
||||
useMemo(
|
||||
() =>
|
||||
identity
|
||||
? tables.voice_sdp_answer.where((o) => o.receiver.eq(identity))
|
||||
: tables.voice_sdp_answer.where(() => false),
|
||||
[identity],
|
||||
),
|
||||
);
|
||||
const [iceCandidates] = useTable(
|
||||
useMemo(
|
||||
() =>
|
||||
identity
|
||||
? tables.voice_ice_candidate.where((o) => o.receiver.eq(identity))
|
||||
: tables.voice_ice_candidate.where(() => false),
|
||||
[identity],
|
||||
),
|
||||
);
|
||||
|
||||
const sendSdpOffer = useReducer(reducers.sendVoiceSdpOffer);
|
||||
const sendSdpAnswer = useReducer(reducers.sendVoiceSdpAnswer);
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useState, useRef, useEffect, useCallback } from "react";
|
||||
import { useReducer } from "spacetimedb/react";
|
||||
import { reducers } from "../../../module_bindings";
|
||||
|
||||
export const useLocalMedia = () => {
|
||||
export const useLocalMedia = (connectedChannelId: bigint | undefined) => {
|
||||
const [localStream, setLocalStream] = useState<MediaStream | null>(null);
|
||||
const [localScreenStream, setLocalScreenStream] =
|
||||
useState<MediaStream | null>(null);
|
||||
@@ -94,9 +94,9 @@ export const useLocalMedia = () => {
|
||||
|
||||
// Voice Activity Detection
|
||||
useEffect(() => {
|
||||
if (!localStream || isMuted || isDeafened) {
|
||||
if (!localStream || isMuted || isDeafened || !connectedChannelId) {
|
||||
if (isTalkingRef.current) {
|
||||
setTalking({ talking: false });
|
||||
setTalking({ talking: false, channelId: connectedChannelId || 0n });
|
||||
isTalkingRef.current = false;
|
||||
setIsTalking(false);
|
||||
}
|
||||
@@ -122,14 +122,14 @@ export const useLocalMedia = () => {
|
||||
if (average > threshold) {
|
||||
silenceFrames = 0;
|
||||
if (!isTalkingRef.current) {
|
||||
setTalking({ talking: true });
|
||||
setTalking({ talking: true, channelId: connectedChannelId });
|
||||
isTalkingRef.current = true;
|
||||
setIsTalking(true);
|
||||
}
|
||||
} else {
|
||||
silenceFrames++;
|
||||
if (silenceFrames > maxSilenceFrames && isTalkingRef.current) {
|
||||
setTalking({ talking: false });
|
||||
setTalking({ talking: false, channelId: connectedChannelId });
|
||||
isTalkingRef.current = false;
|
||||
setIsTalking(false);
|
||||
}
|
||||
@@ -140,7 +140,7 @@ export const useLocalMedia = () => {
|
||||
clearInterval(checkAudio);
|
||||
audioContext.close();
|
||||
};
|
||||
}, [localStream, setTalking, isMuted, isDeafened]);
|
||||
}, [localStream, setTalking, isMuted, isDeafened, connectedChannelId]);
|
||||
|
||||
return {
|
||||
localStream,
|
||||
|
||||
@@ -1,18 +1,42 @@
|
||||
import { useEffect, useCallback, useMemo, useRef } from "react";
|
||||
import { Identity } from "spacetimedb";
|
||||
import { useTable, useReducer } from "spacetimedb/react";
|
||||
import { useTable, useReducer, useSpacetimeDB } from "spacetimedb/react";
|
||||
import { tables, reducers } from "../../../module_bindings";
|
||||
import { usePeerManager } from "./usePeerManager";
|
||||
|
||||
export const useScreenSharingWebRTC = (
|
||||
connectedChannelId: bigint | undefined,
|
||||
identity: Identity | null,
|
||||
localScreenStream: MediaStream | null,
|
||||
) => {
|
||||
const { identity } = useSpacetimeDB();
|
||||
const [watching] = useTable(tables.watching);
|
||||
const [offers] = useTable(tables.screen_sdp_offer);
|
||||
const [answers] = useTable(tables.screen_sdp_answer);
|
||||
const [iceCandidates] = useTable(tables.screen_ice_candidate);
|
||||
const [offers] = useTable(
|
||||
useMemo(
|
||||
() =>
|
||||
identity
|
||||
? tables.screen_sdp_offer.where((o) => o.receiver.eq(identity))
|
||||
: tables.screen_sdp_offer.where(() => false),
|
||||
[identity],
|
||||
),
|
||||
);
|
||||
const [answers] = useTable(
|
||||
useMemo(
|
||||
() =>
|
||||
identity
|
||||
? tables.screen_sdp_answer.where((o) => o.receiver.eq(identity))
|
||||
: tables.screen_sdp_answer.where(() => false),
|
||||
[identity],
|
||||
),
|
||||
);
|
||||
const [iceCandidates] = useTable(
|
||||
useMemo(
|
||||
() =>
|
||||
identity
|
||||
? tables.screen_ice_candidate.where((o) => o.receiver.eq(identity))
|
||||
: tables.screen_ice_candidate.where(() => false),
|
||||
[identity],
|
||||
),
|
||||
);
|
||||
|
||||
const sendSdpOffer = useReducer(reducers.sendScreenSdpOffer);
|
||||
const sendSdpAnswer = useReducer(reducers.sendScreenSdpAnswer);
|
||||
@@ -121,7 +145,7 @@ export const useScreenSharingWebRTC = (
|
||||
);
|
||||
|
||||
const peerManager = usePeerManager(
|
||||
identity,
|
||||
identity || null,
|
||||
"screen",
|
||||
false,
|
||||
onNegotiationNeeded,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useEffect } from "react";
|
||||
import { useCallback, useEffect, useMemo } from "react";
|
||||
import { Identity } from "spacetimedb";
|
||||
import { useTable, useReducer, useSpacetimeDB } from "spacetimedb/react";
|
||||
import { tables, reducers } from "../../../module_bindings";
|
||||
@@ -27,7 +27,7 @@ export const useWebRTC = (connectedChannelId: bigint | undefined) => {
|
||||
stopScreenShare: stopLocalScreenShare,
|
||||
requestMic,
|
||||
releaseMic,
|
||||
} = useLocalMedia();
|
||||
} = useLocalMedia(connectedChannelId);
|
||||
|
||||
// Sync mute/deafen to DB
|
||||
useEffect(() => {
|
||||
@@ -52,7 +52,6 @@ export const useWebRTC = (connectedChannelId: bigint | undefined) => {
|
||||
|
||||
const screen = useScreenSharingWebRTC(
|
||||
connectedChannelId,
|
||||
identity || null,
|
||||
localScreenStream,
|
||||
);
|
||||
|
||||
|
||||
Generated
+4
-1
@@ -9,7 +9,10 @@ import {
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from "spacetimedb";
|
||||
import { ChannelKind } from "./types";
|
||||
import {
|
||||
ChannelKind,
|
||||
} from "./types";
|
||||
|
||||
|
||||
export default __t.row({
|
||||
id: __t.u64().primaryKey(),
|
||||
|
||||
Generated
+265
-448
@@ -81,434 +81,262 @@ import WatchingRow from "./watching_table";
|
||||
|
||||
/** The schema information for all tables in this module. This is defined the same was as the tables would have been defined in the server. */
|
||||
const tablesSchema = __schema({
|
||||
channel: __table(
|
||||
{
|
||||
name: "channel",
|
||||
indexes: [
|
||||
{
|
||||
accessor: "id",
|
||||
name: "channel_id_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["id"],
|
||||
},
|
||||
{
|
||||
accessor: "by_server_id",
|
||||
name: "channel_server_id_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["serverId"],
|
||||
},
|
||||
],
|
||||
constraints: [
|
||||
{ name: "channel_id_key", constraint: "unique", columns: ["id"] },
|
||||
],
|
||||
},
|
||||
ChannelRow,
|
||||
),
|
||||
message: __table(
|
||||
{
|
||||
name: "message",
|
||||
indexes: [
|
||||
{
|
||||
accessor: "by_channel_id",
|
||||
name: "message_channel_id_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["channelId"],
|
||||
},
|
||||
{
|
||||
accessor: "id",
|
||||
name: "message_id_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["id"],
|
||||
},
|
||||
{
|
||||
accessor: "by_thread_id",
|
||||
name: "message_thread_id_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["threadId"],
|
||||
},
|
||||
],
|
||||
constraints: [
|
||||
{ name: "message_id_key", constraint: "unique", columns: ["id"] },
|
||||
],
|
||||
},
|
||||
MessageRow,
|
||||
),
|
||||
screen_ice_candidate: __table(
|
||||
{
|
||||
name: "screen_ice_candidate",
|
||||
indexes: [
|
||||
{
|
||||
accessor: "id",
|
||||
name: "screen_ice_candidate_id_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["id"],
|
||||
},
|
||||
{
|
||||
accessor: "by_receiver",
|
||||
name: "screen_ice_candidate_receiver_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["receiver"],
|
||||
},
|
||||
{
|
||||
accessor: "by_sender",
|
||||
name: "screen_ice_candidate_sender_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["sender"],
|
||||
},
|
||||
],
|
||||
constraints: [
|
||||
{
|
||||
name: "screen_ice_candidate_id_key",
|
||||
constraint: "unique",
|
||||
columns: ["id"],
|
||||
},
|
||||
],
|
||||
},
|
||||
ScreenIceCandidateRow,
|
||||
),
|
||||
screen_sdp_answer: __table(
|
||||
{
|
||||
name: "screen_sdp_answer",
|
||||
indexes: [
|
||||
{
|
||||
accessor: "id",
|
||||
name: "screen_sdp_answer_id_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["id"],
|
||||
},
|
||||
{
|
||||
accessor: "by_receiver",
|
||||
name: "screen_sdp_answer_receiver_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["receiver"],
|
||||
},
|
||||
{
|
||||
accessor: "by_sender",
|
||||
name: "screen_sdp_answer_sender_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["sender"],
|
||||
},
|
||||
],
|
||||
constraints: [
|
||||
{
|
||||
name: "screen_sdp_answer_id_key",
|
||||
constraint: "unique",
|
||||
columns: ["id"],
|
||||
},
|
||||
],
|
||||
},
|
||||
ScreenSdpAnswerRow,
|
||||
),
|
||||
screen_sdp_offer: __table(
|
||||
{
|
||||
name: "screen_sdp_offer",
|
||||
indexes: [
|
||||
{
|
||||
accessor: "id",
|
||||
name: "screen_sdp_offer_id_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["id"],
|
||||
},
|
||||
{
|
||||
accessor: "by_receiver",
|
||||
name: "screen_sdp_offer_receiver_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["receiver"],
|
||||
},
|
||||
{
|
||||
accessor: "by_sender",
|
||||
name: "screen_sdp_offer_sender_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["sender"],
|
||||
},
|
||||
],
|
||||
constraints: [
|
||||
{
|
||||
name: "screen_sdp_offer_id_key",
|
||||
constraint: "unique",
|
||||
columns: ["id"],
|
||||
},
|
||||
],
|
||||
},
|
||||
ScreenSdpOfferRow,
|
||||
),
|
||||
server: __table(
|
||||
{
|
||||
name: "server",
|
||||
indexes: [
|
||||
{
|
||||
accessor: "id",
|
||||
name: "server_id_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["id"],
|
||||
},
|
||||
],
|
||||
constraints: [
|
||||
{ name: "server_id_key", constraint: "unique", columns: ["id"] },
|
||||
],
|
||||
},
|
||||
ServerRow,
|
||||
),
|
||||
server_member: __table(
|
||||
{
|
||||
name: "server_member",
|
||||
indexes: [
|
||||
{
|
||||
accessor: "id",
|
||||
name: "server_member_id_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["id"],
|
||||
},
|
||||
{
|
||||
accessor: "by_identity",
|
||||
name: "server_member_identity_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["identity"],
|
||||
},
|
||||
{
|
||||
accessor: "by_server_id",
|
||||
name: "server_member_server_id_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["serverId"],
|
||||
},
|
||||
],
|
||||
constraints: [
|
||||
{ name: "server_member_id_key", constraint: "unique", columns: ["id"] },
|
||||
],
|
||||
},
|
||||
ServerMemberRow,
|
||||
),
|
||||
thread: __table(
|
||||
{
|
||||
name: "thread",
|
||||
indexes: [
|
||||
{
|
||||
accessor: "by_channel_id",
|
||||
name: "thread_channel_id_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["channelId"],
|
||||
},
|
||||
{
|
||||
accessor: "id",
|
||||
name: "thread_id_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["id"],
|
||||
},
|
||||
{
|
||||
accessor: "parent_message_id",
|
||||
name: "thread_parent_message_id_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["parentMessageId"],
|
||||
},
|
||||
],
|
||||
constraints: [
|
||||
{ name: "thread_id_key", constraint: "unique", columns: ["id"] },
|
||||
{
|
||||
name: "thread_parent_message_id_key",
|
||||
constraint: "unique",
|
||||
columns: ["parentMessageId"],
|
||||
},
|
||||
],
|
||||
},
|
||||
ThreadRow,
|
||||
),
|
||||
user: __table(
|
||||
{
|
||||
name: "user",
|
||||
indexes: [
|
||||
{
|
||||
accessor: "identity",
|
||||
name: "user_identity_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["identity"],
|
||||
},
|
||||
],
|
||||
constraints: [
|
||||
{
|
||||
name: "user_identity_key",
|
||||
constraint: "unique",
|
||||
columns: ["identity"],
|
||||
},
|
||||
],
|
||||
},
|
||||
UserRow,
|
||||
),
|
||||
voice_activity: __table(
|
||||
{
|
||||
name: "voice_activity",
|
||||
indexes: [
|
||||
{
|
||||
accessor: "identity",
|
||||
name: "voice_activity_identity_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["identity"],
|
||||
},
|
||||
],
|
||||
constraints: [
|
||||
{
|
||||
name: "voice_activity_identity_key",
|
||||
constraint: "unique",
|
||||
columns: ["identity"],
|
||||
},
|
||||
],
|
||||
},
|
||||
VoiceActivityRow,
|
||||
),
|
||||
voice_ice_candidate: __table(
|
||||
{
|
||||
name: "voice_ice_candidate",
|
||||
indexes: [
|
||||
{
|
||||
accessor: "id",
|
||||
name: "voice_ice_candidate_id_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["id"],
|
||||
},
|
||||
{
|
||||
accessor: "by_receiver",
|
||||
name: "voice_ice_candidate_receiver_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["receiver"],
|
||||
},
|
||||
{
|
||||
accessor: "by_sender",
|
||||
name: "voice_ice_candidate_sender_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["sender"],
|
||||
},
|
||||
],
|
||||
constraints: [
|
||||
{
|
||||
name: "voice_ice_candidate_id_key",
|
||||
constraint: "unique",
|
||||
columns: ["id"],
|
||||
},
|
||||
],
|
||||
},
|
||||
VoiceIceCandidateRow,
|
||||
),
|
||||
voice_sdp_answer: __table(
|
||||
{
|
||||
name: "voice_sdp_answer",
|
||||
indexes: [
|
||||
{
|
||||
accessor: "id",
|
||||
name: "voice_sdp_answer_id_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["id"],
|
||||
},
|
||||
{
|
||||
accessor: "by_receiver",
|
||||
name: "voice_sdp_answer_receiver_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["receiver"],
|
||||
},
|
||||
{
|
||||
accessor: "by_sender",
|
||||
name: "voice_sdp_answer_sender_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["sender"],
|
||||
},
|
||||
],
|
||||
constraints: [
|
||||
{
|
||||
name: "voice_sdp_answer_id_key",
|
||||
constraint: "unique",
|
||||
columns: ["id"],
|
||||
},
|
||||
],
|
||||
},
|
||||
VoiceSdpAnswerRow,
|
||||
),
|
||||
voice_sdp_offer: __table(
|
||||
{
|
||||
name: "voice_sdp_offer",
|
||||
indexes: [
|
||||
{
|
||||
accessor: "id",
|
||||
name: "voice_sdp_offer_id_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["id"],
|
||||
},
|
||||
{
|
||||
accessor: "by_receiver",
|
||||
name: "voice_sdp_offer_receiver_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["receiver"],
|
||||
},
|
||||
{
|
||||
accessor: "by_sender",
|
||||
name: "voice_sdp_offer_sender_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["sender"],
|
||||
},
|
||||
],
|
||||
constraints: [
|
||||
{
|
||||
name: "voice_sdp_offer_id_key",
|
||||
constraint: "unique",
|
||||
columns: ["id"],
|
||||
},
|
||||
],
|
||||
},
|
||||
VoiceSdpOfferRow,
|
||||
),
|
||||
voice_state: __table(
|
||||
{
|
||||
name: "voice_state",
|
||||
indexes: [
|
||||
{
|
||||
accessor: "by_channel_id",
|
||||
name: "voice_state_channel_id_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["channelId"],
|
||||
},
|
||||
{
|
||||
accessor: "identity",
|
||||
name: "voice_state_identity_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["identity"],
|
||||
},
|
||||
],
|
||||
constraints: [
|
||||
{
|
||||
name: "voice_state_identity_key",
|
||||
constraint: "unique",
|
||||
columns: ["identity"],
|
||||
},
|
||||
],
|
||||
},
|
||||
VoiceStateRow,
|
||||
),
|
||||
watching: __table(
|
||||
{
|
||||
name: "watching",
|
||||
indexes: [
|
||||
{
|
||||
accessor: "id",
|
||||
name: "watching_id_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["id"],
|
||||
},
|
||||
{
|
||||
accessor: "by_watchee",
|
||||
name: "watching_watchee_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["watchee"],
|
||||
},
|
||||
{
|
||||
accessor: "by_watcher",
|
||||
name: "watching_watcher_idx_btree",
|
||||
algorithm: "btree",
|
||||
columns: ["watcher"],
|
||||
},
|
||||
],
|
||||
constraints: [
|
||||
{ name: "watching_id_key", constraint: "unique", columns: ["id"] },
|
||||
],
|
||||
},
|
||||
WatchingRow,
|
||||
),
|
||||
channel: __table({
|
||||
name: 'channel',
|
||||
indexes: [
|
||||
{ accessor: 'id', name: 'channel_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'id',
|
||||
] },
|
||||
{ accessor: 'by_server_id', name: 'channel_server_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'serverId',
|
||||
] },
|
||||
],
|
||||
constraints: [
|
||||
{ name: 'channel_id_key', constraint: 'unique', columns: ['id'] },
|
||||
],
|
||||
}, ChannelRow),
|
||||
message: __table({
|
||||
name: 'message',
|
||||
indexes: [
|
||||
{ accessor: 'by_channel_id', name: 'message_channel_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'channelId',
|
||||
] },
|
||||
{ accessor: 'id', name: 'message_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'id',
|
||||
] },
|
||||
{ accessor: 'by_thread_id', name: 'message_thread_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'threadId',
|
||||
] },
|
||||
],
|
||||
constraints: [
|
||||
{ name: 'message_id_key', constraint: 'unique', columns: ['id'] },
|
||||
],
|
||||
}, MessageRow),
|
||||
screen_ice_candidate: __table({
|
||||
name: 'screen_ice_candidate',
|
||||
indexes: [
|
||||
{ accessor: 'by_channel_id', name: 'screen_ice_candidate_channel_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'channelId',
|
||||
] },
|
||||
{ accessor: 'id', name: 'screen_ice_candidate_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'id',
|
||||
] },
|
||||
{ accessor: 'by_receiver', name: 'screen_ice_candidate_receiver_idx_btree', algorithm: 'btree', columns: [
|
||||
'receiver',
|
||||
] },
|
||||
{ accessor: 'by_sender', name: 'screen_ice_candidate_sender_idx_btree', algorithm: 'btree', columns: [
|
||||
'sender',
|
||||
] },
|
||||
],
|
||||
constraints: [
|
||||
{ name: 'screen_ice_candidate_id_key', constraint: 'unique', columns: ['id'] },
|
||||
],
|
||||
}, ScreenIceCandidateRow),
|
||||
screen_sdp_answer: __table({
|
||||
name: 'screen_sdp_answer',
|
||||
indexes: [
|
||||
{ accessor: 'by_channel_id', name: 'screen_sdp_answer_channel_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'channelId',
|
||||
] },
|
||||
{ accessor: 'id', name: 'screen_sdp_answer_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'id',
|
||||
] },
|
||||
{ accessor: 'by_receiver', name: 'screen_sdp_answer_receiver_idx_btree', algorithm: 'btree', columns: [
|
||||
'receiver',
|
||||
] },
|
||||
{ accessor: 'by_sender', name: 'screen_sdp_answer_sender_idx_btree', algorithm: 'btree', columns: [
|
||||
'sender',
|
||||
] },
|
||||
],
|
||||
constraints: [
|
||||
{ name: 'screen_sdp_answer_id_key', constraint: 'unique', columns: ['id'] },
|
||||
],
|
||||
}, ScreenSdpAnswerRow),
|
||||
screen_sdp_offer: __table({
|
||||
name: 'screen_sdp_offer',
|
||||
indexes: [
|
||||
{ accessor: 'by_channel_id', name: 'screen_sdp_offer_channel_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'channelId',
|
||||
] },
|
||||
{ accessor: 'id', name: 'screen_sdp_offer_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'id',
|
||||
] },
|
||||
{ accessor: 'by_receiver', name: 'screen_sdp_offer_receiver_idx_btree', algorithm: 'btree', columns: [
|
||||
'receiver',
|
||||
] },
|
||||
{ accessor: 'by_sender', name: 'screen_sdp_offer_sender_idx_btree', algorithm: 'btree', columns: [
|
||||
'sender',
|
||||
] },
|
||||
],
|
||||
constraints: [
|
||||
{ name: 'screen_sdp_offer_id_key', constraint: 'unique', columns: ['id'] },
|
||||
],
|
||||
}, ScreenSdpOfferRow),
|
||||
server: __table({
|
||||
name: 'server',
|
||||
indexes: [
|
||||
{ accessor: 'id', name: 'server_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'id',
|
||||
] },
|
||||
],
|
||||
constraints: [
|
||||
{ name: 'server_id_key', constraint: 'unique', columns: ['id'] },
|
||||
],
|
||||
}, ServerRow),
|
||||
server_member: __table({
|
||||
name: 'server_member',
|
||||
indexes: [
|
||||
{ accessor: 'id', name: 'server_member_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'id',
|
||||
] },
|
||||
{ accessor: 'by_identity', name: 'server_member_identity_idx_btree', algorithm: 'btree', columns: [
|
||||
'identity',
|
||||
] },
|
||||
{ accessor: 'by_server_id', name: 'server_member_server_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'serverId',
|
||||
] },
|
||||
],
|
||||
constraints: [
|
||||
{ name: 'server_member_id_key', constraint: 'unique', columns: ['id'] },
|
||||
],
|
||||
}, ServerMemberRow),
|
||||
thread: __table({
|
||||
name: 'thread',
|
||||
indexes: [
|
||||
{ accessor: 'by_channel_id', name: 'thread_channel_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'channelId',
|
||||
] },
|
||||
{ accessor: 'id', name: 'thread_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'id',
|
||||
] },
|
||||
{ accessor: 'parent_message_id', name: 'thread_parent_message_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'parentMessageId',
|
||||
] },
|
||||
],
|
||||
constraints: [
|
||||
{ name: 'thread_id_key', constraint: 'unique', columns: ['id'] },
|
||||
{ name: 'thread_parent_message_id_key', constraint: 'unique', columns: ['parentMessageId'] },
|
||||
],
|
||||
}, ThreadRow),
|
||||
user: __table({
|
||||
name: 'user',
|
||||
indexes: [
|
||||
{ accessor: 'identity', name: 'user_identity_idx_btree', algorithm: 'btree', columns: [
|
||||
'identity',
|
||||
] },
|
||||
],
|
||||
constraints: [
|
||||
{ name: 'user_identity_key', constraint: 'unique', columns: ['identity'] },
|
||||
],
|
||||
}, UserRow),
|
||||
voice_activity: __table({
|
||||
name: 'voice_activity',
|
||||
indexes: [
|
||||
{ accessor: 'by_channel_id', name: 'voice_activity_channel_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'channelId',
|
||||
] },
|
||||
{ accessor: 'identity', name: 'voice_activity_identity_idx_btree', algorithm: 'btree', columns: [
|
||||
'identity',
|
||||
] },
|
||||
],
|
||||
constraints: [
|
||||
{ name: 'voice_activity_identity_key', constraint: 'unique', columns: ['identity'] },
|
||||
],
|
||||
}, VoiceActivityRow),
|
||||
voice_ice_candidate: __table({
|
||||
name: 'voice_ice_candidate',
|
||||
indexes: [
|
||||
{ accessor: 'by_channel_id', name: 'voice_ice_candidate_channel_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'channelId',
|
||||
] },
|
||||
{ accessor: 'id', name: 'voice_ice_candidate_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'id',
|
||||
] },
|
||||
{ accessor: 'by_receiver', name: 'voice_ice_candidate_receiver_idx_btree', algorithm: 'btree', columns: [
|
||||
'receiver',
|
||||
] },
|
||||
{ accessor: 'by_sender', name: 'voice_ice_candidate_sender_idx_btree', algorithm: 'btree', columns: [
|
||||
'sender',
|
||||
] },
|
||||
],
|
||||
constraints: [
|
||||
{ name: 'voice_ice_candidate_id_key', constraint: 'unique', columns: ['id'] },
|
||||
],
|
||||
}, VoiceIceCandidateRow),
|
||||
voice_sdp_answer: __table({
|
||||
name: 'voice_sdp_answer',
|
||||
indexes: [
|
||||
{ accessor: 'by_channel_id', name: 'voice_sdp_answer_channel_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'channelId',
|
||||
] },
|
||||
{ accessor: 'id', name: 'voice_sdp_answer_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'id',
|
||||
] },
|
||||
{ accessor: 'by_receiver', name: 'voice_sdp_answer_receiver_idx_btree', algorithm: 'btree', columns: [
|
||||
'receiver',
|
||||
] },
|
||||
{ accessor: 'by_sender', name: 'voice_sdp_answer_sender_idx_btree', algorithm: 'btree', columns: [
|
||||
'sender',
|
||||
] },
|
||||
],
|
||||
constraints: [
|
||||
{ name: 'voice_sdp_answer_id_key', constraint: 'unique', columns: ['id'] },
|
||||
],
|
||||
}, VoiceSdpAnswerRow),
|
||||
voice_sdp_offer: __table({
|
||||
name: 'voice_sdp_offer',
|
||||
indexes: [
|
||||
{ accessor: 'by_channel_id', name: 'voice_sdp_offer_channel_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'channelId',
|
||||
] },
|
||||
{ accessor: 'id', name: 'voice_sdp_offer_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'id',
|
||||
] },
|
||||
{ accessor: 'by_receiver', name: 'voice_sdp_offer_receiver_idx_btree', algorithm: 'btree', columns: [
|
||||
'receiver',
|
||||
] },
|
||||
{ accessor: 'by_sender', name: 'voice_sdp_offer_sender_idx_btree', algorithm: 'btree', columns: [
|
||||
'sender',
|
||||
] },
|
||||
],
|
||||
constraints: [
|
||||
{ name: 'voice_sdp_offer_id_key', constraint: 'unique', columns: ['id'] },
|
||||
],
|
||||
}, VoiceSdpOfferRow),
|
||||
voice_state: __table({
|
||||
name: 'voice_state',
|
||||
indexes: [
|
||||
{ accessor: 'by_channel_id', name: 'voice_state_channel_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'channelId',
|
||||
] },
|
||||
{ accessor: 'identity', name: 'voice_state_identity_idx_btree', algorithm: 'btree', columns: [
|
||||
'identity',
|
||||
] },
|
||||
],
|
||||
constraints: [
|
||||
{ name: 'voice_state_identity_key', constraint: 'unique', columns: ['identity'] },
|
||||
],
|
||||
}, VoiceStateRow),
|
||||
watching: __table({
|
||||
name: 'watching',
|
||||
indexes: [
|
||||
{ accessor: 'by_channel_id', name: 'watching_channel_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'channelId',
|
||||
] },
|
||||
{ accessor: 'id', name: 'watching_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'id',
|
||||
] },
|
||||
{ accessor: 'by_watchee', name: 'watching_watchee_idx_btree', algorithm: 'btree', columns: [
|
||||
'watchee',
|
||||
] },
|
||||
{ accessor: 'by_watcher', name: 'watching_watcher_idx_btree', algorithm: 'btree', columns: [
|
||||
'watcher',
|
||||
] },
|
||||
],
|
||||
constraints: [
|
||||
{ name: 'watching_id_key', constraint: 'unique', columns: ['id'] },
|
||||
],
|
||||
}, WatchingRow),
|
||||
});
|
||||
|
||||
/** The schema information for all reducers in this module. This is defined the same way as the reducers would have been defined in the server, except the body of the reducer is omitted in code generation. */
|
||||
@@ -539,7 +367,8 @@ const reducersSchema = __reducers(
|
||||
);
|
||||
|
||||
/** The schema information for all procedures in this module. This is defined the same way as the procedures would have been defined in the server. */
|
||||
const proceduresSchema = __procedures();
|
||||
const proceduresSchema = __procedures(
|
||||
);
|
||||
|
||||
/** The remote SpacetimeDB module schema, both runtime and type information. */
|
||||
const REMOTE_MODULE = {
|
||||
@@ -556,33 +385,24 @@ const REMOTE_MODULE = {
|
||||
>;
|
||||
|
||||
/** The tables available in this remote SpacetimeDB module. Each table reference doubles as a query builder. */
|
||||
export const tables: __QueryBuilder<typeof tablesSchema.schemaType> =
|
||||
__makeQueryBuilder(tablesSchema.schemaType);
|
||||
export const tables: __QueryBuilder<typeof tablesSchema.schemaType> = __makeQueryBuilder(tablesSchema.schemaType);
|
||||
|
||||
/** The reducers available in this remote SpacetimeDB module. */
|
||||
export const reducers = __convertToAccessorMap(
|
||||
reducersSchema.reducersType.reducers,
|
||||
);
|
||||
export const reducers = __convertToAccessorMap(reducersSchema.reducersType.reducers);
|
||||
|
||||
/** The context type returned in callbacks for all possible events. */
|
||||
export type EventContext = __EventContextInterface<typeof REMOTE_MODULE>;
|
||||
/** The context type returned in callbacks for reducer events. */
|
||||
export type ReducerEventContext = __ReducerEventContextInterface<
|
||||
typeof REMOTE_MODULE
|
||||
>;
|
||||
export type ReducerEventContext = __ReducerEventContextInterface<typeof REMOTE_MODULE>;
|
||||
/** The context type returned in callbacks for subscription events. */
|
||||
export type SubscriptionEventContext = __SubscriptionEventContextInterface<
|
||||
typeof REMOTE_MODULE
|
||||
>;
|
||||
export type SubscriptionEventContext = __SubscriptionEventContextInterface<typeof REMOTE_MODULE>;
|
||||
/** The context type returned in callbacks for error events. */
|
||||
export type ErrorContext = __ErrorContextInterface<typeof REMOTE_MODULE>;
|
||||
/** The subscription handle type to manage active subscriptions created from a {@link SubscriptionBuilder}. */
|
||||
export type SubscriptionHandle = __SubscriptionHandleImpl<typeof REMOTE_MODULE>;
|
||||
|
||||
/** Builder class to configure a new subscription to the remote SpacetimeDB instance. */
|
||||
export class SubscriptionBuilder extends __SubscriptionBuilderImpl<
|
||||
typeof REMOTE_MODULE
|
||||
> {}
|
||||
export class SubscriptionBuilder extends __SubscriptionBuilderImpl<typeof REMOTE_MODULE> {}
|
||||
|
||||
/** Builder class to configure a new database connection to the remote SpacetimeDB instance. */
|
||||
export class DbConnectionBuilder extends __DbConnectionBuilder<DbConnection> {}
|
||||
@@ -591,11 +411,7 @@ export class DbConnectionBuilder extends __DbConnectionBuilder<DbConnection> {}
|
||||
export class DbConnection extends __DbConnectionImpl<typeof REMOTE_MODULE> {
|
||||
/** Creates a new {@link DbConnectionBuilder} to configure and connect to the remote SpacetimeDB instance. */
|
||||
static builder = (): DbConnectionBuilder => {
|
||||
return new DbConnectionBuilder(
|
||||
REMOTE_MODULE,
|
||||
(config: __DbConnectionConfig<typeof REMOTE_MODULE>) =>
|
||||
new DbConnection(config),
|
||||
);
|
||||
return new DbConnectionBuilder(REMOTE_MODULE, (config: __DbConnectionConfig<typeof REMOTE_MODULE>) => new DbConnection(config));
|
||||
};
|
||||
|
||||
/** Creates a new {@link SubscriptionBuilder} to configure a subscription to the remote SpacetimeDB instance. */
|
||||
@@ -603,3 +419,4 @@ export class DbConnection extends __DbConnectionImpl<typeof REMOTE_MODULE> {
|
||||
return new SubscriptionBuilder(this);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -12,4 +12,5 @@ import {
|
||||
|
||||
export default {
|
||||
talking: __t.bool(),
|
||||
channelId: __t.u64(),
|
||||
};
|
||||
|
||||
Generated
+2
@@ -99,6 +99,7 @@ export type User = __Infer<typeof User>;
|
||||
|
||||
export const VoiceActivity = __t.object("VoiceActivity", {
|
||||
identity: __t.identity(),
|
||||
channelId: __t.u64(),
|
||||
isTalking: __t.bool(),
|
||||
});
|
||||
export type VoiceActivity = __Infer<typeof VoiceActivity>;
|
||||
@@ -146,3 +147,4 @@ export const Watching = __t.object("Watching", {
|
||||
channelId: __t.u64(),
|
||||
});
|
||||
export type Watching = __Infer<typeof Watching>;
|
||||
|
||||
|
||||
Generated
+2
@@ -6,3 +6,5 @@
|
||||
import { type Infer as __Infer } from "spacetimedb";
|
||||
|
||||
// Import all procedure arg schemas
|
||||
|
||||
|
||||
|
||||
Generated
+6
-15
@@ -40,21 +40,11 @@ export type LeaveVoiceParams = __Infer<typeof LeaveVoiceReducer>;
|
||||
export type LoginParams = __Infer<typeof LoginReducer>;
|
||||
export type RegisterParams = __Infer<typeof RegisterReducer>;
|
||||
export type SendMessageParams = __Infer<typeof SendMessageReducer>;
|
||||
export type SendScreenIceCandidateParams = __Infer<
|
||||
typeof SendScreenIceCandidateReducer
|
||||
>;
|
||||
export type SendScreenSdpAnswerParams = __Infer<
|
||||
typeof SendScreenSdpAnswerReducer
|
||||
>;
|
||||
export type SendScreenSdpOfferParams = __Infer<
|
||||
typeof SendScreenSdpOfferReducer
|
||||
>;
|
||||
export type SendVoiceIceCandidateParams = __Infer<
|
||||
typeof SendVoiceIceCandidateReducer
|
||||
>;
|
||||
export type SendVoiceSdpAnswerParams = __Infer<
|
||||
typeof SendVoiceSdpAnswerReducer
|
||||
>;
|
||||
export type SendScreenIceCandidateParams = __Infer<typeof SendScreenIceCandidateReducer>;
|
||||
export type SendScreenSdpAnswerParams = __Infer<typeof SendScreenSdpAnswerReducer>;
|
||||
export type SendScreenSdpOfferParams = __Infer<typeof SendScreenSdpOfferReducer>;
|
||||
export type SendVoiceIceCandidateParams = __Infer<typeof SendVoiceIceCandidateReducer>;
|
||||
export type SendVoiceSdpAnswerParams = __Infer<typeof SendVoiceSdpAnswerReducer>;
|
||||
export type SendVoiceSdpOfferParams = __Infer<typeof SendVoiceSdpOfferReducer>;
|
||||
export type SetDeafenParams = __Infer<typeof SetDeafenReducer>;
|
||||
export type SetMuteParams = __Infer<typeof SetMuteReducer>;
|
||||
@@ -63,3 +53,4 @@ export type SetSharingScreenParams = __Infer<typeof SetSharingScreenReducer>;
|
||||
export type SetTalkingParams = __Infer<typeof SetTalkingReducer>;
|
||||
export type StartWatchingParams = __Infer<typeof StartWatchingReducer>;
|
||||
export type StopWatchingParams = __Infer<typeof StopWatchingReducer>;
|
||||
|
||||
|
||||
+1
@@ -12,5 +12,6 @@ import {
|
||||
|
||||
export default __t.row({
|
||||
identity: __t.identity().primaryKey(),
|
||||
channelId: __t.u64().name("channel_id"),
|
||||
isTalking: __t.bool().name("is_talking"),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user