diff --git a/backend/api/src/app.ts b/backend/api/src/app.ts index 695aacf1..27ce7cb9 100644 --- a/backend/api/src/app.ts +++ b/backend/api/src/app.ts @@ -54,6 +54,7 @@ import swaggerUi from "swagger-ui-express" import * as fs from "fs" import {sendSearchNotifications} from "api/send-search-notifications"; import {sendDiscordMessage} from "common/discord/core"; +import {getMessagesCount} from "api/get-messages-count"; const allowCorsUnrestricted: RequestHandler = cors({}) @@ -165,6 +166,7 @@ const handlers: { [k in APIPath]: APIHandler } = { 'get-channel-messages': getChannelMessages, 'get-channel-seen-time': getLastSeenChannelTime, 'set-channel-seen-time': setChannelLastSeenTime, + 'get-messages-count': getMessagesCount, } Object.entries(handlers).forEach(([path, handler]) => { diff --git a/backend/api/src/get-messages-count.ts b/backend/api/src/get-messages-count.ts new file mode 100644 index 00000000..ff3ea806 --- /dev/null +++ b/backend/api/src/get-messages-count.ts @@ -0,0 +1,18 @@ +import {APIHandler} from './helpers/endpoint' +import {createSupabaseDirectClient} from "shared/supabase/init"; + +export const getMessagesCount: APIHandler<'get-messages-count'> = async (_, auth) => { + const pg = createSupabaseDirectClient() + const result = await pg.one( + ` + SELECT COUNT(*) AS count + FROM private_user_messages; + `, + [] + ); + const count = Number(result.count); + console.debug('private_user_messages count:', count); + return { + count: count, + } +} diff --git a/common/src/api/schema.ts b/common/src/api/schema.ts index c8dd03be..77b61c62 100644 --- a/common/src/api/schema.ts +++ b/common/src/api/schema.ts @@ -4,19 +4,19 @@ import { baseProfilesSchema, arraybeSchema, } from 'common/api/zod-types' -import { PrivateChatMessage } from 'common/chat-message' -import { CompatibilityScore } from 'common/love/compatibility-score' -import { MAX_COMPATIBILITY_QUESTION_LENGTH } from 'common/love/constants' -import { Profile, ProfileRow } from 'common/love/profile' -import { Row } from 'common/supabase/utils' -import { PrivateUser, User } from 'common/user' -import { z } from 'zod' -import { LikeData, ShipData } from './love-types' -import { DisplayUser, FullUser } from './user-types' -import { PrivateMessageChannel } from 'common/supabase/private-messages' -import { Notification } from 'common/notifications' -import { arrify } from 'common/util/array' -import { notification_preference } from 'common/user-notification-preferences' +import {PrivateChatMessage} from 'common/chat-message' +import {CompatibilityScore} from 'common/love/compatibility-score' +import {MAX_COMPATIBILITY_QUESTION_LENGTH} from 'common/love/constants' +import {Profile, ProfileRow} from 'common/love/profile' +import {Row} from 'common/supabase/utils' +import {PrivateUser, User} from 'common/user' +import {z} from 'zod' +import {LikeData, ShipData} from './love-types' +import {DisplayUser, FullUser} from './user-types' +import {PrivateMessageChannel} from 'common/supabase/private-messages' +import {Notification} from 'common/notifications' +import {arrify} from 'common/util/array' +import {notification_preference} from 'common/user-notification-preferences' // mqp: very unscientific, just balancing our willingness to accept load // with user willingness to put up with stale data @@ -59,12 +59,12 @@ export const API = (_apiTypeCheck = { 'user/by-id/:id/block': { method: 'POST', authed: true, - props: z.object({ id: z.string() }).strict(), + props: z.object({id: z.string()}).strict(), }, 'user/by-id/:id/unblock': { method: 'POST', authed: true, - props: z.object({ id: z.string() }).strict(), + props: z.object({id: z.string()}).strict(), }, 'ban-user': { method: 'POST', @@ -176,28 +176,28 @@ export const API = (_apiTypeCheck = { authed: false, cache: DEFAULT_CACHE_STRATEGY, returns: {} as FullUser, - props: z.object({ username: z.string() }).strict(), + props: z.object({username: z.string()}).strict(), }, 'user/:username/lite': { method: 'GET', authed: false, cache: DEFAULT_CACHE_STRATEGY, returns: {} as DisplayUser, - props: z.object({ username: z.string() }).strict(), + props: z.object({username: z.string()}).strict(), }, 'user/by-id/:id': { method: 'GET', authed: false, cache: DEFAULT_CACHE_STRATEGY, returns: {} as FullUser, - props: z.object({ id: z.string() }).strict(), + props: z.object({id: z.string()}).strict(), }, 'user/by-id/:id/lite': { method: 'GET', authed: false, cache: DEFAULT_CACHE_STRATEGY, returns: {} as DisplayUser, - props: z.object({ id: z.string() }).strict(), + props: z.object({id: z.string()}).strict(), }, 'search-users': { method: 'GET', @@ -215,7 +215,7 @@ export const API = (_apiTypeCheck = { 'compatible-profiles': { method: 'GET', authed: false, - props: z.object({ userId: z.string() }), + props: z.object({userId: z.string()}), returns: {} as { profile: Profile compatibleProfiles: Profile[] @@ -227,7 +227,7 @@ export const API = (_apiTypeCheck = { 'remove-pinned-photo': { method: 'POST', authed: true, - returns: { success: true }, + returns: {success: true}, props: z .object({ userId: z.string(), @@ -338,7 +338,7 @@ export const API = (_apiTypeCheck = { 'get-profile-answers': { method: 'GET', authed: false, - props: z.object({ userId: z.string() }).strict(), + props: z.object({userId: z.string()}).strict(), returns: {} as { status: 'success' answers: Row<'love_compatibility_answers'>[] @@ -477,6 +477,12 @@ export const API = (_apiTypeCheck = { radius: z.number().min(1).max(500), }), }, + 'get-messages-count': { + method: 'GET', + authed: false, + props: z.object({}), + returns: {} as { count: number }, + }, } as const) export type APIPath = keyof typeof API @@ -488,8 +494,8 @@ export type ValidatedAPIParams = z.output< > export type APIResponse = APISchema extends { - returns: Record -} + returns: Record + } ? APISchema['returns'] : void diff --git a/web/components/widgets/card.tsx b/web/components/widgets/card.tsx index 36c2ab59..9578d123 100644 --- a/web/components/widgets/card.tsx +++ b/web/components/widgets/card.tsx @@ -9,7 +9,7 @@ export const Card = forwardRef(function Card( return (
>({}) + + useEffect(() => { + async function load() { + const tables = [ + 'profiles', + 'bookmarked_searches', + 'private_user_message_channels', + 'private_user_messages', + 'profile_comments', + 'love_compatibility_answers', + ] as const + + const settled = await Promise.allSettled( + tables.map((t) => getCount(t)) + ) + + const result: Record = {} + settled.forEach((res, i) => { + const key = tables[i] + if (res.status === 'fulfilled') result[key] = res.value + else result[key] = null + }) + + setData(result) + } + + void load() + }, []) + return ( - +

Community Growth over Time

- + + + + {!!data.profiles && } + {!!data.bookmarked_searches && } + {!!data.private_user_message_channels && } + {!!data.private_user_messages && } + {!!data.profile_comments && } + {!!data.love_compatibility_answers && } + +
); }