diff --git a/app/http/endpoints/api/guilds.go b/app/http/endpoints/api/guilds.go index cfac950..cc9d336 100644 --- a/app/http/endpoints/api/guilds.go +++ b/app/http/endpoints/api/guilds.go @@ -1,19 +1,21 @@ package api import ( + "cmp" "context" - "errors" dbclient "github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/rpc/cache" "github.com/TicketsBot/GoPanel/utils" + "github.com/TicketsBot/common/collections" "github.com/TicketsBot/common/permission" syncutils "github.com/TicketsBot/common/utils" "github.com/TicketsBot/database" "github.com/gin-gonic/gin" "github.com/jackc/pgtype" - "github.com/rxdn/gdl/rest/request" "golang.org/x/sync/errgroup" - "sort" + "slices" + "sync" + "time" ) type wrappedGuild struct { @@ -23,101 +25,101 @@ type wrappedGuild struct { PermissionLevel permission.PermissionLevel `json:"permission_level"` } -func GetGuilds(ctx *gin.Context) { - userId := ctx.Keys["userid"].(uint64) +func GetGuilds(c *gin.Context) { + userId := c.Keys["userid"].(uint64) - // Get all guilds the user is in - guilds, err := dbclient.Client.UserGuilds.Get(userId) + // Get the guilds that the user is in, that the bot is also in + userGuilds, err := getGuildIntersection(userId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) - return - } - - // Get the subset of guilds that the user is in that the bot is also in - guildIds := make([]uint64, len(guilds)) - guildMap := make(map[uint64]database.UserGuild) // Make a map of all guilds for O(1) access - for i, guild := range guilds { - guildIds[i] = guild.GuildId - guildMap[guild.GuildId] = guild - } - - botGuilds, err := getExistingGuilds(guildIds) - if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + c.JSON(500, utils.ErrorJson(err)) return } wg := syncutils.NewChannelWaitGroup() - wg.Add(len(botGuilds)) + wg.Add(len(userGuilds)) - group, _ := errgroup.WithContext(context.Background()) - ch := make(chan wrappedGuild) - for _, guildId := range botGuilds { - guildId := guildId - g := guildMap[guildId] + ctx, cancel := context.WithTimeout(c, time.Second*10) + defer cancel() + + group, ctx := errgroup.WithContext(ctx) + + var mu sync.Mutex + guilds := make([]wrappedGuild, 0, len(userGuilds)) + for _, guild := range userGuilds { + guild := guild group.Go(func() error { defer wg.Done() - // Determine the user's permission level in this guild - var permLevel permission.PermissionLevel - if g.Owner { - permLevel = permission.Admin - } else { - tmp, err := utils.GetPermissionLevel(context.Background(), g.GuildId, userId) - if err != nil { - // If a Discord error occurs, just skip the server - var restError request.RestError - if errors.As(err, &restError) { - return nil - } else { - return err - } - } - - permLevel = tmp + permLevel, err := utils.GetPermissionLevel(ctx, guild.GuildId, userId) + if err != nil { + return err } - if permLevel >= permission.Support { - wrapped := wrappedGuild{ - Id: g.GuildId, - Name: g.Name, - Icon: g.Icon, - PermissionLevel: permLevel, - } - - ch <- wrapped - } + mu.Lock() + guilds = append(guilds, wrappedGuild{ + Id: guild.GuildId, + Name: guild.Name, + Icon: guild.Icon, + PermissionLevel: permLevel, + }) + mu.Unlock() return nil }) } - adminGuilds := make([]wrappedGuild, 0) - group.Go(func() error { - loop: - for { - select { - case <-wg.Wait(): - break loop - case guild := <-ch: - adminGuilds = append(adminGuilds, guild) - } - } - return nil - }) - if err := group.Wait(); err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + c.JSON(500, utils.ErrorJson(err)) return } - // sort - sort.Slice(adminGuilds, func(i, j int) bool { - return adminGuilds[i].Name < adminGuilds[j].Name + // Sort the guilds by name, but put the guilds with permission_level=0 last + slices.SortFunc(guilds, func(a, b wrappedGuild) int { + if a.PermissionLevel == 0 && b.PermissionLevel > 0 { + return 1 + } else if a.PermissionLevel > 0 && b.PermissionLevel == 0 { + return -1 + } + + return cmp.Compare(a.Name, b.Name) }) - ctx.JSON(200, adminGuilds) + c.JSON(200, guilds) +} + +func getGuildIntersection(userId uint64) ([]database.UserGuild, error) { + // Get all the guilds that the user is in + userGuilds, err := dbclient.Client.UserGuilds.Get(userId) + if err != nil { + return nil, err + } + + guildIds := make([]uint64, len(userGuilds)) + for i, guild := range userGuilds { + guildIds[i] = guild.GuildId + } + + // Restrict the set of guilds to guilds that the bot is also in + botGuilds, err := getExistingGuilds(guildIds) + if err != nil { + return nil, err + } + + botGuildIds := collections.NewSet[uint64]() + for _, guildId := range botGuilds { + botGuildIds.Add(guildId) + } + + // Get the intersection of the two sets + intersection := make([]database.UserGuild, 0, len(botGuilds)) + for _, guild := range userGuilds { + if botGuildIds.Contains(guild.GuildId) { + intersection = append(intersection, guild) + } + } + + return intersection, nil } func getExistingGuilds(userGuilds []uint64) ([]uint64, error) { diff --git a/frontend/src/components/Guild.svelte b/frontend/src/components/Guild.svelte index 9a38aa4..fc79ca4 100644 --- a/frontend/src/components/Guild.svelte +++ b/frontend/src/components/Guild.svelte @@ -1,23 +1,30 @@ -
-
- {#if guild.icon === undefined || guild.icon === ""} - - {:else} - Guild Icon - {/if} -
+
+
+ {#if guild.icon === undefined || guild.icon === ""} + + {:else} + Guild Icon + {/if} +
-
- - {guild.name} +
+ + {guild.name} + + 0}> + No permission + + + + + -
+
@@ -70,6 +69,10 @@ cursor: pointer; } + .guild-badge.disabled { + cursor: default; + } + @media (max-width: 950px) { :global(.guild-badge) { width: 100%; @@ -102,6 +105,27 @@ :global(.guild-name) { color: white !important; + } + + .text-wrapper.disabled > .guild-name { + opacity: 45%; + } + + .guild-icon-bg > *.disabled { + opacity: 25%; + } + + .text-wrapper { + display: flex; + flex-direction: column; padding-left: 10px; } + + .text-wrapper > .no-permission { + opacity: 75%; + } + + .text-wrapper > .no-permission.disabled { + visibility: hidden; + } diff --git a/frontend/src/components/InviteBadge.svelte b/frontend/src/components/InviteBadge.svelte index 92e6c43..a2de6e2 100644 --- a/frontend/src/components/InviteBadge.svelte +++ b/frontend/src/components/InviteBadge.svelte @@ -3,7 +3,7 @@
-
+
Invite to your server