diff --git a/libs/model/src/community/GetTopics.query.ts b/libs/model/src/community/GetTopics.query.ts new file mode 100644 index 00000000000..324e2302c16 --- /dev/null +++ b/libs/model/src/community/GetTopics.query.ts @@ -0,0 +1,107 @@ +import { Query } from '@hicommonwealth/core'; +import * as schemas from '@hicommonwealth/schemas'; +import { QueryTypes } from 'sequelize'; +import { z } from 'zod'; +import { models } from '../database'; + +export function GetTopics(): Query { + return { + ...schemas.GetTopics, + auth: [], + secure: false, + body: async ({ payload }) => { + const { community_id, with_contest_managers } = payload; + + const contest_managers = with_contest_managers + ? ` +SELECT + td.*, + coalesce(( + SELECT + jsonb_agg(jsonb_set(to_jsonb (cm), -- Convert the contest manager (cm) row to JSONB + '{content}', -- Set the 'content' key in the resulting JSONB + coalesce(( + SELECT + jsonb_agg(ca) -- Aggregates the filtered actions into content + FROM "ContestActions" ca + WHERE + ca.contest_address = cm.contest_address + AND ca.action = 'added' + AND ca.created_at > co.start_time + AND ca.created_at < co.end_time), '[]'::jsonb) -- Use an empty array as fallback if no actions are found +)) + FROM "ContestTopics" ct + LEFT JOIN "ContestManagers" cm ON cm.contest_address = ct.contest_address + JOIN ( + -- Subquery to get the max contest_id, start_time, and end_time for each contest address + SELECT + contest_address, + max(contest_id) AS max_contest_id, + max(start_time) AS start_time, + max(end_time) AS end_time + FROM + "Contests" + GROUP BY + contest_address) co ON cm.contest_address = co.contest_address + WHERE + ct.topic_id = td.id + AND cm.community_id = :community_id + AND COALESCE(cm.cancelled, FALSE) = FALSE -- Exclude cancelled managers + AND (cm.interval = 0 + AND now() < co.end_time -- Check if the interval is 0 and the contest is ongoing + OR cm.interval > 0 -- Or if there is a valid interval +)), '[]'::jsonb) AS active_contest_managers +FROM + topic_data td +` + : `SELECT *, '[]'::json as active_contest_managers FROM topic_data`; + + const sql = ` +WITH topic_data AS ( + SELECT + id, + name, + community_id, + description, + telegram, + featured_in_sidebar, + featured_in_new_post, + default_offchain_template, + "order", + channel_id, + group_ids, + weighted_voting, + token_symbol, + vote_weight_multiplier, + created_at::text AS created_at, + updated_at::text AS updated_at, + deleted_at::text AS deleted_at, + ( + SELECT + count(*)::int + FROM + "Threads" + WHERE + community_id = :community_id + AND topic_id = t.id + AND deleted_at IS NULL) AS total_threads + FROM + "Topics" t + WHERE + t.community_id = :community_id + AND t.deleted_at IS NULL +) + ${contest_managers} + `; + + return await models.sequelize.query>( + sql, + { + replacements: { community_id }, + type: QueryTypes.SELECT, + raw: true, + }, + ); + }, + }; +} diff --git a/libs/model/src/community/index.ts b/libs/model/src/community/index.ts index 18434198e8a..de9babced7d 100644 --- a/libs/model/src/community/index.ts +++ b/libs/model/src/community/index.ts @@ -13,6 +13,7 @@ export * from './GetCommunityStake.query'; export * from './GetMembers.query'; export * from './GetStakeHistoricalPrice.query'; export * from './GetStakeTransaction.query'; +export * from './GetTopics.query'; export * from './JoinCommunity.command'; export * from './RefreshCommunityMemberships.command'; export * from './RefreshCustomDomain.query'; diff --git a/libs/model/src/thread/CreateThread.command.ts b/libs/model/src/thread/CreateThread.command.ts index 533da1207b5..3a2294a3989 100644 --- a/libs/model/src/thread/CreateThread.command.ts +++ b/libs/model/src/thread/CreateThread.command.ts @@ -89,7 +89,7 @@ export function CreateThread(): Command< ...schemas.CreateThread, auth: [ isAuthorized({ - action: schemas.PermissionEnum.CREATE_THREAD + action: schemas.PermissionEnum.CREATE_THREAD, }), verifyThreadSignature, ], diff --git a/libs/model/test/community/community-lifecycle.spec.ts b/libs/model/test/community/community-lifecycle.spec.ts index ebb287f4d99..674b2c15964 100644 --- a/libs/model/test/community/community-lifecycle.spec.ts +++ b/libs/model/test/community/community-lifecycle.spec.ts @@ -24,6 +24,7 @@ import { DeleteTopic, GetCommunities, GetMembers, + GetTopics, JoinCommunity, JoinCommunityErrors, MAX_GROUPS_PER_COMMUNITY, @@ -580,6 +581,14 @@ describe('Community lifecycle', () => { }), ).rejects.toThrow(InvalidActor); }); + + test('should get topics', async () => { + const topics = await query(GetTopics(), { + actor: superAdminActor, + payload: { community_id: community.id, with_contest_managers: false }, + }); + expect(topics?.length).toBe(4); + }); }); describe('updates', () => { diff --git a/libs/schemas/src/queries/community.schemas.ts b/libs/schemas/src/queries/community.schemas.ts index 9f738029a99..485bfb88828 100644 --- a/libs/schemas/src/queries/community.schemas.ts +++ b/libs/schemas/src/queries/community.schemas.ts @@ -9,8 +9,11 @@ import { Community, CommunityMember, CommunityStake, + ContestManager, ExtendedCommunity, + Topic, } from '../entities'; +import * as projections from '../projections'; import { PG_INT } from '../utils'; import { PaginatedResultSchema, PaginationParamsSchema } from './pagination'; @@ -159,3 +162,31 @@ export const GetStakeHistoricalPrice = { }) .array(), }; + +export const ConstestManagerView = ContestManager.extend({ + created_at: z.string(), + topics: z.undefined(), + contests: z.undefined(), + content: z.array( + projections.ContestAction.extend({ + created_at: z.string(), + }), + ), +}); + +export const TopicView = Topic.extend({ + created_at: z.string(), + updated_at: z.string().nullish(), + deleted_at: z.string().nullish(), + contest_topics: z.undefined(), + total_threads: z.number(), + active_contest_managers: z.array(ConstestManagerView), +}); + +export const GetTopics = { + input: z.object({ + community_id: z.string(), + with_contest_managers: z.boolean().optional(), + }), + output: z.array(TopicView), +}; diff --git a/libs/schemas/src/queries/thread.schemas.ts b/libs/schemas/src/queries/thread.schemas.ts index c67d3defff4..05576d58451 100644 --- a/libs/schemas/src/queries/thread.schemas.ts +++ b/libs/schemas/src/queries/thread.schemas.ts @@ -2,9 +2,9 @@ import { z } from 'zod'; import { Thread } from '../entities'; import { DiscordMetaSchema, + PG_INT, linksSchema, paginationSchema, - PG_INT, } from '../utils'; export const OrderByQueriesKeys = z.enum([ diff --git a/packages/commonwealth/client/scripts/models/AbridgedThread.ts b/packages/commonwealth/client/scripts/models/AbridgedThread.ts index b03ee447df9..a3a3c04939d 100644 --- a/packages/commonwealth/client/scripts/models/AbridgedThread.ts +++ b/packages/commonwealth/client/scripts/models/AbridgedThread.ts @@ -1,6 +1,6 @@ import { ProposalType } from '@hicommonwealth/shared'; import type moment from 'moment'; -import type Topic from './Topic'; +import type { Topic } from './Topic'; import type { IUniqueId } from './interfaces'; class AbridgedThread implements IUniqueId { diff --git a/packages/commonwealth/client/scripts/models/Thread.ts b/packages/commonwealth/client/scripts/models/Thread.ts index fb75903fbc2..1a02c6f3028 100644 --- a/packages/commonwealth/client/scripts/models/Thread.ts +++ b/packages/commonwealth/client/scripts/models/Thread.ts @@ -10,7 +10,7 @@ import moment, { Moment } from 'moment'; import { z } from 'zod'; import Comment from './Comment'; import type { ReactionType } from './Reaction'; -import Topic from './Topic'; +import type { Topic } from './Topic'; import type { IUniqueId } from './interfaces'; import type { ThreadKind, ThreadStage } from './types'; @@ -375,8 +375,7 @@ export class Thread implements IUniqueId { this.identifier = `${id}`; this.createdAt = moment(created_at); this.updatedAt = moment(updated_at); - // @ts-expect-error StrictNullChecks - this.topic = topic?.id ? new Topic({ ...(topic || {}) } as any) : null; + this.topic = { ...topic }; this.kind = kind; this.stage = stage; this.authorCommunity = Address?.community_id; diff --git a/packages/commonwealth/client/scripts/models/Topic.ts b/packages/commonwealth/client/scripts/models/Topic.ts index 44c349014d1..15dc3022fd3 100644 --- a/packages/commonwealth/client/scripts/models/Topic.ts +++ b/packages/commonwealth/client/scripts/models/Topic.ts @@ -1,79 +1,4 @@ import * as schemas from '@hicommonwealth/schemas'; import { z } from 'zod'; -const ActiveContestManagers = z.object({ - content: z.array(schemas.ContestAction), - contest_manager: schemas.ContestManager, -}); - -type TopicAttributesBase = z.infer; - -type TopicAttributesExtended = TopicAttributesBase & { - active_contest_managers: Array>; - total_threads: number; -}; - -export type TopicAttributes = TopicAttributesExtended; - -class Topic { - public readonly name: TopicAttributes['name']; - public readonly id: TopicAttributes['id']; - public readonly description: TopicAttributes['description']; - public readonly telegram: TopicAttributes['telegram']; - public readonly communityId: TopicAttributes['community_id']; - public readonly channelId: TopicAttributes['channel_id']; - public readonly featuredInSidebar: TopicAttributes['featured_in_sidebar']; - public readonly featuredInNewPost: TopicAttributes['featured_in_new_post']; - public order: TopicAttributes['order']; - public readonly defaultOffchainTemplate: TopicAttributes['default_offchain_template']; - public totalThreads: TopicAttributes['total_threads']; - public readonly activeContestManagers: TopicAttributes['active_contest_managers']; - public readonly groupIds: TopicAttributes['group_ids']; - public readonly defaultOffchainTemplateBackup: TopicAttributes['default_offchain_template_backup']; - public readonly weightedVoting: TopicAttributes['weighted_voting']; - public readonly tokenAddress: TopicAttributes['token_address']; - public readonly tokenSymbol: TopicAttributes['token_symbol']; - public readonly voteWeightMultiplier: TopicAttributes['vote_weight_multiplier']; - - constructor({ - name, - id, - description, - telegram, - community_id, - featured_in_sidebar, - featured_in_new_post, - order, - default_offchain_template, - total_threads, - channel_id, - active_contest_managers, - group_ids, - default_offchain_template_backup, - weighted_voting, - token_address, - token_symbol, - vote_weight_multiplier, - }: TopicAttributes) { - this.name = name; - this.id = id; - this.description = description; - this.telegram = telegram; - this.communityId = community_id; - this.featuredInSidebar = featured_in_sidebar; - this.featuredInNewPost = featured_in_new_post; - this.order = order; - this.defaultOffchainTemplate = default_offchain_template; - this.totalThreads = total_threads || 0; - this.channelId = channel_id; - this.activeContestManagers = active_contest_managers || []; - this.groupIds = group_ids; - this.defaultOffchainTemplateBackup = default_offchain_template_backup; - this.weightedVoting = weighted_voting; - this.tokenAddress = token_address; - this.tokenSymbol = token_symbol; - this.voteWeightMultiplier = vote_weight_multiplier; - } -} - -export default Topic; +export type Topic = z.infer; diff --git a/packages/commonwealth/client/scripts/state/api/config.ts b/packages/commonwealth/client/scripts/state/api/config.ts index 5a022eabb66..54b4c56dc9e 100644 --- a/packages/commonwealth/client/scripts/state/api/config.ts +++ b/packages/commonwealth/client/scripts/state/api/config.ts @@ -12,7 +12,6 @@ export const queryClient = new QueryClient({ export const ApiEndpoints = { // endpoint builder functions like getEndpoint(id) => /endpoint/:id should have camel cased keys // stand alone endpoints should be have upper snake case keys so we can easily tell them apart in code - BULK_TOPICS: '/topics', FETCH_ADMIN: '/roles', FETCH_COMMUNITY_STAKES: '/communityStakes', FETCH_COMMENTS: '/viewComments', diff --git a/packages/commonwealth/client/scripts/state/api/feeds/util.ts b/packages/commonwealth/client/scripts/state/api/feeds/util.ts index f7a8eea5e67..7328fe1c9ba 100644 --- a/packages/commonwealth/client/scripts/state/api/feeds/util.ts +++ b/packages/commonwealth/client/scripts/state/api/feeds/util.ts @@ -1,6 +1,6 @@ import { AxiosResponse } from 'axios'; import Thread from 'models/Thread'; -import Topic from 'models/Topic'; +import type { Topic } from 'models/Topic'; import { ThreadKind, ThreadStage } from 'models/types'; type ActivityResponse = { diff --git a/packages/commonwealth/client/scripts/state/api/threads/createThread.ts b/packages/commonwealth/client/scripts/state/api/threads/createThread.ts index a6729e694c0..a69dd3d847c 100644 --- a/packages/commonwealth/client/scripts/state/api/threads/createThread.ts +++ b/packages/commonwealth/client/scripts/state/api/threads/createThread.ts @@ -1,6 +1,6 @@ import { toCanvasSignedDataApiArgs } from '@hicommonwealth/shared'; import { signThread } from 'controllers/server/sessions'; -import Topic from 'models/Topic'; +import type { Topic } from 'models/Topic'; import { ThreadStage } from 'models/types'; import useUserOnboardingSliderMutationStore from 'state/ui/userTrainingCards'; import { trpc } from 'utils/trpcClient'; diff --git a/packages/commonwealth/client/scripts/state/api/threads/helpers/cache.ts b/packages/commonwealth/client/scripts/state/api/threads/helpers/cache.ts index 41499e8eb6c..457a3a67031 100644 --- a/packages/commonwealth/client/scripts/state/api/threads/helpers/cache.ts +++ b/packages/commonwealth/client/scripts/state/api/threads/helpers/cache.ts @@ -1,5 +1,5 @@ import Thread from 'models/Thread'; -import Topic from 'models/Topic'; +import type { Topic } from 'models/Topic'; import { ApiEndpoints, queryClient } from 'state/api/config'; /** diff --git a/packages/commonwealth/client/scripts/state/api/topics/createTopic.ts b/packages/commonwealth/client/scripts/state/api/topics/createTopic.ts index 67fb5eb06a9..967f9cf8bdc 100644 --- a/packages/commonwealth/client/scripts/state/api/topics/createTopic.ts +++ b/packages/commonwealth/client/scripts/state/api/topics/createTopic.ts @@ -1,11 +1,11 @@ -import { trpc } from 'client/scripts/utils/trpcClient'; -import { ApiEndpoints, queryClient } from 'state/api/config'; +import { trpc } from 'utils/trpcClient'; const useCreateTopicMutation = () => { + const utils = trpc.useUtils(); return trpc.community.createTopic.useMutation({ onSuccess: async (response) => { - await queryClient.invalidateQueries({ - queryKey: [ApiEndpoints.BULK_TOPICS, response.topic.community_id], + await utils.community.getTopics.invalidate({ + community_id: response.topic.community_id, }); }, }); diff --git a/packages/commonwealth/client/scripts/state/api/topics/deleteTopic.ts b/packages/commonwealth/client/scripts/state/api/topics/deleteTopic.ts index 034e5087ca3..ddc72a0918e 100644 --- a/packages/commonwealth/client/scripts/state/api/topics/deleteTopic.ts +++ b/packages/commonwealth/client/scripts/state/api/topics/deleteTopic.ts @@ -1,11 +1,11 @@ -import { trpc } from 'client/scripts/utils/trpcClient'; -import { ApiEndpoints, queryClient } from 'state/api/config'; +import { trpc } from 'utils/trpcClient'; const useDeleteTopicMutation = () => { + const utils = trpc.useUtils(); return trpc.community.deleteTopic.useMutation({ onSuccess: async (response) => { - await queryClient.invalidateQueries({ - queryKey: [ApiEndpoints.BULK_TOPICS, response.community_id], + await utils.community.getTopics.invalidate({ + community_id: response.community_id, }); // TODO: add a new method in thread cache to deal with this // await app.threads.listingStore.removeTopic(variables.topicName); diff --git a/packages/commonwealth/client/scripts/state/api/topics/editTopic.ts b/packages/commonwealth/client/scripts/state/api/topics/editTopic.ts index f3148beac09..858a5a0a930 100644 --- a/packages/commonwealth/client/scripts/state/api/topics/editTopic.ts +++ b/packages/commonwealth/client/scripts/state/api/topics/editTopic.ts @@ -1,11 +1,11 @@ -import { trpc } from 'client/scripts/utils/trpcClient'; -import { ApiEndpoints, queryClient } from 'state/api/config'; +import { trpc } from 'utils/trpcClient'; const useEditTopicMutation = () => { + const utils = trpc.useUtils(); return trpc.community.updateTopic.useMutation({ - onSuccess: async (data) => { - await queryClient.invalidateQueries({ - queryKey: [ApiEndpoints.BULK_TOPICS, data.topic.community_id], + onSuccess: async (response) => { + await utils.community.getTopics.invalidate({ + community_id: response.topic.community_id, }); }, }); diff --git a/packages/commonwealth/client/scripts/state/api/topics/fetchTopics.ts b/packages/commonwealth/client/scripts/state/api/topics/fetchTopics.ts index bd5ea38d7e6..bdc91f4a6d0 100644 --- a/packages/commonwealth/client/scripts/state/api/topics/fetchTopics.ts +++ b/packages/commonwealth/client/scripts/state/api/topics/fetchTopics.ts @@ -1,8 +1,4 @@ -import { useQuery } from '@tanstack/react-query'; -import axios from 'axios'; -import Topic from 'models/Topic'; -import app from 'state'; -import { ApiEndpoints, SERVER_URL } from 'state/api/config'; +import { trpc } from 'client/scripts/utils/trpcClient'; const TOPICS_STALE_TIME = 30 * 1_000; // 30 s @@ -12,31 +8,21 @@ interface FetchTopicsProps { includeContestData?: boolean; } -const fetchTopics = async ({ - communityId, - includeContestData = false, -}: FetchTopicsProps): Promise => { - const response = await axios.get(`${SERVER_URL}${ApiEndpoints.BULK_TOPICS}`, { - params: { - community_id: communityId || app.activeChainId(), - with_contest_managers: includeContestData, - }, - }); - - return response.data.result.map((t) => new Topic(t)); -}; - const useFetchTopicsQuery = ({ communityId, apiEnabled = true, includeContestData, }: FetchTopicsProps) => { - return useQuery({ - queryKey: [ApiEndpoints.BULK_TOPICS, communityId, includeContestData], - queryFn: () => fetchTopics({ communityId, includeContestData }), - staleTime: TOPICS_STALE_TIME, - enabled: apiEnabled, - }); + return trpc.community.getTopics.useQuery( + { + community_id: communityId, + with_contest_managers: includeContestData, + }, + { + staleTime: TOPICS_STALE_TIME, + enabled: apiEnabled, + }, + ); }; export default useFetchTopicsQuery; diff --git a/packages/commonwealth/client/scripts/state/api/topics/updateFeaturedTopicsOrder.ts b/packages/commonwealth/client/scripts/state/api/topics/updateFeaturedTopicsOrder.ts index 4ae70c3e304..fbe5ff3148c 100644 --- a/packages/commonwealth/client/scripts/state/api/topics/updateFeaturedTopicsOrder.ts +++ b/packages/commonwealth/client/scripts/state/api/topics/updateFeaturedTopicsOrder.ts @@ -1,8 +1,9 @@ import { useMutation } from '@tanstack/react-query'; import axios from 'axios'; -import Topic from 'models/Topic'; +import type { Topic } from 'models/Topic'; import app from 'state'; -import { ApiEndpoints, SERVER_URL, queryClient } from 'state/api/config'; +import { SERVER_URL } from 'state/api/config'; +import { trpc } from 'utils/trpcClient'; import { userStore } from '../../ui/user'; interface UpdateFeaturedTopicsOrderProps { @@ -25,12 +26,12 @@ const updateFeaturedTopicsOrder = async ({ }; const useUpdateFeaturedTopicsOrderMutation = () => { + const utils = trpc.useUtils(); return useMutation({ mutationFn: updateFeaturedTopicsOrder, - onSuccess: async (data, variables) => { - const communityId = variables.featuredTopics[0].communityId; - await queryClient.invalidateQueries({ - queryKey: [ApiEndpoints.BULK_TOPICS, communityId], + onSuccess: async (_, variables) => { + await utils.community.getTopics.invalidate({ + community_id: variables.featuredTopics[0].community_id, }); }, }); diff --git a/packages/commonwealth/client/scripts/views/components/NewThreadFormLegacy/CustomTopicOption.tsx b/packages/commonwealth/client/scripts/views/components/NewThreadFormLegacy/CustomTopicOption.tsx index ab695f2a11d..44fee9f563c 100644 --- a/packages/commonwealth/client/scripts/views/components/NewThreadFormLegacy/CustomTopicOption.tsx +++ b/packages/commonwealth/client/scripts/views/components/NewThreadFormLegacy/CustomTopicOption.tsx @@ -1,4 +1,4 @@ -import Topic from 'models/Topic'; +import type { Topic } from 'models/Topic'; import React from 'react'; import { components, OptionProps } from 'react-select'; import { CWIcon } from 'views/components/component_kit/cw_icons/cw_icon'; @@ -15,7 +15,7 @@ const CustomTopicOption = ({ return ( // @ts-expect-error - {(topic?.activeContestManagers?.length || 0) > 0 && ( + {(topic?.active_contest_managers?.length || 0) > 0 && ( )} {originalProps.label} diff --git a/packages/commonwealth/client/scripts/views/components/NewThreadFormLegacy/NewThreadForm.tsx b/packages/commonwealth/client/scripts/views/components/NewThreadFormLegacy/NewThreadForm.tsx index 9d525136def..9aaf244b0f1 100644 --- a/packages/commonwealth/client/scripts/views/components/NewThreadFormLegacy/NewThreadForm.tsx +++ b/packages/commonwealth/client/scripts/views/components/NewThreadFormLegacy/NewThreadForm.tsx @@ -5,6 +5,7 @@ import { parseCustomStages } from 'helpers'; import { detectURL, getThreadActionTooltipText } from 'helpers/threads'; import useJoinCommunityBanner from 'hooks/useJoinCommunityBanner'; import useTopicGating from 'hooks/useTopicGating'; +import type { Topic } from 'models/Topic'; import { useCommonNavigate } from 'navigation/helpers'; import React, { useEffect, useMemo, useState } from 'react'; import { useLocation } from 'react-router-dom'; @@ -62,7 +63,9 @@ export const NewThreadForm = () => { const { isContestAvailable } = useCommunityContests(); - const sortedTopics = [...topics].sort((a, b) => a.name.localeCompare(b.name)); + const sortedTopics: Topic[] = [...topics].sort((a, b) => + a.name.localeCompare(b.name), + ); const hasTopics = sortedTopics?.length; const topicsForSelector = hasTopics ? sortedTopics : []; @@ -85,13 +88,14 @@ export const NewThreadForm = () => { setCanShowTopicPermissionBanner, } = useNewThreadForm(communityId, topicsForSelector); - const hasTopicOngoingContest = threadTopic?.activeContestManagers?.length > 0; + const hasTopicOngoingContest = + threadTopic?.active_contest_managers?.length > 0; const user = useUserStore(); const { checkForSessionKeyRevalidationErrors } = useAuthModalStore(); - const contestTopicError = threadTopic?.activeContestManagers?.length - ? threadTopic?.activeContestManagers + const contestTopicError = threadTopic?.active_contest_managers?.length + ? threadTopic?.active_contest_managers ?.map( (acm) => acm?.content?.filter( @@ -209,10 +213,7 @@ export const NewThreadForm = () => { const handleCancel = () => { setThreadTitle(''); - setThreadTopic( - // @ts-expect-error - topicsForSelector?.find((t) => t?.name?.includes('General')) || null, - ); + setThreadTopic(topicsForSelector.find((t) => t.name.includes('General'))!); setThreadContentDelta(createDeltaFromText('')); }; @@ -320,10 +321,10 @@ export const NewThreadForm = () => { {contestTopicAffordanceVisible && ( { + contests={threadTopic?.active_contest_managers.map((acm) => { return { - name: acm?.contest_manager?.name, - address: acm?.contest_manager?.contest_address, + name: acm?.name, + address: acm?.contest_address, submittedEntries: acm?.content?.filter( (c) => diff --git a/packages/commonwealth/client/scripts/views/components/NewThreadFormLegacy/helpers/useNewThreadForm.ts b/packages/commonwealth/client/scripts/views/components/NewThreadFormLegacy/helpers/useNewThreadForm.ts index 5911f7035b4..a55c52e5f32 100644 --- a/packages/commonwealth/client/scripts/views/components/NewThreadFormLegacy/helpers/useNewThreadForm.ts +++ b/packages/commonwealth/client/scripts/views/components/NewThreadFormLegacy/helpers/useNewThreadForm.ts @@ -3,7 +3,7 @@ import { useEffect, useMemo, useState } from 'react'; import { useDraft } from 'hooks/useDraft'; import { useSearchParams } from 'react-router-dom'; -import type Topic from '../../../../models/Topic'; +import type { Topic } from '../../../../models/Topic'; import { ThreadKind } from '../../../../models/types'; import { getTextFromDelta } from '../../react_quill_editor'; @@ -85,10 +85,10 @@ const useNewThreadForm = (communityId: string, topicsForSelector: Topic[]) => { } saveDraft(draft); - if (!threadContentDelta && threadTopic?.defaultOffchainTemplate) { + if (!threadContentDelta && threadTopic?.default_offchain_template) { try { const template = JSON.parse( - threadTopic.defaultOffchainTemplate, + threadTopic.default_offchain_template, ) as DeltaStatic; setThreadContentDelta(template); } catch (e) { diff --git a/packages/commonwealth/client/scripts/views/components/NewThreadFormLegacy/types.ts b/packages/commonwealth/client/scripts/views/components/NewThreadFormLegacy/types.ts index 20afa7863e4..3b16317ddfe 100644 --- a/packages/commonwealth/client/scripts/views/components/NewThreadFormLegacy/types.ts +++ b/packages/commonwealth/client/scripts/views/components/NewThreadFormLegacy/types.ts @@ -1,4 +1,4 @@ -import type Topic from '../../../models/Topic'; +import type { Topic } from '../../../models/Topic'; import type { ThreadKind } from '../../../models/types'; export type NewThreadFormType = { diff --git a/packages/commonwealth/client/scripts/views/components/NewThreadFormModern/CustomTopicOption.tsx b/packages/commonwealth/client/scripts/views/components/NewThreadFormModern/CustomTopicOption.tsx index ab695f2a11d..44fee9f563c 100644 --- a/packages/commonwealth/client/scripts/views/components/NewThreadFormModern/CustomTopicOption.tsx +++ b/packages/commonwealth/client/scripts/views/components/NewThreadFormModern/CustomTopicOption.tsx @@ -1,4 +1,4 @@ -import Topic from 'models/Topic'; +import type { Topic } from 'models/Topic'; import React from 'react'; import { components, OptionProps } from 'react-select'; import { CWIcon } from 'views/components/component_kit/cw_icons/cw_icon'; @@ -15,7 +15,7 @@ const CustomTopicOption = ({ return ( // @ts-expect-error - {(topic?.activeContestManagers?.length || 0) > 0 && ( + {(topic?.active_contest_managers?.length || 0) > 0 && ( )} {originalProps.label} diff --git a/packages/commonwealth/client/scripts/views/components/NewThreadFormModern/NewThreadForm.tsx b/packages/commonwealth/client/scripts/views/components/NewThreadFormModern/NewThreadForm.tsx index 541a039b5e4..7678c6e2b73 100644 --- a/packages/commonwealth/client/scripts/views/components/NewThreadFormModern/NewThreadForm.tsx +++ b/packages/commonwealth/client/scripts/views/components/NewThreadFormModern/NewThreadForm.tsx @@ -82,13 +82,14 @@ export const NewThreadForm = () => { setCanShowTopicPermissionBanner, } = useNewThreadForm(communityId, topicsForSelector); - const hasTopicOngoingContest = threadTopic?.activeContestManagers?.length > 0; + const hasTopicOngoingContest = + threadTopic?.active_contest_managers?.length > 0; const user = useUserStore(); const { checkForSessionKeyRevalidationErrors } = useAuthModalStore(); - const contestTopicError = threadTopic?.activeContestManagers?.length - ? threadTopic?.activeContestManagers + const contestTopicError = threadTopic?.active_contest_managers?.length + ? threadTopic?.active_contest_managers ?.map( (acm) => acm?.content?.filter( @@ -303,10 +304,10 @@ export const NewThreadForm = () => { {contestTopicAffordanceVisible && ( { + contests={threadTopic?.active_contest_managers.map((acm) => { return { - name: acm?.contest_manager?.name, - address: acm?.contest_manager?.contest_address, + name: acm?.name, + address: acm?.contest_address, submittedEntries: acm?.content?.filter( (c) => diff --git a/packages/commonwealth/client/scripts/views/components/NewThreadFormModern/helpers/useNewThreadForm.ts b/packages/commonwealth/client/scripts/views/components/NewThreadFormModern/helpers/useNewThreadForm.ts index 8931bfd4d10..32eadb7a1d6 100644 --- a/packages/commonwealth/client/scripts/views/components/NewThreadFormModern/helpers/useNewThreadForm.ts +++ b/packages/commonwealth/client/scripts/views/components/NewThreadFormModern/helpers/useNewThreadForm.ts @@ -2,7 +2,7 @@ import { useEffect, useMemo, useState } from 'react'; import { useDraft } from 'hooks/useDraft'; import { useSearchParams } from 'react-router-dom'; -import type Topic from '../../../../models/Topic'; +import type { Topic } from '../../../../models/Topic'; import { ThreadKind } from '../../../../models/types'; type NewThreadDraft = { @@ -82,10 +82,10 @@ const useNewThreadForm = (communityId: string, topicsForSelector: Topic[]) => { } saveDraft(draft); - if (!editorText && threadTopic?.defaultOffchainTemplate) { + if (!editorText && threadTopic?.default_offchain_template) { try { const template = JSON.parse( - threadTopic.defaultOffchainTemplate, + threadTopic.default_offchain_template, ) as string; setEditorText(template); } catch (e) { diff --git a/packages/commonwealth/client/scripts/views/components/NewThreadFormModern/types.ts b/packages/commonwealth/client/scripts/views/components/NewThreadFormModern/types.ts index 20afa7863e4..3b16317ddfe 100644 --- a/packages/commonwealth/client/scripts/views/components/NewThreadFormModern/types.ts +++ b/packages/commonwealth/client/scripts/views/components/NewThreadFormModern/types.ts @@ -1,4 +1,4 @@ -import type Topic from '../../../models/Topic'; +import type { Topic } from '../../../models/Topic'; import type { ThreadKind } from '../../../models/types'; export type NewThreadFormType = { diff --git a/packages/commonwealth/client/scripts/views/components/feed.tsx b/packages/commonwealth/client/scripts/views/components/feed.tsx index 2249bd5c598..fceef35adca 100644 --- a/packages/commonwealth/client/scripts/views/components/feed.tsx +++ b/packages/commonwealth/client/scripts/views/components/feed.tsx @@ -16,7 +16,6 @@ import { getThreadActionTooltipText } from 'helpers/threads'; import useTopicGating from 'hooks/useTopicGating'; import { getProposalUrlPath } from 'identifiers'; import { Thread, type RecentComment } from 'models/Thread'; -import Topic from 'models/Topic'; import { ThreadKind, ThreadStage } from 'models/types'; import { useCommonNavigate } from 'navigation/helpers'; import { useGetCommunityByIdQuery } from 'state/api/communities'; @@ -132,17 +131,18 @@ function mapThread(thread: z.infer): Thread { id: thread.id, created_at: thread.created_at ?? '', updated_at: thread.updated_at ?? thread.created_at ?? '', - topic: new Topic({ + topic: { community_id: thread.community_id, id: thread.topic.id, name: thread.topic.name, description: thread.topic.description, + created_at: '', featured_in_sidebar: false, featured_in_new_post: false, group_ids: [], active_contest_managers: [], total_threads: 0, - }), + }, kind: thread.kind as ThreadKind, stage: thread.stage as ThreadStage, ThreadVersionHistories: [], diff --git a/packages/commonwealth/client/scripts/views/components/sidebar/discussion_section.tsx b/packages/commonwealth/client/scripts/views/components/sidebar/discussion_section.tsx index b45be22ffed..d2122a0839c 100644 --- a/packages/commonwealth/client/scripts/views/components/sidebar/discussion_section.tsx +++ b/packages/commonwealth/client/scripts/views/components/sidebar/discussion_section.tsx @@ -83,7 +83,7 @@ export const DiscussionSection = ({ }); const topics = (topicsData || []) - .filter((t) => t.featuredInSidebar) + .filter((t) => t.featured_in_sidebar) .sort((a, b) => a.name.localeCompare(b.name)) // @ts-expect-error .sort((a, b) => a.order - b.order); @@ -99,7 +99,7 @@ export const DiscussionSection = ({ }; for (const topic of topics) { - if (topic.featuredInSidebar) { + if (topic.featured_in_sidebar) { discussionsDefaultToggleTree.children[topic.name] = { toggledState: true, children: { @@ -190,7 +190,7 @@ export const DiscussionSection = ({ ]; for (const topic of topics) { - if (topic.featuredInSidebar) { + if (topic.featured_in_sidebar) { const topicInvolvedInActiveContest = topic?.id && topicIdsIncludedInContest.includes(topic.id); diff --git a/packages/commonwealth/client/scripts/views/components/topic_selector.tsx b/packages/commonwealth/client/scripts/views/components/topic_selector.tsx index ff30d3560f9..0217d0ff270 100644 --- a/packages/commonwealth/client/scripts/views/components/topic_selector.tsx +++ b/packages/commonwealth/client/scripts/views/components/topic_selector.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import Topic from '../../models/Topic'; +import type { Topic } from '../../models/Topic'; import 'components/topic_selector.scss'; import { SelectList } from 'views/components/component_kit/cw_select_list'; diff --git a/packages/commonwealth/client/scripts/views/modals/change_thread_topic_modal.tsx b/packages/commonwealth/client/scripts/views/modals/change_thread_topic_modal.tsx index bafa82e7050..e323e652f0d 100644 --- a/packages/commonwealth/client/scripts/views/modals/change_thread_topic_modal.tsx +++ b/packages/commonwealth/client/scripts/views/modals/change_thread_topic_modal.tsx @@ -3,7 +3,7 @@ import React, { useState } from 'react'; import { buildUpdateThreadInput } from 'client/scripts/state/api/threads/editThread'; import useUserStore from 'state/ui/user'; import type Thread from '../../models/Thread'; -import type Topic from '../../models/Topic'; +import type { Topic } from '../../models/Topic'; import app from '../../state'; import { useEditThreadMutation } from '../../state/api/threads'; import { useFetchTopicsQuery } from '../../state/api/topics'; diff --git a/packages/commonwealth/client/scripts/views/modals/edit_topic_modal.tsx b/packages/commonwealth/client/scripts/views/modals/edit_topic_modal.tsx index c7cf8798038..642b5251424 100644 --- a/packages/commonwealth/client/scripts/views/modals/edit_topic_modal.tsx +++ b/packages/commonwealth/client/scripts/views/modals/edit_topic_modal.tsx @@ -1,6 +1,6 @@ import { pluralizeWithoutNumberPrefix } from 'helpers'; import React, { useState } from 'react'; -import Topic from '../../models/Topic'; +import type { Topic } from '../../models/Topic'; import { useCommonNavigate } from '../../navigation/helpers'; import app from '../../state'; import { @@ -37,7 +37,7 @@ export const EditTopicModal = ({ }: EditTopicModalProps) => { const { description: descriptionProp, - featuredInSidebar: featuredInSidebarProp, + featured_in_sidebar: featuredInSidebarProp, id, name: nameProp, } = topic; diff --git a/packages/commonwealth/client/scripts/views/modals/order_topics_modal/draggable_topics_list.tsx b/packages/commonwealth/client/scripts/views/modals/order_topics_modal/draggable_topics_list.tsx index 8a1558feb67..13d01d99801 100644 --- a/packages/commonwealth/client/scripts/views/modals/order_topics_modal/draggable_topics_list.tsx +++ b/packages/commonwealth/client/scripts/views/modals/order_topics_modal/draggable_topics_list.tsx @@ -5,7 +5,7 @@ import { DragDropContext, Draggable } from 'react-beautiful-dnd'; import { Virtuoso } from 'react-virtuoso'; import { CWIcon } from 'views/components/component_kit/cw_icons/cw_icon'; import { CWText } from 'views/components/component_kit/cw_text'; -import type Topic from '../../../models/Topic'; +import type { Topic } from '../../../models/Topic'; import CWIconButton from '../../components/component_kit/new_designs/CWIconButton'; const reorder = (list: Topic[], startIndex, endIndex): Topic[] => { @@ -17,8 +17,10 @@ const reorder = (list: Topic[], startIndex, endIndex): Topic[] => { }; // This component handles fixed size of the item in the list +// eslint-disable-next-line react/prop-types const HeightPreservingItem = ({ children, ...props }) => { const [size, setSize] = useState(0); + // eslint-disable-next-line react/prop-types const knownSize = props['data-known-size']; useEffect(() => { @@ -43,6 +45,7 @@ interface TopicRowProps { onEdit?: React.Dispatch>; } +// eslint-disable-next-line react/no-multi-comp const TopicRow = ({ provided, item, isDragging, onEdit }: TopicRowProps) => { return (
>; } +// eslint-disable-next-line react/no-multi-comp const DraggableTopicsList = ({ topics, setTopics, diff --git a/packages/commonwealth/client/scripts/views/pages/CommunityManagement/Contests/AdminContestsPage/AdminContestsPage.tsx b/packages/commonwealth/client/scripts/views/pages/CommunityManagement/Contests/AdminContestsPage/AdminContestsPage.tsx index ab34085e0df..54ff4498b1a 100644 --- a/packages/commonwealth/client/scripts/views/pages/CommunityManagement/Contests/AdminContestsPage/AdminContestsPage.tsx +++ b/packages/commonwealth/client/scripts/views/pages/CommunityManagement/Contests/AdminContestsPage/AdminContestsPage.tsx @@ -62,7 +62,7 @@ const AdminContestsPage = () => { }); const hasAtLeastOneWeightedVotingTopic = topicData?.some( - (t) => t.weightedVoting, + (t) => t.weighted_voting, ); const { data: feeManagerBalance, isLoading: isFeeManagerBalanceLoading } = diff --git a/packages/commonwealth/client/scripts/views/pages/CommunityManagement/Contests/ManageContest/steps/DetailsFormStep/DetailsFormStep.tsx b/packages/commonwealth/client/scripts/views/pages/CommunityManagement/Contests/ManageContest/steps/DetailsFormStep/DetailsFormStep.tsx index 996105e13c4..ea3d232e718 100644 --- a/packages/commonwealth/client/scripts/views/pages/CommunityManagement/Contests/ManageContest/steps/DetailsFormStep/DetailsFormStep.tsx +++ b/packages/commonwealth/client/scripts/views/pages/CommunityManagement/Contests/ManageContest/steps/DetailsFormStep/DetailsFormStep.tsx @@ -124,12 +124,12 @@ const DetailsFormStep = ({ const totalPayoutPercentageError = totalPayoutPercentage !== 100; const weightedTopics = (topicsData || []) - .filter((t) => t?.weightedVoting) + .filter((t) => t?.weighted_voting) .map((t) => ({ value: t.id, label: t.name, - weightedVoting: t.weightedVoting, - helpText: weightedVotingValueToLabel(t.weightedVoting!), + weightedVoting: t.weighted_voting, + helpText: weightedVotingValueToLabel(t.weighted_voting!), })); const getInitialValues = () => { @@ -313,7 +313,7 @@ const DetailsFormStep = ({ if (t?.weightedVoting === TopicWeightedVoting.ERC20) { const token = topicsData?.find( (topic) => topic.id === t.value, - )?.tokenAddress; + )?.token_address; setTokenValue(token || ''); } }} diff --git a/packages/commonwealth/client/scripts/views/pages/CommunityManagement/Integrations/Discord/Discord.tsx b/packages/commonwealth/client/scripts/views/pages/CommunityManagement/Integrations/Discord/Discord.tsx index 344750faa9d..6c2c5800712 100644 --- a/packages/commonwealth/client/scripts/views/pages/CommunityManagement/Integrations/Discord/Discord.tsx +++ b/packages/commonwealth/client/scripts/views/pages/CommunityManagement/Integrations/Discord/Discord.tsx @@ -229,7 +229,7 @@ const Discord = () => { topics={topics.map((topic) => ({ name: topic.name, id: `${topic.id}`, - channelId: topic.channelId, + channelId: topic.channel_id, }))} refetchTopics={async () => { await refetchTopics(); diff --git a/packages/commonwealth/client/scripts/views/pages/CommunityManagement/Topics/TopicDetails/ManageTopicsSection/ManageTopicsSection.tsx b/packages/commonwealth/client/scripts/views/pages/CommunityManagement/Topics/TopicDetails/ManageTopicsSection/ManageTopicsSection.tsx index d4c889867d6..f6ea255da0b 100644 --- a/packages/commonwealth/client/scripts/views/pages/CommunityManagement/Topics/TopicDetails/ManageTopicsSection/ManageTopicsSection.tsx +++ b/packages/commonwealth/client/scripts/views/pages/CommunityManagement/Topics/TopicDetails/ManageTopicsSection/ManageTopicsSection.tsx @@ -1,6 +1,6 @@ import { notifyError, notifySuccess } from 'controllers/app/notifications'; import useBrowserWindow from 'hooks/useBrowserWindow'; -import type Topic from 'models/Topic'; +import type { Topic } from 'models/Topic'; import React, { useEffect, useState } from 'react'; import app from 'state'; import { @@ -18,7 +18,7 @@ import './ManageTopicsSection.scss'; export const ManageTopicsSection = () => { const getFeaturedTopics = (rawTopics: Topic[]): Topic[] => { const topics = rawTopics - .filter((topic) => topic.featuredInSidebar) + .filter((topic) => topic.featured_in_sidebar) .map((topic) => ({ ...topic }) as Topic); if (!topics.length) return []; @@ -37,7 +37,7 @@ export const ManageTopicsSection = () => { const getRegularTopics = (rawTopics: Topic[]): Topic[] => { const topics = rawTopics - .filter((topic) => !topic.featuredInSidebar) + .filter((topic) => !topic.featured_in_sidebar) .map((topic) => ({ ...topic }) as Topic); if (!topics.length) return []; diff --git a/packages/commonwealth/client/scripts/views/pages/CommunityManagement/Topics/TopicsOld/ManageTopicsSectionOld/ManageTopicsSectionOld.tsx b/packages/commonwealth/client/scripts/views/pages/CommunityManagement/Topics/TopicsOld/ManageTopicsSectionOld/ManageTopicsSectionOld.tsx index f8fcd81fffa..ea388371c7f 100644 --- a/packages/commonwealth/client/scripts/views/pages/CommunityManagement/Topics/TopicsOld/ManageTopicsSectionOld/ManageTopicsSectionOld.tsx +++ b/packages/commonwealth/client/scripts/views/pages/CommunityManagement/Topics/TopicsOld/ManageTopicsSectionOld/ManageTopicsSectionOld.tsx @@ -1,6 +1,6 @@ import { notifyError, notifySuccess } from 'controllers/app/notifications'; import useBrowserWindow from 'hooks/useBrowserWindow'; -import type Topic from 'models/Topic'; +import type { Topic } from 'models/Topic'; import React, { useEffect, useState } from 'react'; import app from 'state'; import { @@ -18,7 +18,7 @@ import './ManageTopicsSectionOld.scss'; export const ManageTopicsSectionOld = () => { const getFeaturedTopics = (rawTopics: Topic[]): Topic[] => { const topics = rawTopics - .filter((topic) => topic.featuredInSidebar) + .filter((topic) => topic.featured_in_sidebar) .map((topic) => ({ ...topic }) as Topic); if (!topics.length) return []; @@ -37,7 +37,7 @@ export const ManageTopicsSectionOld = () => { const getRegularTopics = (rawTopics: Topic[]): Topic[] => { const topics = rawTopics - .filter((topic) => !topic.featuredInSidebar) + .filter((topic) => !topic.featured_in_sidebar) .map((topic) => ({ ...topic }) as Topic); if (!topics.length) return []; diff --git a/packages/commonwealth/client/scripts/views/pages/discussions/DiscussionsPage.tsx b/packages/commonwealth/client/scripts/views/pages/discussions/DiscussionsPage.tsx index ba8cdf0211c..e9d10cb3871 100644 --- a/packages/commonwealth/client/scripts/views/pages/discussions/DiscussionsPage.tsx +++ b/packages/commonwealth/client/scripts/views/pages/discussions/DiscussionsPage.tsx @@ -108,7 +108,7 @@ const DiscussionsPage = ({ topicName }: DiscussionsPageProps) => { (domain?.isCustomDomain ? `/archived` : `/${app.activeChainId()}/archived`); const { data: tokenMetadata } = useTokenMetadataQuery({ - tokenId: topicObj?.tokenAddress || '', + tokenId: topicObj?.token_address || '', nodeEthChainId: app?.chain.meta?.ChainNode?.eth_chain_id || 0, }); @@ -175,7 +175,7 @@ const DiscussionsPage = ({ topicName }: DiscussionsPageProps) => { const isTopicWeighted = weightedTopicsEnabled && topicId && - topicObj.weightedVoting === TopicWeightedVoting.ERC20; + topicObj.weighted_voting === TopicWeightedVoting.ERC20; const activeContestsInTopic = contestsData?.filter((contest) => { const isContestInTopic = (contest.topics || []).find( @@ -321,11 +321,11 @@ const DiscussionsPage = ({ topicName }: DiscussionsPageProps) => { {isTopicWeighted && ( )} diff --git a/packages/commonwealth/client/scripts/views/pages/discussions/HeaderWithFilters/HeaderWithFilters.tsx b/packages/commonwealth/client/scripts/views/pages/discussions/HeaderWithFilters/HeaderWithFilters.tsx index 05a86d25fe0..134623ea205 100644 --- a/packages/commonwealth/client/scripts/views/pages/discussions/HeaderWithFilters/HeaderWithFilters.tsx +++ b/packages/commonwealth/client/scripts/views/pages/discussions/HeaderWithFilters/HeaderWithFilters.tsx @@ -20,7 +20,7 @@ import { EditTopicModal } from 'views/modals/edit_topic_modal'; import { Contest } from 'views/pages/CommunityManagement/Contests/ContestsList'; import ContestCard from 'views/pages/CommunityManagement/Contests/ContestsList/ContestCard'; import useCommunityContests from 'views/pages/CommunityManagement/Contests/useCommunityContests'; -import type Topic from '../../../../models/Topic'; +import type { Topic } from '../../../../models/Topic'; import { ThreadFeaturedFilterTypes, ThreadStage, @@ -107,13 +107,13 @@ export const HeaderWithFilters = ({ ); const featuredTopics = (topics || []) - .filter((t) => t.featuredInSidebar) + .filter((t) => t.featured_in_sidebar) .sort((a, b) => a.name.localeCompare(b.name)) // @ts-expect-error .sort((a, b) => a.order - b.order); const otherTopics = (topics || []) - .filter((t) => !t.featuredInSidebar) + .filter((t) => !t.featured_in_sidebar) .sort((a, b) => a.name.localeCompare(b.name)); const selectedTopic = (topics || []).find((t) => topic && topic === t.name); diff --git a/packages/commonwealth/client/scripts/views/pages/overview/TopicSummaryRow.tsx b/packages/commonwealth/client/scripts/views/pages/overview/TopicSummaryRow.tsx index 574abdb5fd4..2a965e58ab4 100644 --- a/packages/commonwealth/client/scripts/views/pages/overview/TopicSummaryRow.tsx +++ b/packages/commonwealth/client/scripts/views/pages/overview/TopicSummaryRow.tsx @@ -10,7 +10,7 @@ import app from 'state'; import useUserStore from 'state/ui/user'; import Permissions from 'utils/Permissions'; import type Thread from '../../../models/Thread'; -import type Topic from '../../../models/Topic'; +import type { Topic } from '../../../models/Topic'; import { CWText } from '../../components/component_kit/cw_text'; import { ThreadCard } from '../discussions/ThreadCard'; import { TopicSummaryRowSkeleton } from './TopicSummaryRowSkeleton'; @@ -73,7 +73,7 @@ export const TopicSummaryRow = ({ fontWeight="medium" className="threads-count-text" > - {topic.totalThreads || 0} Threads + {topic.total_threads || 0} Threads
{topic.description && {topic.description}} diff --git a/packages/commonwealth/client/scripts/views/pages/overview/index.tsx b/packages/commonwealth/client/scripts/views/pages/overview/index.tsx index a1e32dd85ee..b6035951ca4 100644 --- a/packages/commonwealth/client/scripts/views/pages/overview/index.tsx +++ b/packages/commonwealth/client/scripts/views/pages/overview/index.tsx @@ -10,7 +10,7 @@ import useUserStore from 'state/ui/user'; import { CWButton } from 'views/components/component_kit/new_designs/CWButton'; import CWPageLayout from 'views/components/component_kit/new_designs/CWPageLayout'; import type Thread from '../../../models/Thread'; -import type Topic from '../../../models/Topic'; +import type { Topic } from '../../../models/Topic'; import { CWDivider } from '../../components/component_kit/cw_divider'; import { CWText } from '../../components/component_kit/cw_text'; import { PageLoading } from '../loading'; @@ -35,10 +35,10 @@ const OverviewPage = () => { apiEnabled: !!communityId, }); - const anyTopicsFeatured = topics.some((t) => t.featuredInSidebar); + const anyTopicsFeatured = topics.some((t) => t.featured_in_sidebar); const topicsFiltered = anyTopicsFeatured - ? topics.filter((t) => t.featuredInSidebar) + ? topics.filter((t) => t.featured_in_sidebar) : topics; const topicsSorted = anyTopicsFeatured diff --git a/packages/commonwealth/server/api/community.ts b/packages/commonwealth/server/api/community.ts index fcb1a1f7529..01e7b4a93e9 100644 --- a/packages/commonwealth/server/api/community.ts +++ b/packages/commonwealth/server/api/community.ts @@ -99,6 +99,7 @@ export const trpcRouter = trpc.router({ Community.UpdateCustomDomain, trpc.Tag.Community, ), + getTopics: trpc.query(Community.GetTopics, trpc.Tag.Community), createTopic: trpc.command(Community.CreateTopic, trpc.Tag.Community, [ MixpanelCommunityInteractionEvent.CREATE_TOPIC, (result) => ({ diff --git a/packages/commonwealth/server/api/external-router.ts b/packages/commonwealth/server/api/external-router.ts index 919ac93812d..b94717a1253 100644 --- a/packages/commonwealth/server/api/external-router.ts +++ b/packages/commonwealth/server/api/external-router.ts @@ -57,6 +57,9 @@ const api = { getComments: trpc.query(Comment.GetComments, trpc.Tag.Comment, { forceSecure: true, }), + getTopics: trpc.query(Community.GetTopics, trpc.Tag.Community, { + forceSecure: true, + }), createCommunity, updateCommunity, createTopic, diff --git a/packages/commonwealth/server/controllers/server_topics_controller.ts b/packages/commonwealth/server/controllers/server_topics_controller.ts index 31aa6f96d48..fc4b4d83214 100644 --- a/packages/commonwealth/server/controllers/server_topics_controller.ts +++ b/packages/commonwealth/server/controllers/server_topics_controller.ts @@ -1,9 +1,4 @@ import { DB } from '@hicommonwealth/model'; -import { - GetTopicsOptions, - GetTopicsResult, - __getTopics, -} from './server_topics_methods/get_topics'; import { UpdateTopicChannelOptions, UpdateTopicChannelResult, @@ -21,10 +16,6 @@ import { export class ServerTopicsController { constructor(public models: DB) {} - async getTopics(options: GetTopicsOptions): Promise { - return __getTopics.call(this, options); - } - async updateTopicsOrder( options: UpdateTopicsOrderOptions, ): Promise { diff --git a/packages/commonwealth/server/controllers/server_topics_methods/get_topics.ts b/packages/commonwealth/server/controllers/server_topics_methods/get_topics.ts deleted file mode 100644 index c9991ae0510..00000000000 --- a/packages/commonwealth/server/controllers/server_topics_methods/get_topics.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { CommunityInstance, TopicAttributes } from '@hicommonwealth/model'; -import * as schemas from '@hicommonwealth/schemas'; -import { QueryTypes } from 'sequelize'; -import { z } from 'zod'; -import { ServerTopicsController } from '../server_topics_controller'; - -export type GetTopicsOptions = { - community: CommunityInstance; - with_contest_managers: boolean; -}; - -const ActiveContestManagers = schemas.ContestManager.extend({ - content: z.array(schemas.ContestAction), - contest_manager: schemas.ContestManager, -}); - -type TopicWithTotalThreads = TopicAttributes & { - total_threads: number; - active_contest_managers: Array>; -}; - -export type GetTopicsResult = TopicWithTotalThreads[]; -export async function __getTopics( - this: ServerTopicsController, - { community, with_contest_managers }: GetTopicsOptions, -): Promise { - const baseQuery = ` - WITH topic_data AS ( - SELECT t.*, ( - SELECT COUNT(*)::int - FROM "Threads" - WHERE community_id = :community_id AND topic_id = t.id AND deleted_at IS NULL - ) as total_threads - FROM "Topics" t - WHERE t.community_id = :community_id AND t.deleted_at IS NULL - ) - `; - - let sql = baseQuery; - - if (with_contest_managers) { - sql += ` - SELECT td.*, - COALESCE( - ( - SELECT JSON_AGG( - JSON_BUILD_OBJECT( - 'contest_manager', cm, - 'content', ( - SELECT JSON_AGG(ca) - FROM "ContestActions" ca - WHERE ca.contest_address = cm.contest_address - AND ca.action = 'added' - AND ca.created_at > co.start_time - AND ca.created_at < co.end_time - ) - ) - ) - FROM "ContestTopics" ct - LEFT JOIN "ContestManagers" cm ON cm.contest_address = ct.contest_address - JOIN ( - SELECT contest_address, MAX(contest_id) AS max_contest_id, - MAX(start_time) as start_time, MAX(end_time) as end_time - FROM "Contests" - GROUP BY contest_address - ) co ON cm.contest_address = co.contest_address - WHERE ct.topic_id = td.id - AND cm.community_id = :community_id - AND cm.cancelled = false - AND ( - cm.interval = 0 AND NOW() < co.end_time - OR cm.interval > 0 - ) - ), - '[]'::json - ) as active_contest_managers - FROM topic_data td - `; - } else { - sql += `SELECT *, '[]'::json as active_contest_managers FROM topic_data`; - } - - const topics = await this.models.sequelize.query(sql, { - replacements: { community_id: community.id }, - type: QueryTypes.SELECT, - }); - - return topics; -} diff --git a/packages/commonwealth/server/routes/topics/get_topics_handler.ts b/packages/commonwealth/server/routes/topics/get_topics_handler.ts deleted file mode 100644 index cb886003d79..00000000000 --- a/packages/commonwealth/server/routes/topics/get_topics_handler.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { TopicAttributes } from '@hicommonwealth/model'; -import { Request } from 'express'; -import { ServerControllers } from '../../routing/router'; -import { TypedResponse, success } from '../../types'; - -type GetTopicsResponse = TopicAttributes[]; - -export const getTopicsHandler = async ( - controllers: ServerControllers, - req: Request, - res: TypedResponse, -) => { - const { community } = req; - const { with_contest_managers } = req.query; - - const topics = await controllers.topics.getTopics({ - community: community!, - with_contest_managers: with_contest_managers === 'true', - }); - - return success(res, topics); -}; diff --git a/packages/commonwealth/server/routing/router.ts b/packages/commonwealth/server/routing/router.ts index 48883c0ffe2..4b68f35f523 100644 --- a/packages/commonwealth/server/routing/router.ts +++ b/packages/commonwealth/server/routing/router.ts @@ -98,7 +98,6 @@ import { getTagsHandler } from '../routes/tags/get_tags_handler'; import { createThreadPollHandler } from '../routes/threads/create_thread_poll_handler'; import { getThreadPollsHandler } from '../routes/threads/get_thread_polls_handler'; import { getThreadsHandler } from '../routes/threads/get_threads_handler'; -import { getTopicsHandler } from '../routes/topics/get_topics_handler'; import { updateTopicChannelHandler } from '../routes/topics/update_topic_channel_handler'; import { updateTopicsOrderHandler } from '../routes/topics/update_topics_order_handler'; import { failure } from '../types'; @@ -388,13 +387,6 @@ function setupRouter( databaseValidationService.validateCommunity, updateTopicsOrderHandler.bind(this, serverControllers), ); - registerRoute( - router, - 'get', - '/topics' /* OLD: /bulkTopics */, - databaseValidationService.validateCommunity, - getTopicsHandler.bind(this, serverControllers), - ); // reactions registerRoute( diff --git a/packages/commonwealth/test/integration/api/topics.spec.ts b/packages/commonwealth/test/integration/api/topics.spec.ts deleted file mode 100644 index 19323d1fc5d..00000000000 --- a/packages/commonwealth/test/integration/api/topics.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { dispose } from '@hicommonwealth/core'; -import chai from 'chai'; -import chaiHttp from 'chai-http'; -import { ApiEndpoints } from 'state/api/config'; -import { afterAll, beforeAll, describe, test } from 'vitest'; -import { TestServer, testServer } from '../../../server-test'; - -chai.use(chaiHttp); -const { expect } = chai; - -let adminJWT; - -describe('Topic Tests', () => { - const chain = 'ethereum'; - - let server: TestServer; - - beforeAll(async () => { - server = await testServer(); - }); - - afterAll(async () => { - await dispose()(); - }); - - describe('Bulk Topics', () => { - test('Should pass /bulkTopics', async () => { - const res = await chai.request - .agent(server.app) - .get(`/api${ApiEndpoints.BULK_TOPICS}`) - .set('Accept', 'application/json') - .query({ - chain, - jwt: adminJWT, - }); - expect(res.body).to.not.be.null; - expect(res.body.status).to.be.equal('Success'); - expect(res.body.result).to.not.be.null; - expect(res.body.result.length).to.be.equal(2); - }); - }); -}); diff --git a/packages/commonwealth/test/unit/server_controllers/server_topics_controller.spec.ts b/packages/commonwealth/test/unit/server_controllers/server_topics_controller.spec.ts index b2fdaf46aaa..ab2707906ad 100644 --- a/packages/commonwealth/test/unit/server_controllers/server_topics_controller.spec.ts +++ b/packages/commonwealth/test/unit/server_controllers/server_topics_controller.spec.ts @@ -1,5 +1,4 @@ import { CommunityInstance, UserInstance } from '@hicommonwealth/model'; -import { expect } from 'chai'; import { ServerTopicsController } from 'server/controllers/server_topics_controller'; import { describe, test } from 'vitest'; @@ -79,15 +78,6 @@ const createMockedTopicsController = (isAdmin: boolean = false) => { }; describe('ServerTopicsController', () => { - test('#getTopics', async () => { - const { controller, chain } = createMockedTopicsController(); - const topics = await controller.getTopics({ - community: chain, - with_contest_managers: false, - }); - expect(topics).to.have.length(1); - }); - test('#updateTopicChannel', async () => { const { controller, user } = createMockedTopicsController(true); await controller.updateTopicChannel({