Skip to content

Commit

Permalink
feat: add hideOnLandingPage user setting
Browse files Browse the repository at this point in the history
  • Loading branch information
Satont committed Dec 6, 2023
1 parent d9c8d07 commit 9e95659
Show file tree
Hide file tree
Showing 25 changed files with 429 additions and 64 deletions.
16 changes: 16 additions & 0 deletions apps/api/internal/helpers/get_dashboard_id.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package helpers

import (
"context"
"fmt"
)

func GetSelectedDashboardIDFromCtx(ctx context.Context) (string, error) {
dashboardId, ok := ctx.Value("dashboardId").(string)

if !ok {
return "", fmt.Errorf("failed to get dashboardId from context")
}

return dashboardId, nil
}
17 changes: 17 additions & 0 deletions apps/api/internal/helpers/get_user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package helpers

import (
"context"
"fmt"

model "github.com/satont/twir/libs/gomodels"
)

func GetUserModelFromCtx(ctx context.Context) (model.Users, error) {
user, ok := ctx.Value("dbUser").(model.Users)
if !ok {
return model.Users{}, fmt.Errorf("failed to get user from context")
}

return user, nil
}
8 changes: 7 additions & 1 deletion apps/api/internal/impl_protected/auth/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/nicklaw5/helix/v2"
"github.com/samber/lo"
"github.com/satont/twir/apps/api/internal/helpers"
"github.com/satont/twir/apps/api/internal/impl_deps"
model "github.com/satont/twir/libs/gomodels"
"github.com/satont/twir/libs/grpc/generated/api/auth"
Expand All @@ -17,7 +18,11 @@ type Auth struct {
}

func (c *Auth) AuthUserProfile(ctx context.Context, _ *emptypb.Empty) (*auth.Profile, error) {
dbUser := c.SessionManager.Get(ctx, "dbUser").(model.Users)
dbUser, err := helpers.GetUserModelFromCtx(ctx)
if err != nil {
return nil, fmt.Errorf("cannot get user model from ctx: %w", err)
}

twitchUser := c.SessionManager.Get(ctx, "twitchUser").(helix.User)
selectedDashboardId := c.SessionManager.Get(ctx, "dashboardId").(string)

Expand Down Expand Up @@ -46,6 +51,7 @@ func (c *Auth) AuthUserProfile(ctx context.Context, _ *emptypb.Empty) (*auth.Pro
ApiKey: dbUser.ApiKey,
IsBotAdmin: dbUser.IsBotAdmin,
SelectedDashboardId: selectedDashboardId,
HideOnLandingPage: dbUser.HideOnLandingPage,
}, nil
}

Expand Down
3 changes: 3 additions & 0 deletions apps/api/internal/impl_protected/impl_protected.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/satont/twir/apps/api/internal/impl_protected/roles"
"github.com/satont/twir/apps/api/internal/impl_protected/timers"
"github.com/satont/twir/apps/api/internal/impl_protected/twitch"
"github.com/satont/twir/apps/api/internal/impl_protected/users"
"github.com/satont/twir/apps/api/internal/impl_protected/variables"
config "github.com/satont/twir/libs/config"
"github.com/satont/twir/libs/grpc/generated/bots"
Expand Down Expand Up @@ -63,6 +64,7 @@ type Protected struct {
*games.Games
*overlays.Overlays
*moderation.Moderation
*users.Users
}

type Opts struct {
Expand Down Expand Up @@ -126,5 +128,6 @@ func New(opts Opts) *Protected {
Games: &games.Games{Deps: d},
Overlays: &overlays.Overlays{Deps: d},
Moderation: &moderation.Moderation{Deps: d},
Users: &users.Users{Deps: d},
}
}
59 changes: 59 additions & 0 deletions apps/api/internal/impl_protected/users/users.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package users

import (
"context"
"fmt"

"github.com/google/uuid"
"github.com/satont/twir/apps/api/internal/helpers"
"github.com/satont/twir/apps/api/internal/impl_deps"
"github.com/satont/twir/libs/grpc/generated/api/users"
"google.golang.org/protobuf/types/known/emptypb"
)

type Users struct {
*impl_deps.Deps
}

func (c *Users) UsersRegenerateApiKey(
ctx context.Context,
req *users.RegenerateApiKeyRequest,
) (*emptypb.Empty, error) {
user, err := helpers.GetUserModelFromCtx(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get user from context: %w", err)
}

user.ApiKey = uuid.New().String()

if err := c.Db.Save(&user).Error; err != nil {
return nil, fmt.Errorf("failed to save user: %w", err)
}

return &emptypb.Empty{}, nil
}

func (c *Users) UsersUpdate(ctx context.Context, req *users.UpdateUserRequest) (
*emptypb.Empty,
error,
) {
user, err := helpers.GetUserModelFromCtx(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get user from context: %w", err)
}

query := c.Db.WithContext(ctx).Model(&user)

// everything bellow working like a PATCH http request
if req.HideOnLandingPage != nil {
user.HideOnLandingPage = *req.HideOnLandingPage
}

if err := query.Save(&user).Error; err != nil {
return nil, fmt.Errorf("failed to update user: %w", err)
}

c.SessionManager.Put(ctx, "dbUser", user)

return &emptypb.Empty{}, nil
}
30 changes: 22 additions & 8 deletions apps/api/internal/impl_unprotected/stats/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,17 +107,25 @@ func (c *Stats) cacheCounts() {

func (c *Stats) cacheStreamers() {
c.Logger.Info("Updating streamers cache in stats")
var streamers []string
if err := c.Db.Model(&model.Channels{}).Where(
`"isEnabled" = ? AND "isTwitchBanned" = ? AND "isBanned" = ?`,
true,
false,
false,
).Pluck("id", &streamers).Error; err != nil {
var streamers []model.Channels
if err := c.Db.Model(&model.Channels{}).
Preload("User").
Where(
`"isEnabled" = ? AND "isTwitchBanned" = ? AND "isBanned" = ?`,
true,
false,
false,
).Find(&streamers).Error; err != nil {
c.Logger.Error("cannot cache streamers", slog.Any("err", err))
return
}

streamers = lo.Filter(
streamers, func(item model.Channels, _ int) bool {
return item.User != nil && !item.User.HideOnLandingPage
},
)

helixUsersMu := sync.Mutex{}
helixUsers := make([]helix.User, 0, len(streamers))

Expand All @@ -134,9 +142,15 @@ func (c *Stats) cacheStreamers() {
chunk := chunk
errGroup.Go(
func() error {
usersIds := lo.Map(
chunk, func(item model.Channels, _ int) string {
return item.ID
},
)

usersReq, usersErr := twitchClient.GetUsers(
&helix.UsersParams{
IDs: chunk,
IDs: usersIds,
},
)
if usersErr != nil {
Expand Down
3 changes: 0 additions & 3 deletions frontend/dashboard/src/api/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ export const useLogout = () => useMutation({
mutationFn: async () => {
await protectedApiClient.authLogout({});
},
onSuccess: () => {
window.location.replace('/');
},
});


Expand Down
1 change: 1 addition & 0 deletions frontend/dashboard/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ export * from './files.js';
export * from './games/index.js';
export * from './googleFonts.js';
export * from './moderation.js';
export * from './user.js';
32 changes: 32 additions & 0 deletions frontend/dashboard/src/api/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useMutation, useQueryClient } from '@tanstack/vue-query';
import type { UpdateUserRequest } from '@twir/grpc/generated/api/api/users';

import { profileQueryOptions } from '@/api/auth';
import { protectedApiClient } from '@/api/twirp';

export const useUser = () => {
const queryClient = useQueryClient();

return {
useRegenerateApiKey: () => useMutation({
mutationKey: ['userRegenerateApiKey'],
mutationFn: async () => {
const call = await protectedApiClient.usersRegenerateApiKey({});
return call.response;
},
async onSuccess() {
await queryClient.invalidateQueries(profileQueryOptions.queryKey);
},
}),
useUpdate: () => useMutation({
mutationKey: ['userUpdate'],
mutationFn: async (data: UpdateUserRequest) => {
const call = await protectedApiClient.usersUpdate(data);
return call.response;
},
async onSuccess() {
await queryClient.invalidateQueries(profileQueryOptions.queryKey);
},
}),
};
};
61 changes: 61 additions & 0 deletions frontend/dashboard/src/layout/dropdowns/dropdownProfile.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<script setup lang="ts">
import { type DropdownOption, NAvatar, NButton, NDropdown, NSpin } from 'naive-ui';
import { h } from 'vue';
import DropdownBody from './profile/body.vue';
import DropdownFooter from './profile/footer.vue';
import DropdownHeader from './profile/header.vue';
import { useProfile } from '@/api';
const { data: profileData, isLoading: isProfileLoading } = useProfile();
const profileOptions: DropdownOption[] = [
{
key: 'header',
type: 'render',
render: () => h(DropdownHeader),
},
{
key: 'header-divider',
type: 'divider',
},
{
key: 'body',
type: 'render',
render: () => h(DropdownBody),
},
{
key: 'body-divider',
type: 'divider',
},
{
key: 'footer',
type: 'render',
render: () => h(DropdownFooter),
},
];
</script>

<template>
<n-dropdown trigger="click" :options="profileOptions" size="large" style="width: 400px;">
<n-button text>
<n-spin v-if="isProfileLoading" size="small" />
<div v-else class="profile">
<n-avatar
size="small"
:src="profileData?.avatar"
round
/>
</div>
</n-button>
</n-dropdown>
</template>

<style scoped>
.profile {
display: flex;
gap: 5px;
align-items: center;
}
</style>
46 changes: 0 additions & 46 deletions frontend/dashboard/src/layout/dropdowns/dropdownProfileOptions.vue

This file was deleted.

Loading

0 comments on commit 9e95659

Please sign in to comment.