From 96e5d555cf5a09b50c9590148405402246f9b4bc Mon Sep 17 00:00:00 2001 From: Satont Date: Sun, 5 May 2024 14:03:03 +0300 Subject: [PATCH] feat: add top users to emote statistic --- .../resolvers/emotes-statistic.resolver.go | 89 +++++++++++++----- apps/api-gql/schema/emotes-statistic.graphqls | 12 ++- .../dashboard/src/api/emotes-statistic.ts | 12 ++- ...y-emotes-details-content-users-history.vue | 36 ++++++++ ...nity-emotes-details-content-users-top.vue} | 14 +-- .../community-emotes-details-content.vue | 33 ++++++- ...community-emotes-details-users-history.ts} | 18 ++-- .../use-community-emotes-details-users-top.ts | 92 +++++++++++++++++++ .../use-community-emotes-details.ts | 12 ++- frontend/dashboard/src/locales/en.json | 7 +- 10 files changed, 276 insertions(+), 49 deletions(-) create mode 100644 frontend/dashboard/src/features/community-emotes-statistic/components/community-emotes-details-content-users-history.vue rename frontend/dashboard/src/features/community-emotes-statistic/components/{community-emotes-details-content-users.vue => community-emotes-details-content-users-top.vue} (67%) rename frontend/dashboard/src/features/community-emotes-statistic/composables/{use-community-emotes-details-users.ts => use-community-emotes-details-users-history.ts} (84%) create mode 100644 frontend/dashboard/src/features/community-emotes-statistic/composables/use-community-emotes-details-users-top.ts diff --git a/apps/api-gql/internal/gql/resolvers/emotes-statistic.resolver.go b/apps/api-gql/internal/gql/resolvers/emotes-statistic.resolver.go index 95652370b..9cf26e88c 100644 --- a/apps/api-gql/internal/gql/resolvers/emotes-statistic.resolver.go +++ b/apps/api-gql/internal/gql/resolvers/emotes-statistic.resolver.go @@ -15,18 +15,17 @@ import ( ) // TwitchProfile is the resolver for the twitchProfile field. -func (r *emoteStatisticUserUsageResolver) TwitchProfile( - ctx context.Context, - obj *gqlmodel.EmoteStatisticUserUsage, -) (*gqlmodel.TwirUserTwitchInfo, error) { +func (r *emoteStatisticTopUserResolver) TwitchProfile(ctx context.Context, obj *gqlmodel.EmoteStatisticTopUser) (*gqlmodel.TwirUserTwitchInfo, error) { + return data_loader.GetHelixUserById(ctx, obj.UserID) +} + +// TwitchProfile is the resolver for the twitchProfile field. +func (r *emoteStatisticUserUsageResolver) TwitchProfile(ctx context.Context, obj *gqlmodel.EmoteStatisticUserUsage) (*gqlmodel.TwirUserTwitchInfo, error) { return data_loader.GetHelixUserById(ctx, obj.UserID) } // EmotesStatistics is the resolver for the emotesStatistics field. -func (r *queryResolver) EmotesStatistics( - ctx context.Context, - opts gqlmodel.EmotesStatisticsOpts, -) (*gqlmodel.EmotesStatisticResponse, error) { +func (r *queryResolver) EmotesStatistics(ctx context.Context, opts gqlmodel.EmotesStatisticsOpts) (*gqlmodel.EmotesStatisticResponse, error) { dashboardId, err := r.sessions.GetSelectedDashboard(ctx) if err != nil { return nil, err @@ -129,10 +128,7 @@ func (r *queryResolver) EmotesStatistics( } // EmotesStatisticEmoteDetailedInformation is the resolver for the emotesStatisticEmoteDetailedInformation field. -func (r *queryResolver) EmotesStatisticEmoteDetailedInformation( - ctx context.Context, - opts gqlmodel.EmotesStatisticEmoteDetailedOpts, -) (*gqlmodel.EmotesStatisticEmoteDetailedResponse, error) { +func (r *queryResolver) EmotesStatisticEmoteDetailedInformation(ctx context.Context, opts gqlmodel.EmotesStatisticEmoteDetailedOpts) (*gqlmodel.EmotesStatisticEmoteDetailedResponse, error) { if opts.EmoteName == "" { return nil, nil } @@ -167,26 +163,23 @@ func (r *queryResolver) EmotesStatisticEmoteDetailedInformation( var usagedByUserPage int usagesByUserPerPage := 10 - if opts.UsagesByUsersPage.IsSet() { usagedByUserPage = *opts.UsagesByUsersPage.Value() } - if opts.UsagesByUsersPerPage.IsSet() { usagesByUserPerPage = *opts.UsagesByUsersPerPage.Value() } - var usagesByUsers []model.ChannelEmoteUsage + var usagesHistoryEntities []model.ChannelEmoteUsage if err := r.gorm. WithContext(ctx). Where(`"channelId" = ? AND "emote" = ?`, dashboardId, opts.EmoteName). Order(`"createdAt" DESC`). Limit(usagesByUserPerPage). Offset(usagedByUserPage * usagesByUserPerPage). - Find(&usagesByUsers).Error; err != nil { + Find(&usagesHistoryEntities).Error; err != nil { return nil, err } - var usagesByUsersTotalCount int64 if err := r.gorm. WithContext(ctx). @@ -196,29 +189,81 @@ func (r *queryResolver) EmotesStatisticEmoteDetailedInformation( return nil, err } - users := make([]gqlmodel.EmoteStatisticUserUsage, 0, len(usagesByUsers)) - for _, usage := range usagesByUsers { - users = append( - users, gqlmodel.EmoteStatisticUserUsage{ + var topUsersPage int + topUsersPerPage := 10 + if opts.TopUsersPage.IsSet() { + topUsersPage = *opts.TopUsersPage.Value() + } + if opts.TopUsersPerPage.IsSet() { + topUsersPerPage = *opts.TopUsersPerPage.Value() + } + + var topUsersEntities []emoteEntityModelWithCount + if err := r.gorm. + WithContext(ctx). + Where(`"channelId" = ? AND "emote" = ?`, dashboardId, opts.EmoteName). + Select(`"userId", COUNT("userId") as count`). + Group("userId"). + Order("count DESC"). + Limit(topUsersPerPage). + Offset(topUsersPage * topUsersPerPage). + Find(&topUsersEntities).Error; err != nil { + return nil, err + } + + var topUsersTotalCount int64 + if err := r.gorm. + WithContext(ctx). + Model(&model.ChannelEmoteUsage{}). + Where(`"channelId" = ? AND "emote" = ?`, dashboardId, opts.EmoteName). + Group(`"userId"`). + Count(&topUsersTotalCount).Error; err != nil { + return nil, err + } + + usagesHistory := make([]gqlmodel.EmoteStatisticUserUsage, 0, len(usagesHistoryEntities)) + for _, usage := range usagesHistoryEntities { + usagesHistory = append( + usagesHistory, + gqlmodel.EmoteStatisticUserUsage{ UserID: usage.UserID, Date: usage.CreatedAt, }, ) } + topUsers := make([]gqlmodel.EmoteStatisticTopUser, 0, len(topUsersEntities)) + for _, user := range topUsersEntities { + topUsers = append( + topUsers, + gqlmodel.EmoteStatisticTopUser{ + UserID: user.UserID, + Count: user.Count, + }, + ) + } + return &gqlmodel.EmotesStatisticEmoteDetailedResponse{ EmoteName: opts.EmoteName, TotalUsages: int(usages), LastUsedTimestamp: int(lastUsedEntity.CreatedAt.UTC().UnixMilli()), GraphicUsages: graphicUsages, - UsagesByUsers: users, + UsagesHistory: usagesHistory, UsagesByUsersTotal: int(usagesByUsersTotalCount), + TopUsers: topUsers, + TopUsersTotal: int(topUsersTotalCount), }, nil } +// EmoteStatisticTopUser returns graph.EmoteStatisticTopUserResolver implementation. +func (r *Resolver) EmoteStatisticTopUser() graph.EmoteStatisticTopUserResolver { + return &emoteStatisticTopUserResolver{r} +} + // EmoteStatisticUserUsage returns graph.EmoteStatisticUserUsageResolver implementation. func (r *Resolver) EmoteStatisticUserUsage() graph.EmoteStatisticUserUsageResolver { return &emoteStatisticUserUsageResolver{r} } +type emoteStatisticTopUserResolver struct{ *Resolver } type emoteStatisticUserUsageResolver struct{ *Resolver } diff --git a/apps/api-gql/schema/emotes-statistic.graphqls b/apps/api-gql/schema/emotes-statistic.graphqls index f68ff0633..b5acb1331 100644 --- a/apps/api-gql/schema/emotes-statistic.graphqls +++ b/apps/api-gql/schema/emotes-statistic.graphqls @@ -41,6 +41,8 @@ input EmotesStatisticEmoteDetailedOpts { range: EmoteStatisticRange! usagesByUsersPage: Int usagesByUsersPerPage: Int + topUsersPage: Int + topUsersPerPage: Int } type EmotesStatisticEmoteDetailedResponse { @@ -48,8 +50,10 @@ type EmotesStatisticEmoteDetailedResponse { totalUsages: Int! lastUsedTimestamp: Int! graphicUsages: [EmoteStatisticUsage!]! - usagesByUsers: [EmoteStatisticUserUsage!]! + usagesHistory: [EmoteStatisticUserUsage!]! usagesByUsersTotal: Int! + topUsers: [EmoteStatisticTopUser!]! + topUsersTotal: Int! } type EmoteStatisticUsage { @@ -62,3 +66,9 @@ type EmoteStatisticUserUsage { twitchProfile: TwirUserTwitchInfo! @goField(forceResolver: true) date: Time! } + +type EmoteStatisticTopUser { + userId: String! + twitchProfile: TwirUserTwitchInfo! @goField(forceResolver: true) + count: Int! +} diff --git a/frontend/dashboard/src/api/emotes-statistic.ts b/frontend/dashboard/src/api/emotes-statistic.ts index 5687f5c85..296a3c61a 100644 --- a/frontend/dashboard/src/api/emotes-statistic.ts +++ b/frontend/dashboard/src/api/emotes-statistic.ts @@ -54,7 +54,7 @@ export function useEmotesStatisticDetailsQuery(opts: Ref +import { NScrollbar } from 'naive-ui' +import { storeToRefs } from 'pinia' + +import Pagination from '@/components/pagination.vue' +import Table from '@/components/table.vue' +import { + useCommunityEmotesDetails, +} from '@/features/community-emotes-statistic/composables/use-community-emotes-details' +import { + useCommunityEmotesDetailsUsersHistory, +} from '@/features/community-emotes-statistic/composables/use-community-emotes-details-users-history' + +const { isLoading, usagesPagination } = storeToRefs(useCommunityEmotesDetails()) +const users = useCommunityEmotesDetailsUsersHistory() + + + diff --git a/frontend/dashboard/src/features/community-emotes-statistic/components/community-emotes-details-content-users.vue b/frontend/dashboard/src/features/community-emotes-statistic/components/community-emotes-details-content-users-top.vue similarity index 67% rename from frontend/dashboard/src/features/community-emotes-statistic/components/community-emotes-details-content-users.vue rename to frontend/dashboard/src/features/community-emotes-statistic/components/community-emotes-details-content-users-top.vue index a988d1743..2c0bed18c 100644 --- a/frontend/dashboard/src/features/community-emotes-statistic/components/community-emotes-details-content-users.vue +++ b/frontend/dashboard/src/features/community-emotes-statistic/components/community-emotes-details-content-users-top.vue @@ -8,11 +8,11 @@ import { useCommunityEmotesDetails, } from '@/features/community-emotes-statistic/composables/use-community-emotes-details' import { - useCommunityEmotesDetailsUsers, -} from '@/features/community-emotes-statistic/composables/use-community-emotes-details-users' + useCommunityEmotesDetailsUsersTop, +} from '@/features/community-emotes-statistic/composables/use-community-emotes-details-users-top' -const { isLoading } = storeToRefs(useCommunityEmotesDetails()) -const users = useCommunityEmotesDetailsUsers() +const { isLoading, topPagination } = storeToRefs(useCommunityEmotesDetails()) +const users = useCommunityEmotesDetailsUsersTop()