diff --git a/backend/api/src/app.ts b/backend/api/src/app.ts index 27ce7cb9..8cf3e851 100644 --- a/backend/api/src/app.ts +++ b/backend/api/src/app.ts @@ -50,6 +50,7 @@ import {leavePrivateUserMessageChannel} from './leave-private-user-message-chann import {updatePrivateUserMessageChannel} from './update-private-user-message-channel' import {getNotifications} from './get-notifications' import {updateNotifSettings} from './update-notif-setting' +import {setLastOnline} from './set-last-online' import swaggerUi from "swagger-ui-express" import * as fs from "fs" import {sendSearchNotifications} from "api/send-search-notifications"; @@ -167,6 +168,7 @@ const handlers: { [k in APIPath]: APIHandler } = { 'get-channel-seen-time': getLastSeenChannelTime, 'set-channel-seen-time': setChannelLastSeenTime, 'get-messages-count': getMessagesCount, + 'set-last-online': setLastOnline, } Object.entries(handlers).forEach(([path, handler]) => { diff --git a/backend/api/src/get-profiles.ts b/backend/api/src/get-profiles.ts index e483ebea..70ceb932 100644 --- a/backend/api/src/get-profiles.ts +++ b/backend/api/src/get-profiles.ts @@ -1,7 +1,7 @@ import {type APIHandler} from 'api/helpers/endpoint' import {convertRow} from 'shared/love/supabase' import {createSupabaseDirectClient} from 'shared/supabase/init' -import {from, join, limit, orderBy, renderSql, select, where,} from 'shared/supabase/sql-builder' +import {from, join, leftJoin, limit, orderBy, renderSql, select, where,} from 'shared/supabase/sql-builder' import {getCompatibleProfiles} from 'api/compatible-profiles' import {intersection} from 'lodash' import {MAX_INT, MIN_BIO_LENGTH, MIN_INT} from "common/constants"; @@ -98,9 +98,10 @@ export const loadProfiles = async (props: profileQueryType) => { } const query = renderSql( - select('profiles.*, name, username, users.data as user'), + select('profiles.*, name, username, users.data as user, user_activity.last_online_time'), from('profiles'), join('users on users.id = profiles.user_id'), + leftJoin('user_activity on user_activity.user_id = profiles.user_id'), where('looking_for_matches = true'), // where(`pinned_url is not null and pinned_url != ''`), where( diff --git a/backend/api/src/set-last-online.ts b/backend/api/src/set-last-online.ts new file mode 100644 index 00000000..5e06072a --- /dev/null +++ b/backend/api/src/set-last-online.ts @@ -0,0 +1,22 @@ +import {APIHandler} from './helpers/endpoint' +import {createSupabaseDirectClient} from 'shared/supabase/init' + +export const setLastOnline: APIHandler<'set-last-online'> = async ( + _, + auth +) => { + if (!auth || !auth.uid) return + + const pg = createSupabaseDirectClient() + const result = await pg.none(` + INSERT INTO user_activity (user_id, last_online_time) + VALUES ($1, now()) + ON CONFLICT (user_id) + DO UPDATE + SET last_online_time = EXCLUDED.last_online_time + WHERE user_activity.last_online_time < now() - interval '1 minute'; + `, + [auth.uid] + ) + console.log('setLastOnline', result) +} diff --git a/backend/supabase/profiles.sql b/backend/supabase/profiles.sql index 48fe2720..5acf15db 100644 --- a/backend/supabase/profiles.sql +++ b/backend/supabase/profiles.sql @@ -28,7 +28,6 @@ CREATE TABLE IF NOT EXISTS profiles ( id BIGINT GENERATED ALWAYS AS IDENTITY NOT NULL, is_smoker BOOLEAN, is_vegetarian_or_vegan BOOLEAN, - last_online_time TIMESTAMPTZ DEFAULT now() NOT NULL, last_modification_time TIMESTAMPTZ DEFAULT now() NOT NULL, looking_for_matches BOOLEAN DEFAULT TRUE NOT NULL, messaging_status TEXT DEFAULT 'open'::TEXT NOT NULL, @@ -89,17 +88,11 @@ CREATE OR REPLACE FUNCTION update_last_modification_time() RETURNS TRIGGER AS $$ BEGIN - IF NEW.last_online_time IS DISTINCT FROM OLD.last_online_time AND row(NEW.*) = row(OLD.*) THEN - -- Only last_online_time changed, do nothing - RETURN NEW; -END IF; - - -- Some other column changed - NEW.last_modification_time = now(); -RETURN NEW; + NEW.last_modification_time = now(); + RETURN NEW; END; -$$ -LANGUAGE plpgsql; +$$ LANGUAGE plpgsql; + CREATE TRIGGER trigger_update_last_mod_time BEFORE UPDATE @@ -141,5 +134,3 @@ $$ LANGUAGE plpgsql; CREATE TRIGGER trg_update_bio_text BEFORE INSERT OR UPDATE OF bio ON profiles FOR EACH ROW EXECUTE FUNCTION update_bio_text(); - - diff --git a/common/src/api/schema.ts b/common/src/api/schema.ts index 77b61c62..61577c6b 100644 --- a/common/src/api/schema.ts +++ b/common/src/api/schema.ts @@ -405,6 +405,11 @@ export const API = (_apiTypeCheck = { channelId: z.coerce.number(), }), }, + 'set-last-online': { + method: 'POST', + authed: true, + props: z.object({}), + }, 'get-notifications': { method: 'GET', authed: true, diff --git a/web/hooks/use-online.ts b/web/hooks/use-online.ts index fe0be713..51d35464 100644 --- a/web/hooks/use-online.ts +++ b/web/hooks/use-online.ts @@ -1,18 +1,23 @@ -import { useEffect } from 'react' -import { useProfile } from 'web/hooks/use-profile' -import { useIsAuthorized } from 'web/hooks/use-user' -import { run } from 'common/supabase/utils' -import { db } from 'web/lib/supabase/db' +import {useEffect} from 'react' +import {useProfile} from 'web/hooks/use-profile' +import {useIsAuthorized} from 'web/hooks/use-user' +import {api} from 'web/lib/api' + export const useOnline = () => { const profile = useProfile() const isAuthed = useIsAuthorized() useEffect(() => { if (!profile || !isAuthed) return - run( - db - .from('profiles') - .update({ last_online_time: new Date().toISOString() }) - .eq('id', profile.id) - ) - }, []) + void (async () => { + const date = new Date().toISOString() + // const result = await run( + // db + // .from('profiles') + // .update({ last_online_time: date }) + // .eq('id', profile.id) + // ) + api('set-last-online') + console.log('set last online time for', profile.id, date) + })() + }, [profile?.id, isAuthed]) }