fix: multiple bugs

* Hard-refresh saves coords before disconnect
* Position persisted between sessions
* Scaling isn't assumed on initial, it waits for calculations to finish
This commit is contained in:
Evan Carroll 2026-01-13 23:42:22 -06:00
parent a96581cbf0
commit 98f38c9714
3 changed files with 99 additions and 8 deletions

View file

@ -8,16 +8,31 @@ use chattyness_error::AppError;
/// Join a channel as an authenticated user.
///
/// Creates a channel_members entry with default position (400, 300).
/// Restores the user's last position if they were previously in the same scene,
/// otherwise uses the default position (400, 300).
/// Note: channel_id is actually scene_id in this system (scenes are used directly as channels).
pub async fn join_channel<'e>(
executor: impl PgExecutor<'e>,
channel_id: Uuid,
user_id: Uuid,
) -> Result<ChannelMember, AppError> {
// Note: channel_id is actually scene_id in this system
let member = sqlx::query_as::<_, ChannelMember>(
r#"
INSERT INTO realm.channel_members (channel_id, user_id, position)
VALUES ($1, $2, ST_SetSRID(ST_MakePoint(400, 300), 0))
SELECT $1, $2, COALESCE(
-- Try to restore last position if user was in the same scene
-- Note: channel_id = scene_id in this system
(SELECT m.last_position
FROM realm.memberships m
JOIN realm.scenes s ON s.id = $1
WHERE m.user_id = $2
AND m.realm_id = s.realm_id
AND m.last_scene_id = $1
AND m.last_position IS NOT NULL),
-- Default position
ST_SetSRID(ST_MakePoint(400, 300), 0)
)
ON CONFLICT (channel_id, user_id) DO UPDATE
SET joined_at = now()
RETURNING
@ -67,13 +82,40 @@ pub async fn ensure_active_avatar<'e>(
}
/// Leave a channel.
///
/// Saves the user's current position to memberships.last_position before removing them.
/// Note: channel_id is actually scene_id in this system (scenes are used directly as channels).
pub async fn leave_channel<'e>(
executor: impl PgExecutor<'e>,
channel_id: Uuid,
user_id: Uuid,
) -> Result<(), AppError> {
// Use data-modifying CTEs with RETURNING to ensure all CTEs execute
// Note: channel_id is actually scene_id in this system
sqlx::query(
r#"DELETE FROM realm.channel_members WHERE channel_id = $1 AND user_id = $2"#,
r#"
WITH member_info AS (
SELECT cm.position, cm.channel_id as scene_id, s.realm_id
FROM realm.channel_members cm
JOIN realm.scenes s ON cm.channel_id = s.id
WHERE cm.channel_id = $1 AND cm.user_id = $2
),
save_position AS (
UPDATE realm.memberships m
SET last_position = mi.position,
last_scene_id = mi.scene_id,
last_visited_at = now()
FROM member_info mi
WHERE m.realm_id = mi.realm_id AND m.user_id = $2
RETURNING m.user_id
),
do_delete AS (
DELETE FROM realm.channel_members
WHERE channel_id = $1 AND user_id = $2
RETURNING user_id
)
SELECT COUNT(*) FROM save_position, do_delete
"#,
)
.bind(channel_id)
.bind(user_id)