Skip to content

Commit

Permalink
Merge branch 'main' into group-groups
Browse files Browse the repository at this point in the history
  • Loading branch information
sipec committed Nov 14, 2024
2 parents f4d1d1b + d73d0d8 commit 4d4bea1
Show file tree
Hide file tree
Showing 55 changed files with 801 additions and 523 deletions.
2 changes: 1 addition & 1 deletion backend/api/src/get-feed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { getFollowedReposts, getTopicReposts } from 'shared/supabase/reposts'
import { FeedContract, GROUP_SCORE_PRIOR } from 'common/feed'

const DEBUG_USER_ID = undefined
const DEBUG_FEED = process.platform === 'darwin'
const DEBUG_FEED = false //process.platform === 'darwin'
export const getFeed: APIHandler<'get-feed'> = async (props) => {
const { limit, offset, ignoreContractIds } = props
const pg = createSupabaseDirectClient()
Expand Down
27 changes: 27 additions & 0 deletions backend/api/src/get-markets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { createSupabaseDirectClient } from 'shared/supabase/init'
import { convertAnswer, convertContract } from 'common/supabase/contracts'
import { contractColumnsToSelect } from 'shared/utils'
import { CPMMMultiContract } from 'common/contract'
import { APIHandler } from './helpers/endpoint'
import { sortBy } from 'lodash'

export const getMarketsByIds: APIHandler<'markets-by-ids'> = async (props) => {
const pg = createSupabaseDirectClient()
const results = await pg.multi(
`select ${contractColumnsToSelect} from contracts
where id in ($1:list);
select * from answers where contract_id in ($1:list);
`,
[props.ids]
)
const contracts = results[0].map(convertContract)
const answers = results[1].map(convertAnswer)
const multiContracts = contracts.filter((c) => c.mechanism === 'cpmm-multi-1')
for (const contract of multiContracts) {
;(contract as CPMMMultiContract).answers = sortBy(
answers.filter((a) => a.contractId === contract.id),
(a) => a.index
)
}
return contracts
}
61 changes: 43 additions & 18 deletions backend/api/src/reaction.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import { createSupabaseDirectClient } from 'shared/supabase/init'
import { APIError, APIHandler } from './helpers/endpoint'
import { createLikeNotification } from 'shared/create-notification'
import { assertUnreachable } from 'common/util/types'
import { createLikeNotification } from 'shared/create-notification'
import { createSupabaseDirectClient } from 'shared/supabase/init'
import { log } from 'shared/utils'
import { APIError, APIHandler } from './helpers/endpoint'

export const addOrRemoveReaction: APIHandler<'react'> = async (props, auth) => {
const { contentId, contentType, remove } = props
const { contentId, contentType, remove, reactionType = 'like' } = props
const userId = auth.uid

const pg = createSupabaseDirectClient()

if (remove) {
const deleteReaction = async (deleteReactionType: string) => {
await pg.none(
`delete from user_reactions
where user_id = $1 and content_id = $2 and content_type = $3`,
[userId, contentId, contentType]
where user_id = $1 and content_id = $2 and content_type = $3 and reaction_type = $4`,
[userId, contentId, contentType, deleteReactionType]
)
}

if (remove) {
await deleteReaction(reactionType)
} else {
// get the id of the person this content belongs to, to denormalize the owner
let ownerId: string
Expand Down Expand Up @@ -54,36 +58,57 @@ export const addOrRemoveReaction: APIHandler<'react'> = async (props, auth) => {
)

if (existingReactions.length > 0) {
log('Reaction already exists, do nothing')
return { result: { success: true }, continue: async () => {} }
const existingReactionType = existingReactions[0].reaction_type
// if it's the same reaction type, do nothing
if (existingReactionType === reactionType) {
return { result: { success: true }, continue: async () => {} }
} else {
// otherwise, remove the other reaction type
await deleteReaction(existingReactionType)
}
}

// actually do the insert
const reactionRow = await pg.one(
`insert into user_reactions
(content_id, content_type, content_owner_id, user_id)
values ($1, $2, $3, $4)
(content_id, content_type, content_owner_id, user_id, reaction_type)
values ($1, $2, $3, $4, $5)
returning *`,
[contentId, contentType, ownerId, userId]
[contentId, contentType, ownerId, userId, reactionType]
)

await createLikeNotification(reactionRow)
if (reactionType === 'like') {
await createLikeNotification(reactionRow)
}
}

return {
result: { success: true },
continue: async () => {
if (contentType === 'comment') {
const count = await pg.one(
const likeCount = await pg.one(
`select count(*) from user_reactions
where content_id = $1 and content_type = $2 and reaction_type = $3`,
[contentId, contentType, 'like'],
(r) => r.count
)
const dislikeCount = await pg.one(
`select count(*) from user_reactions
where content_id = $1 and content_type = $2`,
[contentId, contentType],
where content_id = $1 and content_type = $2 and reaction_type = $3`,
[contentId, contentType, 'dislike'],
(r) => r.count
)
log('new like count ' + count)

log('new like count ' + likeCount)
log('new dislike count ' + dislikeCount)

await pg.none(
`update contract_comments set likes = $1 where comment_id = $2`,
[count, contentId]
[likeCount, contentId]
)
await pg.none(
`update contract_comments set dislikes = $1 where comment_id = $2`,
[dislikeCount, contentId]
)
}
},
Expand Down
2 changes: 2 additions & 0 deletions backend/api/src/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ import { getLeaderboard } from './get-leaderboard'
import { toggleSystemTradingStatus } from './toggle-system-status'
import { completeCashoutRequest } from './gidx/complete-cashout-request'
import { getDailyChangedMetricsAndContracts } from './get-daily-changed-metrics-and-contracts'
import { getMarketsByIds } from './get-markets'
import { getTopicTopics } from './get-topic-topics'
import { getTopicDashboards } from './get-topic-dashboards'

Expand Down Expand Up @@ -174,6 +175,7 @@ export const handlers: { [k in APIPath]: APIHandler<k> } = {
groups: getGroups,
'market/:id': getMarket,
'market/:id/lite': ({ id }) => getMarket({ id, lite: true }),
'markets-by-ids': getMarketsByIds,
'slug/:slug': getMarket,
'market/:contractId/update': updateMarket,
'market/:contractId/close': closeMarket,
Expand Down
1 change: 1 addition & 0 deletions backend/api/url-map-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,4 @@ pathMatchers:
- pathTemplateMatch: /v0/leagues
- pathTemplateMatch: /v0/get-mod-reports
- pathTemplateMatch: /get-notifications
- pathTemplateMatch: /markets-by-ids
1 change: 0 additions & 1 deletion backend/scheduler/src/jobs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { createJob } from './helpers'
import { updateContractMetricsCore } from 'shared/update-contract-metrics-core'
import { sendOnboardingNotificationsInternal } from 'shared/onboarding-helpers'
import { updateUserMetricPeriods } from 'shared/update-user-metric-periods'
import { updateGroupMetricsCore } from 'shared/update-group-metrics-core'
import { cleanOldNotifications } from './clean-old-notifications'
import { updateStatsCore } from './update-stats'
import { calculateConversionScore } from 'shared/conversion-score'
Expand Down
2 changes: 1 addition & 1 deletion backend/shared/src/supabase/contract-comments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export async function getCommentsDirect(
const { userId, contractId, limit = 5000, page = 0 } = filters
return await pg.map(
`
select cc.data, likes from contract_comments cc
select cc.data, likes, dislikes from contract_comments cc
join contracts on cc.contract_id = contracts.id
where contracts.visibility = 'public'
and ($3 is null or contract_id = $3)
Expand Down
1 change: 1 addition & 0 deletions backend/shared/src/supabase/search-contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ function getSearchContractWhereSQL(args: {
// Include an extra day to capture markets that close on the first of the month. Add 7 hours to shift UTC time zone to PT.
'closing-this-month': `close_time > now() AND close_time < (date_trunc('month', now()) + interval '1 month' + interval '1 day' + interval '7 hours') AND resolution_time IS NULL`,
'closing-next-month': `close_time > ((date_trunc('month', now()) + interval '1 month') + interval '1 day' + interval '7 hours') AND close_time < (date_trunc('month', now()) + interval '2 month' + interval '1 day' + interval '7 hours') AND resolution_time IS NULL`,
closing: `close_time > now() AND close_time < now() + interval '1 month' + interval '1 day' + interval '7 hours' AND resolution_time IS NULL`,
resolved: 'resolution_time IS NOT NULL',
all: '',
}
Expand Down
2 changes: 2 additions & 0 deletions backend/shared/src/update-contract-metrics-core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,13 +244,15 @@ const getBetProbsAt = async (
from contract_bets
where created_time < millis_to_ts($1)
and contract_id = any($2)
and not is_redemption
order by contract_id, answer_id, created_time desc
), probs_after as (
select distinct on (contract_id, answer_id)
contract_id, answer_id, prob_before as prob
from contract_bets
where created_time >= millis_to_ts($1)
and contract_id = any($2)
and not is_redemption
order by contract_id, answer_id, created_time
)
select
Expand Down
12 changes: 6 additions & 6 deletions backend/supabase/contract_bets.sql
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,6 @@ $function$;
alter table contract_bets enable row level security;

-- Indexes
drop index if exists contract_bets_bet_id_key;

create unique index contract_bets_bet_id_key on public.contract_bets using btree (bet_id);

drop index if exists contract_bets_contract_limit_orders;

create index contract_bets_contract_limit_orders on public.contract_bets using btree (
Expand All @@ -97,9 +93,13 @@ drop index if exists contract_bets_created_time_only;

create index contract_bets_created_time_only on public.contract_bets using btree (created_time desc);

drop index if exists contract_bets_historical_probs;
drop index if exists contract_bets_historical_probs_non_redemption;

create index concurrently contract_bets_historical_probs_non_redemption on public.contract_bets using btree
(contract_id, answer_id, created_time desc)
include (prob_before, prob_after)
where not is_redemption;

create index contract_bets_historical_probs on public.contract_bets using btree (contract_id, answer_id, created_time desc) include (prob_before, prob_after);

drop index if exists contract_bets_pkey;

Expand Down
6 changes: 4 additions & 2 deletions backend/supabase/user_contract_metrics.sql
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ BEGIN
-- Update the row where answer_id is null with the aggregated metrics
UPDATE user_contract_metrics
SET
data = data || jsonb_build_object('hasYesShares', sum_has_yes_shares, 'hasNoShares', sum_has_no_shares, 'hasShares', sum_has_shares),
has_yes_shares = sum_has_yes_shares,
has_no_shares = sum_has_no_shares,
has_shares = sum_has_shares
Expand Down Expand Up @@ -80,9 +81,10 @@ drop index if exists contract_metrics_answer_id;

create index contract_metrics_answer_id on public.user_contract_metrics using btree (contract_id, answer_id);

drop index if exists idx_user_contract_metrics_contract_profit;
drop index if exists user_contract_metrics_contract_profit_null;

create index idx_user_contract_metrics_contract_profit on public.user_contract_metrics using btree (contract_id, profit);
create index concurrently user_contract_metrics_contract_profit_null on public.user_contract_metrics using btree (contract_id, profit)
where answer_id is null;

drop index if exists unique_user_contract_answer;

Expand Down
1 change: 1 addition & 0 deletions common/src/api/market-search-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const searchProps = z
z.literal('open'),
z.literal('closing-this-month'),
z.literal('closing-next-month'),
z.literal('closing'),
z.literal('closed'),
z.literal('resolved'),
z.literal('all'),
Expand Down
12 changes: 12 additions & 0 deletions common/src/api/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,17 @@ export const API = (_apiTypeCheck = {
cache: DEFAULT_CACHE_STRATEGY,
props: z.object({ id: z.string(), lite: coerceBoolean.optional() }),
},
'markets-by-ids': {
method: 'GET',
visibility: 'undocumented',
authed: false,
returns: [] as Contract[],
props: z
.object({
ids: z.array(z.string()).max(200),
})
.strict(),
},
// deprecated. use /market/:id?lite=true instead
'market/:id/lite': {
method: 'GET',
Expand Down Expand Up @@ -1056,6 +1067,7 @@ export const API = (_apiTypeCheck = {
contentId: z.string(),
contentType: z.enum(['comment', 'contract']),
remove: z.boolean().optional(),
reactionType: z.enum(['like', 'dislike']).optional().default('like'),
})
.strict(),
returns: { success: true },
Expand Down
19 changes: 17 additions & 2 deletions common/src/api/websocket-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const VERBOSE_LOGGING = false

// mqp: no way should our server ever take 5 seconds to reply
const TIMEOUT_MS = 5000

const HEARTBEAT_MS = 10_000
const RECONNECT_WAIT_MS = 5000

type ConnectingState = typeof WebSocket.CONNECTING
Expand Down Expand Up @@ -69,6 +69,10 @@ export class APIRealtimeClient {
}

close() {
if (this.heartbeat) {
clearInterval(this.heartbeat)
this.heartbeat = undefined
}
this.ws.close(1000, 'Closed manually.')
clearTimeout(this.connectTimeout)
}
Expand All @@ -90,9 +94,12 @@ export class APIRealtimeClient {
if (VERBOSE_LOGGING) {
console.info('API websocket opened.')
}
if (this.heartbeat) {
clearInterval(this.heartbeat)
}
this.heartbeat = setInterval(
async () => this.sendMessage('ping', {}).catch(console.error),
30000
HEARTBEAT_MS
)
if (this.subscriptions.size > 0) {
this.sendMessage('subscribe', {
Expand Down Expand Up @@ -188,6 +195,14 @@ export class APIRealtimeClient {
}, TIMEOUT_MS)
this.txns.set(txid, { resolve, reject, timeout })
this.ws.send(JSON.stringify({ type, txid, ...data }))
}).catch((error) => {
// If this is a heartbeat message that failed, trigger reconnection
if (type === 'ping') {
console.error('Heartbeat failed, attempting to reconnect:', error)
this.ws.close()
this.waitAndReconnect()
}
throw error // Re-throw the error for other message types
})
} else {
// expected if components in the code try to subscribe or unsubscribe
Expand Down
23 changes: 17 additions & 6 deletions common/src/calculate-metrics.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { Dictionary, groupBy, min, orderBy, sum, sumBy, uniq } from 'lodash'
import {
cloneDeep,
Dictionary,
groupBy,
min,
orderBy,
sum,
sumBy,
uniq,
} from 'lodash'
import {
calculatePayout,
calculateTotalSpentAndShares,
Expand Down Expand Up @@ -415,11 +424,13 @@ export const calculateAnswerMetricsWithNewBetsOnly = (

return Object.entries(betsByUser).flatMap(([userId, bets]) => {
// If it's a multi market, we need to summarize the stats for the null answer
const oldSummary = userMetrics.find(
(m) =>
m.answerId === null &&
m.userId === userId &&
m.contractId === contractId
const oldSummary = cloneDeep(
userMetrics.find(
(m) =>
m.answerId === null &&
m.userId === userId &&
m.contractId === contractId
)
)
const userBetsByAnswer = groupBy(bets, 'answerId')
const newMetrics = Object.entries(userBetsByAnswer).map(
Expand Down
2 changes: 1 addition & 1 deletion common/src/comment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export type Comment<T extends AnyCommentType = AnyCommentType> = {
userAvatarUrl?: string
/** @deprecated Not actually deprecated, only in supabase column, and not in data column */
likes?: number

dislikes?: number
hidden?: boolean
hiddenTime?: number
hiderId?: string
Expand Down
2 changes: 2 additions & 0 deletions common/src/reaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ export type Reaction = Row<'user_reactions'>

export type ReactionContentTypes = 'contract' | 'comment'

export type ReactionType = 'like' | 'dislike'

// export type ReactionTypes = 'like'
Loading

0 comments on commit 4d4bea1

Please sign in to comment.