table and reducer updates

This commit is contained in:
2026-03-30 17:01:52 -04:00
parent b9524fbfd4
commit 76ed6d7ab3
+51 -21
View File
@@ -212,7 +212,7 @@ export const register = spacetimedb.reducer(
(ctx, { username, password }) => {
validateName(username);
if (!password || password.length < 4) throw new SenderError('Password must be at least 4 characters');
for (const u of ctx.db.user.iter()) {
if (u.username === username) {
throw new SenderError('Username already taken');
@@ -221,11 +221,11 @@ export const register = spacetimedb.reducer(
const user = ctx.db.user.identity.find(ctx.sender);
if (user) {
ctx.db.user.identity.update({
...user,
username,
ctx.db.user.identity.update({
...user,
username,
password,
name: user.name || username
name: user.name || username
});
} else {
ctx.db.user.insert({
@@ -302,12 +302,12 @@ export const join_server = spacetimedb.reducer(
}
const s = ctx.db.server.id.find(serverId);
if (!s) throw new SenderError('Server not found');
// Check if already a member
for (const m of ctx.db.server_member.by_identity.filter(ctx.sender)) {
if (m.server_id === serverId) return;
}
ctx.db.server_member.insert({ id: 0n, identity: ctx.sender, server_id: serverId });
}
);
@@ -317,7 +317,7 @@ export const leave_server = spacetimedb.reducer(
(ctx, { serverId }) => {
const user = ctx.db.user.identity.find(ctx.sender);
if (!user) return;
for (const m of ctx.db.server_member.by_identity.filter(ctx.sender)) {
if (m.server_id === serverId) {
ctx.db.server_member.id.delete(m.id);
@@ -336,11 +336,11 @@ export const create_channel = spacetimedb.reducer(
}
const s = ctx.db.server.id.find(serverId);
if (!s) throw new SenderError('Server not found');
ctx.db.channel.insert({
id: 0n,
server_id: serverId,
name,
kind: isVoice ? { tag: 'Voice' } : { tag: 'Text' }
ctx.db.channel.insert({
id: 0n,
server_id: serverId,
name,
kind: isVoice ? { tag: 'Voice' } : { tag: 'Text' }
});
}
);
@@ -354,7 +354,7 @@ export const join_voice = spacetimedb.reducer(
}
const chan = ctx.db.channel.id.find(channelId);
if (!chan || chan.kind.tag !== 'Voice') throw new SenderError('Invalid voice channel');
const existing = ctx.db.voice_state.identity.find(ctx.sender);
if (existing) {
if (existing.channel_id !== channelId) {
@@ -382,7 +382,7 @@ export const start_watching = spacetimedb.reducer(
(ctx, { watchee, channelId }) => {
// Prevent self-watching or duplicate watching
if (ctx.sender.isEqual(watchee)) return;
for (const w of ctx.db.watching.by_watcher.filter(ctx.sender)) {
if (w.watchee.isEqual(watchee)) return;
}
@@ -412,6 +412,31 @@ export const leave_voice = spacetimedb.reducer((ctx) => {
export const send_sdp_offer = spacetimedb.reducer(
{ receiver: t.identity(), sdp: t.string(), channelId: t.u64() },
(ctx, { receiver, sdp, channelId }) => {
// Clear any existing offers/answers/candidates between this pair in both directions
// to ensure a fresh negotiation state.
// Outgoing from sender to receiver
for (const offer of ctx.db.sdp_offer.by_sender.filter(ctx.sender)) {
if (offer.receiver.isEqual(receiver)) ctx.db.sdp_offer.id.delete(offer.id);
}
for (const answer of ctx.db.sdp_answer.by_sender.filter(ctx.sender)) {
if (answer.receiver.isEqual(receiver)) ctx.db.sdp_answer.id.delete(answer.id);
}
for (const cand of ctx.db.ice_candidate.by_sender.filter(ctx.sender)) {
if (cand.receiver.isEqual(receiver)) ctx.db.ice_candidate.id.delete(cand.id);
}
// Incoming to sender from receiver (stale messages from previous negotiations)
for (const offer of ctx.db.sdp_offer.by_receiver.filter(ctx.sender)) {
if (offer.sender.isEqual(receiver)) ctx.db.sdp_offer.id.delete(offer.id);
}
for (const answer of ctx.db.sdp_answer.by_receiver.filter(ctx.sender)) {
if (answer.sender.isEqual(receiver)) ctx.db.sdp_answer.id.delete(answer.id);
}
for (const cand of ctx.db.ice_candidate.by_receiver.filter(ctx.sender)) {
if (cand.sender.isEqual(receiver)) ctx.db.ice_candidate.id.delete(cand.id);
}
ctx.db.sdp_offer.insert({ id: 0n, sender: ctx.sender, receiver, sdp, channel_id: channelId });
}
);
@@ -419,6 +444,11 @@ export const send_sdp_offer = spacetimedb.reducer(
export const send_sdp_answer = spacetimedb.reducer(
{ receiver: t.identity(), sdp: t.string(), channelId: t.u64() },
(ctx, { receiver, sdp, channelId }) => {
for (const answer of ctx.db.sdp_answer.by_sender.filter(ctx.sender)) {
if (answer.receiver.isEqual(receiver)) {
ctx.db.sdp_answer.id.delete(answer.id);
}
}
ctx.db.sdp_answer.insert({ id: 0n, sender: ctx.sender, receiver, sdp, channel_id: channelId });
}
);
@@ -432,7 +462,7 @@ export const send_ice_candidate = spacetimedb.reducer(
function clearSignalingForUser(ctx: any, identity: any) {
// Clean up stale signaling messages for the user
// Clean up watching status
for (const w of ctx.db.watching.by_watcher.filter(identity)) {
ctx.db.watching.id.delete(w.id);
@@ -473,7 +503,7 @@ export const create_thread = spacetimedb.reducer(
}
const parentMsg = ctx.db.message.id.find(parentMessageId);
if (!parentMsg) throw new SenderError('Parent message not found');
ctx.db.thread.insert({ id: 0n, channel_id: channelId, parent_message_id: parentMessageId, name });
}
);
@@ -482,7 +512,7 @@ export const send_message = spacetimedb.reducer(
{ text: t.string(), channelId: t.u64(), threadId: t.u64().optional() },
(ctx, { text, channelId, threadId }) => {
if (!text || text.trim().length === 0) throw new SenderError('Messages must not be empty');
const user = ctx.db.user.identity.find(ctx.sender);
if (!user || (!user.username && !user.subject)) {
throw new SenderError('You must be logged in to send messages');
@@ -514,7 +544,7 @@ export const init = spacetimedb.init(ctx => {
export const onConnect = spacetimedb.clientConnected(ctx => {
const user = ctx.db.user.identity.find(ctx.sender);
if (ctx.senderAuth.hasJWT && ctx.senderAuth.jwt) {
const jwt = ctx.senderAuth.jwt;
const issuer = jwt.issuer;
@@ -523,8 +553,8 @@ export const onConnect = spacetimedb.clientConnected(ctx => {
const name = (payload.name as string) || (payload.nickname as string) || (payload.preferred_username as string) || (payload.email as string);
if (user) {
ctx.db.user.identity.update({
...user,
ctx.db.user.identity.update({
...user,
online: true,
talking: false,
name: user.name || name,