From 6993d39d49704b203ac7595c994c34cf3524e188 Mon Sep 17 00:00:00 2001 From: rotorsoft Date: Thu, 17 Oct 2024 17:17:43 -0400 Subject: [PATCH 01/10] fist pass --- .../model/src/thread/GetTopics.query.ts | 81 ++++++------- libs/model/src/thread/index.ts | 1 + libs/schemas/src/queries/thread.schemas.ts | 22 +++- .../client/scripts/models/Topic.ts | 108 ++++++------------ .../client/scripts/state/api/config.ts | 1 - .../scripts/state/api/topics/fetchTopics.ts | 37 +++--- .../server/api/external-router.ts | 5 +- packages/commonwealth/server/api/threads.ts | 1 + .../controllers/server_topics_controller.ts | 9 -- .../routes/topics/get_topics_handler.ts | 22 ---- .../commonwealth/server/routing/router.ts | 8 -- 11 files changed, 111 insertions(+), 184 deletions(-) rename packages/commonwealth/server/controllers/server_topics_methods/get_topics.ts => libs/model/src/thread/GetTopics.query.ts (59%) delete mode 100644 packages/commonwealth/server/routes/topics/get_topics_handler.ts diff --git a/packages/commonwealth/server/controllers/server_topics_methods/get_topics.ts b/libs/model/src/thread/GetTopics.query.ts similarity index 59% rename from packages/commonwealth/server/controllers/server_topics_methods/get_topics.ts rename to libs/model/src/thread/GetTopics.query.ts index c9991ae0510..e7ba93fc607 100644 --- a/packages/commonwealth/server/controllers/server_topics_methods/get_topics.ts +++ b/libs/model/src/thread/GetTopics.query.ts @@ -1,46 +1,19 @@ -import { CommunityInstance, TopicAttributes } from '@hicommonwealth/model'; +import { Query } from '@hicommonwealth/core'; import * as schemas from '@hicommonwealth/schemas'; import { QueryTypes } from 'sequelize'; import { z } from 'zod'; -import { ServerTopicsController } from '../server_topics_controller'; +import { models } from '../database'; -export type GetTopicsOptions = { - community: CommunityInstance; - with_contest_managers: boolean; -}; +export function GetTopics(): Query { + return { + ...schemas.GetTopics, + auth: [], + secure: false, + body: async ({ payload }) => { + const { community_id, with_contest_managers } = payload; -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.*, + const base = with_contest_managers + ? `SELECT td.*, COALESCE( ( SELECT JSON_AGG( @@ -75,15 +48,29 @@ export async function __getTopics( '[]'::json ) as active_contest_managers FROM topic_data td - `; - } else { - sql += `SELECT *, '[]'::json as active_contest_managers FROM topic_data`; - } + ` + : `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, - }); + const sql = ` + 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 + ) + ${base} + `; - return topics; + return await models.sequelize.query< + z.infer + >(sql, { + replacements: { community_id }, + type: QueryTypes.SELECT, + raw: true, + }); + }, + }; } diff --git a/libs/model/src/thread/index.ts b/libs/model/src/thread/index.ts index b29d9c54a60..72877341b38 100644 --- a/libs/model/src/thread/index.ts +++ b/libs/model/src/thread/index.ts @@ -3,4 +3,5 @@ export * from './CreateThreadReaction.command'; export * from './DeleteThread.command'; export * from './GetThread.query'; export * from './GetThreads.query'; +export * from './GetTopics.query'; export * from './UpdateThread.command'; diff --git a/libs/schemas/src/queries/thread.schemas.ts b/libs/schemas/src/queries/thread.schemas.ts index c67d3defff4..08e37805293 100644 --- a/libs/schemas/src/queries/thread.schemas.ts +++ b/libs/schemas/src/queries/thread.schemas.ts @@ -1,10 +1,11 @@ import { z } from 'zod'; -import { Thread } from '../entities'; +import { ContestManager, Thread, Topic } from '../entities'; +import * as projections from '../projections'; import { DiscordMetaSchema, + PG_INT, linksSchema, paginationSchema, - PG_INT, } from '../utils'; export const OrderByQueriesKeys = z.enum([ @@ -139,3 +140,20 @@ export const GetThreads = { threads: z.array(MappedThread), }), }; + +export const ActiveContestManager = ContestManager.extend({ + content: z.array(projections.ContestAction), +}); + +export const ExtendedTopic = Topic.extend({ + total_threads: z.number(), + active_contest_managers: z.array(ActiveContestManager), +}); + +export const GetTopics = { + input: z.object({ + community_id: z.string(), + with_contest_managers: z.boolean().optional(), + }), + output: z.array(ExtendedTopic), +}; diff --git a/packages/commonwealth/client/scripts/models/Topic.ts b/packages/commonwealth/client/scripts/models/Topic.ts index b449b939e8a..3c1fbc15773 100644 --- a/packages/commonwealth/client/scripts/models/Topic.ts +++ b/packages/commonwealth/client/scripts/models/Topic.ts @@ -1,82 +1,50 @@ import * as schemas from '@hicommonwealth/schemas'; import { z } from 'zod'; -const ActiveContestManagers = z.object({ - content: z.array(schemas.ContestAction), - contest_manager: schemas.ContestManager, -}); +export type TopicAttributes = z.infer; -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 chainNodeId: TopicAttributes['chain_node_id']; - 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({ +function Topic({ + name, + id, + description, + telegram, + community_id, + featured_in_new_post, + featured_in_sidebar, + order, + default_offchain_template, + total_threads, + channel_id, + active_contest_managers, + chain_node_id, + group_ids, + default_offchain_template_backup, + weighted_voting, + token_address, + token_symbol, + vote_weight_multiplier, +}: TopicAttributes) { + return { name, id, description, telegram, - community_id, - featured_in_sidebar, - featured_in_new_post, + communityId: community_id, + featuredInSidebar: featured_in_sidebar, + featuredInNewPost: featured_in_new_post, order, - default_offchain_template, - total_threads, - channel_id, - active_contest_managers, - chain_node_id, - 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.chainNodeId = chain_node_id; - 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; - } + defaultOffchainTemplate: default_offchain_template, + totalThreads: total_threads || 0, + channelId: channel_id, + activeContestManagers: active_contest_managers || [], + chainNodeId: chain_node_id, + groupIds: group_ids, + defaultOffchainTemplateBackup: default_offchain_template_backup, + weightedVoting: weighted_voting, + tokenAddress: token_address, + tokenSymbol: token_symbol, + voteWeightMultiplier: vote_weight_multiplier, + }; } export default Topic; 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/topics/fetchTopics.ts b/packages/commonwealth/client/scripts/state/api/topics/fetchTopics.ts index bd5ea38d7e6..fd215277f71 100644 --- a/packages/commonwealth/client/scripts/state/api/topics/fetchTopics.ts +++ b/packages/commonwealth/client/scripts/state/api/topics/fetchTopics.ts @@ -1,8 +1,5 @@ -import { useQuery } from '@tanstack/react-query'; -import axios from 'axios'; +import { trpc } from 'client/scripts/utils/trpcClient'; import Topic from 'models/Topic'; -import app from 'state'; -import { ApiEndpoints, SERVER_URL } from 'state/api/config'; const TOPICS_STALE_TIME = 30 * 1_000; // 30 s @@ -12,31 +9,23 @@ 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.thread.getTopics.useQuery( + { + community_id: communityId, + with_contest_managers: includeContestData, + }, + { + staleTime: TOPICS_STALE_TIME, + enabled: apiEnabled, + // @ts-expect-error + select: (data) => data.map((topic) => Topic(topic)), + }, + ); }; export default useFetchTopicsQuery; diff --git a/packages/commonwealth/server/api/external-router.ts b/packages/commonwealth/server/api/external-router.ts index 742475cc0da..859c16462af 100644 --- a/packages/commonwealth/server/api/external-router.ts +++ b/packages/commonwealth/server/api/external-router.ts @@ -1,5 +1,5 @@ import { express, trpc } from '@hicommonwealth/adapters'; -import { Comment, Community, Feed } from '@hicommonwealth/model'; +import { Comment, Community, Feed, Thread } from '@hicommonwealth/model'; import cors from 'cors'; import { Router } from 'express'; import passport from 'passport'; @@ -56,6 +56,9 @@ const api = { getComments: trpc.query(Comment.GetComments, trpc.Tag.Comment, { forceSecure: true, }), + getTopics: trpc.query(Thread.GetTopics, trpc.Tag.Thread, { + forceSecure: true, + }), createCommunity, updateCommunity, createTopic, diff --git a/packages/commonwealth/server/api/threads.ts b/packages/commonwealth/server/api/threads.ts index cee71742a6a..a02f6159d69 100644 --- a/packages/commonwealth/server/api/threads.ts +++ b/packages/commonwealth/server/api/threads.ts @@ -5,6 +5,7 @@ import { MixpanelCommunityInteractionEvent } from '../../shared/analytics/types' import { applyCanvasSignedDataMiddleware } from '../federation'; export const trpcRouter = trpc.router({ + getTopics: trpc.query(Thread.GetTopics, trpc.Tag.Thread), createThread: trpc.command( Thread.CreateThread, trpc.Tag.Thread, 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/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 4766ea0e53f..4daab4c6e96 100644 --- a/packages/commonwealth/server/routing/router.ts +++ b/packages/commonwealth/server/routing/router.ts @@ -99,7 +99,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'; @@ -389,13 +388,6 @@ function setupRouter( databaseValidationService.validateCommunity, updateTopicsOrderHandler.bind(this, serverControllers), ); - registerRoute( - router, - 'get', - '/topics' /* OLD: /bulkTopics */, - databaseValidationService.validateCommunity, - getTopicsHandler.bind(this, serverControllers), - ); // reactions registerRoute( From 2b36e96f451b3c1972b226b50b7b4a0b41cf138c Mon Sep 17 00:00:00 2001 From: rotorsoft Date: Thu, 17 Oct 2024 17:30:53 -0400 Subject: [PATCH 02/10] fix topics --- .../commonwealth/client/scripts/models/Thread.ts | 2 +- .../commonwealth/client/scripts/models/Topic.ts | 16 +++++++++++----- .../scripts/state/api/topics/fetchTopics.ts | 4 ++-- .../client/scripts/views/components/feed.tsx | 4 ++-- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/commonwealth/client/scripts/models/Thread.ts b/packages/commonwealth/client/scripts/models/Thread.ts index 89f9fce5c3d..671b3587bfe 100644 --- a/packages/commonwealth/client/scripts/models/Thread.ts +++ b/packages/commonwealth/client/scripts/models/Thread.ts @@ -376,7 +376,7 @@ export class Thread implements IUniqueId { 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?.id ? mapTopic({ ...(topic || {}) } as any) : null; 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 3c1fbc15773..e040d853c39 100644 --- a/packages/commonwealth/client/scripts/models/Topic.ts +++ b/packages/commonwealth/client/scripts/models/Topic.ts @@ -1,9 +1,9 @@ import * as schemas from '@hicommonwealth/schemas'; import { z } from 'zod'; -export type TopicAttributes = z.infer; +export type Topic = z.infer; -function Topic({ +export function mapTopic({ name, id, description, @@ -23,16 +23,22 @@ function Topic({ token_address, token_symbol, vote_weight_multiplier, -}: TopicAttributes) { +}: Topic) { return { - name, + community_id, id, + name, description, telegram, + featured_in_sidebar, + featured_in_new_post, + order, + group_ids, + total_threads, + active_contest_managers, communityId: community_id, featuredInSidebar: featured_in_sidebar, featuredInNewPost: featured_in_new_post, - order, defaultOffchainTemplate: default_offchain_template, totalThreads: total_threads || 0, channelId: channel_id, diff --git a/packages/commonwealth/client/scripts/state/api/topics/fetchTopics.ts b/packages/commonwealth/client/scripts/state/api/topics/fetchTopics.ts index fd215277f71..876deb2dabf 100644 --- a/packages/commonwealth/client/scripts/state/api/topics/fetchTopics.ts +++ b/packages/commonwealth/client/scripts/state/api/topics/fetchTopics.ts @@ -1,5 +1,5 @@ import { trpc } from 'client/scripts/utils/trpcClient'; -import Topic from 'models/Topic'; +import { mapTopic } from 'models/Topic'; const TOPICS_STALE_TIME = 30 * 1_000; // 30 s @@ -23,7 +23,7 @@ const useFetchTopicsQuery = ({ staleTime: TOPICS_STALE_TIME, enabled: apiEnabled, // @ts-expect-error - select: (data) => data.map((topic) => Topic(topic)), + select: (data) => data.map((topic) => mapTopic(topic)), }, ); }; diff --git a/packages/commonwealth/client/scripts/views/components/feed.tsx b/packages/commonwealth/client/scripts/views/components/feed.tsx index 776f5d17891..6598f6f953d 100644 --- a/packages/commonwealth/client/scripts/views/components/feed.tsx +++ b/packages/commonwealth/client/scripts/views/components/feed.tsx @@ -12,11 +12,11 @@ import { PermissionEnum, } from '@hicommonwealth/schemas'; import { slugify } from '@hicommonwealth/shared'; +import { mapTopic } from 'client/scripts/models/Topic'; 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,7 +132,7 @@ 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: mapTopic({ community_id: thread.community_id, id: thread.topic.id, name: thread.topic.name, From 763d23ffcb5159499f16cfe2fb62c67426508576 Mon Sep 17 00:00:00 2001 From: rotorsoft Date: Fri, 18 Oct 2024 09:07:20 -0400 Subject: [PATCH 03/10] move query to community --- .../{thread => community}/GetTopics.query.ts | 0 libs/model/src/community/index.ts | 1 + libs/model/src/thread/index.ts | 1 - .../community/community-lifecycle.spec.ts | 35 +++++++++++++++---- .../server/api/external-router.ts | 4 +-- packages/commonwealth/server/api/threads.ts | 4 +-- .../server_topics_controller.spec.ts | 10 ------ 7 files changed, 33 insertions(+), 22 deletions(-) rename libs/model/src/{thread => community}/GetTopics.query.ts (100%) diff --git a/libs/model/src/thread/GetTopics.query.ts b/libs/model/src/community/GetTopics.query.ts similarity index 100% rename from libs/model/src/thread/GetTopics.query.ts rename to libs/model/src/community/GetTopics.query.ts diff --git a/libs/model/src/community/index.ts b/libs/model/src/community/index.ts index 7a99078703b..4afb6e3587c 100644 --- a/libs/model/src/community/index.ts +++ b/libs/model/src/community/index.ts @@ -12,6 +12,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/index.ts b/libs/model/src/thread/index.ts index 72877341b38..b29d9c54a60 100644 --- a/libs/model/src/thread/index.ts +++ b/libs/model/src/thread/index.ts @@ -3,5 +3,4 @@ export * from './CreateThreadReaction.command'; export * from './DeleteThread.command'; export * from './GetThread.query'; export * from './GetThreads.query'; -export * from './GetTopics.query'; export * from './UpdateThread.command'; diff --git a/libs/model/test/community/community-lifecycle.spec.ts b/libs/model/test/community/community-lifecycle.spec.ts index 0e16c5f3c6f..a1fb60a465a 100644 --- a/libs/model/test/community/community-lifecycle.spec.ts +++ b/libs/model/test/community/community-lifecycle.spec.ts @@ -7,10 +7,7 @@ import { dispose, query, } from '@hicommonwealth/core'; -import { - PermissionEnum, - TopicWeightedVoting, -} from '@hicommonwealth/schemas'; +import { PermissionEnum, TopicWeightedVoting } from '@hicommonwealth/schemas'; import { ChainBase, ChainType } from '@hicommonwealth/shared'; import { Chance } from 'chance'; import { CreateTopic } from 'model/src/community/CreateTopic.command'; @@ -25,6 +22,7 @@ import { DeleteTopic, GetCommunities, GetMembers, + GetTopics, JoinCommunity, JoinCommunityErrors, MAX_GROUPS_PER_COMMUNITY, @@ -364,15 +362,30 @@ describe('Community lifecycle', () => { payload: buildCreateGroupPayload(community.id, [ { id: 1, - permissions: [PermissionEnum.CREATE_COMMENT, PermissionEnum.CREATE_THREAD, PermissionEnum.CREATE_COMMENT_REACTION,PermissionEnum.CREATE_THREAD_REACTION], + permissions: [ + PermissionEnum.CREATE_COMMENT, + PermissionEnum.CREATE_THREAD, + PermissionEnum.CREATE_COMMENT_REACTION, + PermissionEnum.CREATE_THREAD_REACTION, + ], }, { id: 2, - permissions: [PermissionEnum.CREATE_COMMENT, PermissionEnum.CREATE_THREAD, PermissionEnum.CREATE_COMMENT_REACTION,PermissionEnum.CREATE_THREAD_REACTION], + permissions: [ + PermissionEnum.CREATE_COMMENT, + PermissionEnum.CREATE_THREAD, + PermissionEnum.CREATE_COMMENT_REACTION, + PermissionEnum.CREATE_THREAD_REACTION, + ], }, { id: 3, - permissions: [PermissionEnum.CREATE_COMMENT, PermissionEnum.CREATE_THREAD, PermissionEnum.CREATE_COMMENT_REACTION,PermissionEnum.CREATE_THREAD_REACTION], + permissions: [ + PermissionEnum.CREATE_COMMENT, + PermissionEnum.CREATE_THREAD, + PermissionEnum.CREATE_COMMENT_REACTION, + PermissionEnum.CREATE_THREAD_REACTION, + ], }, ]), }), @@ -566,6 +579,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/packages/commonwealth/server/api/external-router.ts b/packages/commonwealth/server/api/external-router.ts index 859c16462af..ff34c976dfb 100644 --- a/packages/commonwealth/server/api/external-router.ts +++ b/packages/commonwealth/server/api/external-router.ts @@ -1,5 +1,5 @@ import { express, trpc } from '@hicommonwealth/adapters'; -import { Comment, Community, Feed, Thread } from '@hicommonwealth/model'; +import { Comment, Community, Feed } from '@hicommonwealth/model'; import cors from 'cors'; import { Router } from 'express'; import passport from 'passport'; @@ -56,7 +56,7 @@ const api = { getComments: trpc.query(Comment.GetComments, trpc.Tag.Comment, { forceSecure: true, }), - getTopics: trpc.query(Thread.GetTopics, trpc.Tag.Thread, { + getTopics: trpc.query(Community.GetTopics, trpc.Tag.Community, { forceSecure: true, }), createCommunity, diff --git a/packages/commonwealth/server/api/threads.ts b/packages/commonwealth/server/api/threads.ts index a02f6159d69..2e41f7fce2f 100644 --- a/packages/commonwealth/server/api/threads.ts +++ b/packages/commonwealth/server/api/threads.ts @@ -1,11 +1,11 @@ import { trpc } from '@hicommonwealth/adapters'; import { CacheNamespaces, cache } from '@hicommonwealth/core'; -import { Reaction, Thread } from '@hicommonwealth/model'; +import { Community, Reaction, Thread } from '@hicommonwealth/model'; import { MixpanelCommunityInteractionEvent } from '../../shared/analytics/types'; import { applyCanvasSignedDataMiddleware } from '../federation'; export const trpcRouter = trpc.router({ - getTopics: trpc.query(Thread.GetTopics, trpc.Tag.Thread), + getTopics: trpc.query(Community.GetTopics, trpc.Tag.Community), createThread: trpc.command( Thread.CreateThread, trpc.Tag.Thread, 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({ From b6d08941c5181a6a92b880c7885d7e5d0b917c09 Mon Sep 17 00:00:00 2001 From: rotorsoft Date: Fri, 18 Oct 2024 10:32:28 -0400 Subject: [PATCH 04/10] remove client Topic state type and use TopicView from schemas --- libs/model/src/community/GetTopics.query.ts | 44 ++++++++++----- libs/schemas/src/queries/thread.schemas.ts | 21 ++++++-- .../client/scripts/models/AbridgedThread.ts | 2 +- .../client/scripts/models/Thread.ts | 5 +- .../client/scripts/models/Topic.ts | 54 +------------------ .../client/scripts/state/api/config.ts | 1 + .../client/scripts/state/api/feeds/util.ts | 2 +- .../scripts/state/api/threads/createThread.ts | 2 +- .../state/api/threads/helpers/cache.ts | 2 +- .../scripts/state/api/topics/fetchTopics.ts | 3 -- .../api/topics/updateFeaturedTopicsOrder.ts | 4 +- .../NewThreadFormLegacy/CustomTopicOption.tsx | 4 +- .../NewThreadFormLegacy/NewThreadForm.tsx | 23 ++++---- .../helpers/useNewThreadForm.ts | 6 +-- .../components/NewThreadFormLegacy/types.ts | 2 +- .../NewThreadFormModern/CustomTopicOption.tsx | 4 +- .../NewThreadFormModern/NewThreadForm.tsx | 13 ++--- .../helpers/useNewThreadForm.ts | 6 +-- .../components/NewThreadFormModern/types.ts | 2 +- .../client/scripts/views/components/feed.tsx | 6 +-- .../components/sidebar/discussion_section.tsx | 6 +-- .../views/components/topic_selector.tsx | 2 +- .../modals/change_thread_topic_modal.tsx | 2 +- .../scripts/views/modals/edit_topic_modal.tsx | 4 +- .../draggable_topics_list.tsx | 6 ++- .../AdminContestsPage/AdminContestsPage.tsx | 2 +- .../steps/DetailsFormStep/DetailsFormStep.tsx | 8 +-- .../Integrations/Discord/Discord.tsx | 2 +- .../ManageTopicsSection.tsx | 6 +-- .../ManageTopicsSectionOld.tsx | 6 +-- .../pages/discussions/DiscussionsPage.tsx | 8 +-- .../HeaderWithFilters/HeaderWithFilters.tsx | 6 +-- .../views/pages/overview/TopicSummaryRow.tsx | 4 +- .../scripts/views/pages/overview/index.tsx | 6 +-- 34 files changed, 128 insertions(+), 146 deletions(-) diff --git a/libs/model/src/community/GetTopics.query.ts b/libs/model/src/community/GetTopics.query.ts index e7ba93fc607..55bdd4177bb 100644 --- a/libs/model/src/community/GetTopics.query.ts +++ b/libs/model/src/community/GetTopics.query.ts @@ -53,24 +53,44 @@ export function GetTopics(): Query { const sql = ` 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 + SELECT + id, + name, + community_id, + description, + telegram, + featured_in_sidebar, + featured_in_new_post, + default_offchain_template, + "order", + channel_id, + group_ids, + weighted_voting, + chain_node_id, + 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 ) ${base} `; - return await models.sequelize.query< - z.infer - >(sql, { - replacements: { community_id }, - type: QueryTypes.SELECT, - raw: true, - }); + return await models.sequelize.query>( + sql, + { + replacements: { community_id }, + type: QueryTypes.SELECT, + raw: true, + }, + ); }, }; } diff --git a/libs/schemas/src/queries/thread.schemas.ts b/libs/schemas/src/queries/thread.schemas.ts index 08e37805293..13516b36391 100644 --- a/libs/schemas/src/queries/thread.schemas.ts +++ b/libs/schemas/src/queries/thread.schemas.ts @@ -141,13 +141,24 @@ export const GetThreads = { }), }; -export const ActiveContestManager = ContestManager.extend({ - content: z.array(projections.ContestAction), +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 ExtendedTopic = Topic.extend({ +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(ActiveContestManager), + active_contest_managers: z.array(ConstestManagerView), }); export const GetTopics = { @@ -155,5 +166,5 @@ export const GetTopics = { community_id: z.string(), with_contest_managers: z.boolean().optional(), }), - output: z.array(ExtendedTopic), + output: z.array(TopicView), }; 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 671b3587bfe..530fad1c189 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 ? mapTopic({ ...(topic || {}) } as any) : null; + this.topic = topic?.id ? ({ ...(topic || {}) } as any) : null; 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 e040d853c39..15dc3022fd3 100644 --- a/packages/commonwealth/client/scripts/models/Topic.ts +++ b/packages/commonwealth/client/scripts/models/Topic.ts @@ -1,56 +1,4 @@ import * as schemas from '@hicommonwealth/schemas'; import { z } from 'zod'; -export type Topic = z.infer; - -export function mapTopic({ - name, - id, - description, - telegram, - community_id, - featured_in_new_post, - featured_in_sidebar, - order, - default_offchain_template, - total_threads, - channel_id, - active_contest_managers, - chain_node_id, - group_ids, - default_offchain_template_backup, - weighted_voting, - token_address, - token_symbol, - vote_weight_multiplier, -}: Topic) { - return { - community_id, - id, - name, - description, - telegram, - featured_in_sidebar, - featured_in_new_post, - order, - group_ids, - total_threads, - active_contest_managers, - communityId: community_id, - featuredInSidebar: featured_in_sidebar, - featuredInNewPost: featured_in_new_post, - defaultOffchainTemplate: default_offchain_template, - totalThreads: total_threads || 0, - channelId: channel_id, - activeContestManagers: active_contest_managers || [], - chainNodeId: chain_node_id, - groupIds: group_ids, - defaultOffchainTemplateBackup: default_offchain_template_backup, - weightedVoting: weighted_voting, - tokenAddress: token_address, - tokenSymbol: token_symbol, - 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 54b4c56dc9e..5a022eabb66 100644 --- a/packages/commonwealth/client/scripts/state/api/config.ts +++ b/packages/commonwealth/client/scripts/state/api/config.ts @@ -12,6 +12,7 @@ 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 b1f8470e7cf..88e18855cff 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/fetchTopics.ts b/packages/commonwealth/client/scripts/state/api/topics/fetchTopics.ts index 876deb2dabf..17840bf6a36 100644 --- a/packages/commonwealth/client/scripts/state/api/topics/fetchTopics.ts +++ b/packages/commonwealth/client/scripts/state/api/topics/fetchTopics.ts @@ -1,5 +1,4 @@ import { trpc } from 'client/scripts/utils/trpcClient'; -import { mapTopic } from 'models/Topic'; const TOPICS_STALE_TIME = 30 * 1_000; // 30 s @@ -22,8 +21,6 @@ const useFetchTopicsQuery = ({ { staleTime: TOPICS_STALE_TIME, enabled: apiEnabled, - // @ts-expect-error - select: (data) => data.map((topic) => mapTopic(topic)), }, ); }; diff --git a/packages/commonwealth/client/scripts/state/api/topics/updateFeaturedTopicsOrder.ts b/packages/commonwealth/client/scripts/state/api/topics/updateFeaturedTopicsOrder.ts index 4ae70c3e304..7c516ffa104 100644 --- a/packages/commonwealth/client/scripts/state/api/topics/updateFeaturedTopicsOrder.ts +++ b/packages/commonwealth/client/scripts/state/api/topics/updateFeaturedTopicsOrder.ts @@ -1,6 +1,6 @@ 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 { userStore } from '../../ui/user'; @@ -28,7 +28,7 @@ const useUpdateFeaturedTopicsOrderMutation = () => { return useMutation({ mutationFn: updateFeaturedTopicsOrder, onSuccess: async (data, variables) => { - const communityId = variables.featuredTopics[0].communityId; + const communityId = variables.featuredTopics[0].community_id; await queryClient.invalidateQueries({ queryKey: [ApiEndpoints.BULK_TOPICS, communityId], }); 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 6598f6f953d..e627d72146f 100644 --- a/packages/commonwealth/client/scripts/views/components/feed.tsx +++ b/packages/commonwealth/client/scripts/views/components/feed.tsx @@ -12,7 +12,6 @@ import { PermissionEnum, } from '@hicommonwealth/schemas'; import { slugify } from '@hicommonwealth/shared'; -import { mapTopic } from 'client/scripts/models/Topic'; import { getThreadActionTooltipText } from 'helpers/threads'; import useTopicGating from 'hooks/useTopicGating'; import { getProposalUrlPath } from 'identifiers'; @@ -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: mapTopic({ + 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 787ca182d90..5a5e8e4331b 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 7dd9ce51fe3..a94e3e237e0 100644 --- a/packages/commonwealth/client/scripts/views/pages/discussions/HeaderWithFilters/HeaderWithFilters.tsx +++ b/packages/commonwealth/client/scripts/views/pages/discussions/HeaderWithFilters/HeaderWithFilters.tsx @@ -22,7 +22,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, @@ -119,13 +119,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 From 63af2b69600889671e5afbd51c82d91b93356855 Mon Sep 17 00:00:00 2001 From: rotorsoft Date: Fri, 18 Oct 2024 10:40:36 -0400 Subject: [PATCH 05/10] fix lint --- libs/schemas/src/queries/community.schemas.ts | 31 +++++++++++++++++++ libs/schemas/src/queries/thread.schemas.ts | 31 +------------------ .../client/scripts/models/Thread.ts | 2 +- 3 files changed, 33 insertions(+), 31 deletions(-) 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 13516b36391..05576d58451 100644 --- a/libs/schemas/src/queries/thread.schemas.ts +++ b/libs/schemas/src/queries/thread.schemas.ts @@ -1,6 +1,5 @@ import { z } from 'zod'; -import { ContestManager, Thread, Topic } from '../entities'; -import * as projections from '../projections'; +import { Thread } from '../entities'; import { DiscordMetaSchema, PG_INT, @@ -140,31 +139,3 @@ export const GetThreads = { threads: z.array(MappedThread), }), }; - -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/packages/commonwealth/client/scripts/models/Thread.ts b/packages/commonwealth/client/scripts/models/Thread.ts index 530fad1c189..a274367168a 100644 --- a/packages/commonwealth/client/scripts/models/Thread.ts +++ b/packages/commonwealth/client/scripts/models/Thread.ts @@ -375,7 +375,7 @@ export class Thread implements IUniqueId { this.identifier = `${id}`; this.createdAt = moment(created_at); this.updatedAt = moment(updated_at); - this.topic = topic?.id ? ({ ...(topic || {}) } as any) : null; + this.topic = { ...topic }; this.kind = kind; this.stage = stage; this.authorCommunity = Address?.community_id; From 933ed5f4e906c371997504ddae065f6ca85ce954 Mon Sep 17 00:00:00 2001 From: rotorsoft Date: Fri, 18 Oct 2024 11:02:02 -0400 Subject: [PATCH 06/10] remove test --- .../test/integration/api/topics.spec.ts | 42 ------------------- 1 file changed, 42 deletions(-) delete mode 100644 packages/commonwealth/test/integration/api/topics.spec.ts 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); - }); - }); -}); From 5b3bb0d6d123e8942eecddbdf8fdfb8bbaddc350 Mon Sep 17 00:00:00 2001 From: rotorsoft Date: Fri, 18 Oct 2024 12:30:01 -0400 Subject: [PATCH 07/10] fix query --- libs/model/src/community/GetTopics.query.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/model/src/community/GetTopics.query.ts b/libs/model/src/community/GetTopics.query.ts index 55bdd4177bb..8d75f605f50 100644 --- a/libs/model/src/community/GetTopics.query.ts +++ b/libs/model/src/community/GetTopics.query.ts @@ -66,7 +66,6 @@ export function GetTopics(): Query { channel_id, group_ids, weighted_voting, - chain_node_id, token_symbol, vote_weight_multiplier, created_at::text as created_at, From e77d5b78f5dee44cfbf5e8cd5216f997e5ca75c9 Mon Sep 17 00:00:00 2001 From: rotorsoft Date: Fri, 18 Oct 2024 14:59:51 -0400 Subject: [PATCH 08/10] fix query --- libs/model/src/community/GetTopics.query.ts | 142 +++++++++++--------- 1 file changed, 77 insertions(+), 65 deletions(-) diff --git a/libs/model/src/community/GetTopics.query.ts b/libs/model/src/community/GetTopics.query.ts index 8d75f605f50..c06237ab18f 100644 --- a/libs/model/src/community/GetTopics.query.ts +++ b/libs/model/src/community/GetTopics.query.ts @@ -12,74 +12,86 @@ export function GetTopics(): Query { body: async ({ payload }) => { const { community_id, with_contest_managers } = payload; - const base = with_contest_managers - ? `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 - ` + 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 -- Filtering by topic_id + AND cm.community_id = :community_id -- Filter by community + AND cm.cancelled = 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 - ) - ${base} +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>( From 79a75cfd20eb3fe94feaac45c6a48cd1f53d7f0d Mon Sep 17 00:00:00 2001 From: rotorsoft Date: Fri, 18 Oct 2024 15:28:01 -0400 Subject: [PATCH 09/10] add coalesce to cancelled --- libs/model/src/community/GetTopics.query.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/model/src/community/GetTopics.query.ts b/libs/model/src/community/GetTopics.query.ts index c06237ab18f..324e2302c16 100644 --- a/libs/model/src/community/GetTopics.query.ts +++ b/libs/model/src/community/GetTopics.query.ts @@ -44,9 +44,9 @@ SELECT GROUP BY contest_address) co ON cm.contest_address = co.contest_address WHERE - ct.topic_id = td.id -- Filtering by topic_id - AND cm.community_id = :community_id -- Filter by community - AND cm.cancelled = FALSE -- Exclude cancelled managers + 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 From 79a5417ac5d56e24f102ec27945f286cf1e54639 Mon Sep 17 00:00:00 2001 From: rotorsoft Date: Mon, 21 Oct 2024 09:49:03 -0400 Subject: [PATCH 10/10] fix invalidate, move getTopics to community router --- .../commonwealth/client/scripts/state/api/config.ts | 1 - .../client/scripts/state/api/topics/createTopic.ts | 8 ++++---- .../client/scripts/state/api/topics/deleteTopic.ts | 8 ++++---- .../client/scripts/state/api/topics/editTopic.ts | 10 +++++----- .../client/scripts/state/api/topics/fetchTopics.ts | 2 +- .../state/api/topics/updateFeaturedTopicsOrder.ts | 11 ++++++----- packages/commonwealth/server/api/community.ts | 1 + packages/commonwealth/server/api/threads.ts | 3 +-- 8 files changed, 22 insertions(+), 22 deletions(-) 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/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 17840bf6a36..bdc91f4a6d0 100644 --- a/packages/commonwealth/client/scripts/state/api/topics/fetchTopics.ts +++ b/packages/commonwealth/client/scripts/state/api/topics/fetchTopics.ts @@ -13,7 +13,7 @@ const useFetchTopicsQuery = ({ apiEnabled = true, includeContestData, }: FetchTopicsProps) => { - return trpc.thread.getTopics.useQuery( + return trpc.community.getTopics.useQuery( { community_id: communityId, with_contest_managers: includeContestData, diff --git a/packages/commonwealth/client/scripts/state/api/topics/updateFeaturedTopicsOrder.ts b/packages/commonwealth/client/scripts/state/api/topics/updateFeaturedTopicsOrder.ts index 7c516ffa104..fbe5ff3148c 100644 --- a/packages/commonwealth/client/scripts/state/api/topics/updateFeaturedTopicsOrder.ts +++ b/packages/commonwealth/client/scripts/state/api/topics/updateFeaturedTopicsOrder.ts @@ -2,7 +2,8 @@ import { useMutation } from '@tanstack/react-query'; import axios from 'axios'; 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].community_id; - 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/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/threads.ts b/packages/commonwealth/server/api/threads.ts index 2e41f7fce2f..cee71742a6a 100644 --- a/packages/commonwealth/server/api/threads.ts +++ b/packages/commonwealth/server/api/threads.ts @@ -1,11 +1,10 @@ import { trpc } from '@hicommonwealth/adapters'; import { CacheNamespaces, cache } from '@hicommonwealth/core'; -import { Community, Reaction, Thread } from '@hicommonwealth/model'; +import { Reaction, Thread } from '@hicommonwealth/model'; import { MixpanelCommunityInteractionEvent } from '../../shared/analytics/types'; import { applyCanvasSignedDataMiddleware } from '../federation'; export const trpcRouter = trpc.router({ - getTopics: trpc.query(Community.GetTopics, trpc.Tag.Community), createThread: trpc.command( Thread.CreateThread, trpc.Tag.Thread,