feat(dashboard): ignore messages page, remove ignore channel page
This commit is contained in:
parent
0cac964aa6
commit
19d9f33454
18 changed files with 575 additions and 315 deletions
|
|
@ -13,18 +13,15 @@
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using System.Net;
|
|
||||||
using Catalogger.Backend.Api.Middleware;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Remora.Discord.API;
|
|
||||||
using Remora.Discord.API.Abstractions.Objects;
|
using Remora.Discord.API.Abstractions.Objects;
|
||||||
|
|
||||||
namespace Catalogger.Backend.Api;
|
namespace Catalogger.Backend.Api;
|
||||||
|
|
||||||
public partial class GuildsController
|
public partial class GuildsController
|
||||||
{
|
{
|
||||||
[HttpPut("ignored-channels/{channelId}")]
|
[HttpPut("ignored-messages/channels/{channelId}")]
|
||||||
public async Task<IActionResult> AddIgnoredChannelAsync(string id, ulong channelId)
|
public async Task<IActionResult> AddIgnoredMessageChannelAsync(string id, ulong channelId)
|
||||||
{
|
{
|
||||||
var (guildId, _) = await ParseGuildAsync(id);
|
var (guildId, _) = await ParseGuildAsync(id);
|
||||||
var guildConfig = await guildRepository.GetAsync(guildId);
|
var guildConfig = await guildRepository.GetAsync(guildId);
|
||||||
|
|
@ -53,8 +50,8 @@ public partial class GuildsController
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("ignored-channels/{channelId}")]
|
[HttpDelete("ignored-messages/channels/{channelId}")]
|
||||||
public async Task<IActionResult> RemoveIgnoredChannelAsync(string id, ulong channelId)
|
public async Task<IActionResult> RemoveIgnoredMessageChannelAsync(string id, ulong channelId)
|
||||||
{
|
{
|
||||||
var (guildId, _) = await ParseGuildAsync(id);
|
var (guildId, _) = await ParseGuildAsync(id);
|
||||||
var guildConfig = await guildRepository.GetAsync(guildId);
|
var guildConfig = await guildRepository.GetAsync(guildId);
|
||||||
|
|
@ -65,40 +62,109 @@ public partial class GuildsController
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPut("key-roles/{roleId}")]
|
[HttpPut("ignored-messages/roles/{roleId}")]
|
||||||
public async Task<IActionResult> AddKeyRoleAsync(string id, ulong roleId)
|
public async Task<IActionResult> AddIgnoredMessageRoleAsync(string id, ulong roleId)
|
||||||
{
|
{
|
||||||
var (guildId, _) = await ParseGuildAsync(id);
|
var (guildId, _) = await ParseGuildAsync(id);
|
||||||
var guildConfig = await guildRepository.GetAsync(guildId);
|
var guildConfig = await guildRepository.GetAsync(guildId);
|
||||||
|
|
||||||
|
if (guildConfig.Messages.IgnoredRoles.Contains(roleId))
|
||||||
|
return NoContent();
|
||||||
|
|
||||||
if (roleCache.GuildRoles(guildId).All(r => r.ID.Value != roleId))
|
if (roleCache.GuildRoles(guildId).All(r => r.ID.Value != roleId))
|
||||||
throw new ApiError(HttpStatusCode.BadRequest, ErrorCode.BadRequest, "Role not found");
|
return NoContent();
|
||||||
|
|
||||||
if (guildConfig.KeyRoles.Contains(roleId))
|
guildConfig.Messages.IgnoredRoles.Add(roleId);
|
||||||
throw new ApiError(
|
await guildRepository.UpdateChannelConfigAsync(guildId, guildConfig);
|
||||||
HttpStatusCode.BadRequest,
|
|
||||||
ErrorCode.BadRequest,
|
|
||||||
"Role is already a key role"
|
|
||||||
);
|
|
||||||
|
|
||||||
await guildRepository.AddKeyRoleAsync(guildId, DiscordSnowflake.New(roleId));
|
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("key-roles/{roleId}")]
|
[HttpDelete("ignored-messages/roles/{roleId}")]
|
||||||
public async Task<IActionResult> RemoveKeyRoleAsync(string id, ulong roleId)
|
public async Task<IActionResult> RemoveIgnoredMessageRoleAsync(string id, ulong roleId)
|
||||||
{
|
{
|
||||||
var (guildId, _) = await ParseGuildAsync(id);
|
var (guildId, _) = await ParseGuildAsync(id);
|
||||||
var guildConfig = await guildRepository.GetAsync(guildId);
|
var guildConfig = await guildRepository.GetAsync(guildId);
|
||||||
|
|
||||||
if (!guildConfig.KeyRoles.Contains(roleId))
|
guildConfig.Messages.IgnoredRoles.Remove(roleId);
|
||||||
throw new ApiError(
|
await guildRepository.UpdateChannelConfigAsync(guildId, guildConfig);
|
||||||
HttpStatusCode.BadRequest,
|
|
||||||
ErrorCode.BadRequest,
|
return NoContent();
|
||||||
"Role is already not a key role"
|
}
|
||||||
);
|
|
||||||
|
[HttpPut("ignored-entities/channels/{channelId}")]
|
||||||
|
public async Task<IActionResult> AddIgnoredEntityChannelAsync(string id, ulong channelId)
|
||||||
|
{
|
||||||
|
var (guildId, _) = await ParseGuildAsync(id);
|
||||||
|
var guildConfig = await guildRepository.GetAsync(guildId);
|
||||||
|
|
||||||
|
if (guildConfig.IgnoredChannels.Contains(channelId))
|
||||||
|
return NoContent();
|
||||||
|
|
||||||
|
var channel = channelCache
|
||||||
|
.GuildChannels(guildId)
|
||||||
|
.FirstOrDefault(c =>
|
||||||
|
c.ID.Value == channelId
|
||||||
|
&& c.Type
|
||||||
|
is ChannelType.GuildText
|
||||||
|
or ChannelType.GuildCategory
|
||||||
|
or ChannelType.GuildAnnouncement
|
||||||
|
or ChannelType.GuildForum
|
||||||
|
or ChannelType.GuildMedia
|
||||||
|
or ChannelType.GuildVoice
|
||||||
|
);
|
||||||
|
if (channel == null)
|
||||||
|
return NoContent();
|
||||||
|
|
||||||
|
guildConfig.IgnoredChannels = [.. guildConfig.IgnoredChannels, channelId];
|
||||||
|
await guildRepository.UpdateChannelConfigAsync(guildId, guildConfig);
|
||||||
|
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("ignored-entities/channels/{channelId}")]
|
||||||
|
public async Task<IActionResult> RemoveIgnoredEntityChannelAsync(string id, ulong channelId)
|
||||||
|
{
|
||||||
|
var (guildId, _) = await ParseGuildAsync(id);
|
||||||
|
var guildConfig = await guildRepository.GetAsync(guildId);
|
||||||
|
|
||||||
|
var channels = guildConfig.IgnoredChannels.ToList();
|
||||||
|
channels.Remove(channelId);
|
||||||
|
guildConfig.IgnoredChannels = channels.ToArray();
|
||||||
|
await guildRepository.UpdateChannelConfigAsync(guildId, guildConfig);
|
||||||
|
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("ignored-entities/roles/{roleId}")]
|
||||||
|
public async Task<IActionResult> AddIgnoredEntityRoleAsync(string id, ulong roleId)
|
||||||
|
{
|
||||||
|
var (guildId, _) = await ParseGuildAsync(id);
|
||||||
|
var guildConfig = await guildRepository.GetAsync(guildId);
|
||||||
|
|
||||||
|
if (guildConfig.Messages.IgnoredRoles.Contains(roleId))
|
||||||
|
return NoContent();
|
||||||
|
|
||||||
|
if (roleCache.GuildRoles(guildId).All(r => r.ID.Value != roleId))
|
||||||
|
return NoContent();
|
||||||
|
|
||||||
|
guildConfig.IgnoredRoles = [.. guildConfig.IgnoredRoles, roleId];
|
||||||
|
await guildRepository.UpdateChannelConfigAsync(guildId, guildConfig);
|
||||||
|
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("ignored-entities/roles/{roleId}")]
|
||||||
|
public async Task<IActionResult> RemoveIgnoredEntityRoleAsync(string id, ulong roleId)
|
||||||
|
{
|
||||||
|
var (guildId, _) = await ParseGuildAsync(id);
|
||||||
|
var guildConfig = await guildRepository.GetAsync(guildId);
|
||||||
|
|
||||||
|
var roles = guildConfig.IgnoredRoles.ToList();
|
||||||
|
roles.Remove(roleId);
|
||||||
|
guildConfig.IgnoredRoles = roles.ToArray();
|
||||||
|
await guildRepository.UpdateChannelConfigAsync(guildId, guildConfig);
|
||||||
|
|
||||||
await guildRepository.RemoveKeyRoleAsync(guildId, DiscordSnowflake.New(roleId));
|
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
61
Catalogger.Backend/Api/GuildsController.KeyRoles.cs
Normal file
61
Catalogger.Backend/Api/GuildsController.KeyRoles.cs
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
// Copyright (C) 2021-present sam (starshines.gay)
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published
|
||||||
|
// by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
using System.Net;
|
||||||
|
using Catalogger.Backend.Api.Middleware;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Remora.Discord.API;
|
||||||
|
|
||||||
|
namespace Catalogger.Backend.Api;
|
||||||
|
|
||||||
|
public partial class GuildsController
|
||||||
|
{
|
||||||
|
[HttpPut("key-roles/{roleId}")]
|
||||||
|
public async Task<IActionResult> AddKeyRoleAsync(string id, ulong roleId)
|
||||||
|
{
|
||||||
|
var (guildId, _) = await ParseGuildAsync(id);
|
||||||
|
var guildConfig = await guildRepository.GetAsync(guildId);
|
||||||
|
|
||||||
|
if (roleCache.GuildRoles(guildId).All(r => r.ID.Value != roleId))
|
||||||
|
throw new ApiError(HttpStatusCode.BadRequest, ErrorCode.BadRequest, "Role not found");
|
||||||
|
|
||||||
|
if (guildConfig.KeyRoles.Contains(roleId))
|
||||||
|
throw new ApiError(
|
||||||
|
HttpStatusCode.BadRequest,
|
||||||
|
ErrorCode.BadRequest,
|
||||||
|
"Role is already a key role"
|
||||||
|
);
|
||||||
|
|
||||||
|
await guildRepository.AddKeyRoleAsync(guildId, DiscordSnowflake.New(roleId));
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("key-roles/{roleId}")]
|
||||||
|
public async Task<IActionResult> RemoveKeyRoleAsync(string id, ulong roleId)
|
||||||
|
{
|
||||||
|
var (guildId, _) = await ParseGuildAsync(id);
|
||||||
|
var guildConfig = await guildRepository.GetAsync(guildId);
|
||||||
|
|
||||||
|
if (!guildConfig.KeyRoles.Contains(roleId))
|
||||||
|
throw new ApiError(
|
||||||
|
HttpStatusCode.BadRequest,
|
||||||
|
ErrorCode.BadRequest,
|
||||||
|
"Role is already not a key role"
|
||||||
|
);
|
||||||
|
|
||||||
|
await guildRepository.RemoveKeyRoleAsync(guildId, DiscordSnowflake.New(roleId));
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -111,6 +111,9 @@ public partial class GuildsController(
|
||||||
categories,
|
categories,
|
||||||
channelsWithoutCategories,
|
channelsWithoutCategories,
|
||||||
roles,
|
roles,
|
||||||
|
guildConfig.IgnoredChannels,
|
||||||
|
guildConfig.IgnoredRoles,
|
||||||
|
guildConfig.Messages,
|
||||||
guildConfig.Channels,
|
guildConfig.Channels,
|
||||||
guildConfig.KeyRoles
|
guildConfig.KeyRoles
|
||||||
)
|
)
|
||||||
|
|
@ -137,7 +140,10 @@ public partial class GuildsController(
|
||||||
IEnumerable<GuildCategory> Categories,
|
IEnumerable<GuildCategory> Categories,
|
||||||
IEnumerable<GuildChannel> ChannelsWithoutCategory,
|
IEnumerable<GuildChannel> ChannelsWithoutCategory,
|
||||||
IEnumerable<GuildRole> Roles,
|
IEnumerable<GuildRole> Roles,
|
||||||
Database.Models.Guild.ChannelConfig Config,
|
ulong[] IgnoredChannels,
|
||||||
|
ulong[] IgnoredRoles,
|
||||||
|
Database.Models.Guild.MessageConfig Messages,
|
||||||
|
Database.Models.Guild.ChannelConfig Channels,
|
||||||
ulong[] KeyRoles
|
ulong[] KeyRoles
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-static": "^3.0.5",
|
"@sveltejs/adapter-static": "^3.0.5",
|
||||||
"@sveltejs/kit": "^2.0.0",
|
"@sveltejs/kit": "^2.0.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "3",
|
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||||
"@sveltestrap/sveltestrap": "^6.2.7",
|
"@sveltestrap/sveltestrap": "^6.2.7",
|
||||||
"@types/eslint": "^9.6.0",
|
"@types/eslint": "^9.6.0",
|
||||||
"@types/file-saver": "^2.0.7",
|
"@types/file-saver": "^2.0.7",
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
"sass": "^1.80.1",
|
"sass": "^1.80.1",
|
||||||
"snarkdown": "^2.0.0",
|
"snarkdown": "^2.0.0",
|
||||||
"svelecte": "^4.3.1",
|
"svelecte": "^4.3.1",
|
||||||
"svelte": "4",
|
"svelte": "^4.2.7",
|
||||||
"svelte-check": "^4.0.0",
|
"svelte-check": "^4.0.0",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^5.0.0",
|
||||||
"typescript-eslint": "^8.0.0",
|
"typescript-eslint": "^8.0.0",
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,10 @@ export type FullGuild = {
|
||||||
categories: GuildCategory[];
|
categories: GuildCategory[];
|
||||||
channels_without_category: GuildChannel[];
|
channels_without_category: GuildChannel[];
|
||||||
roles: GuildRole[];
|
roles: GuildRole[];
|
||||||
config: GuildConfig;
|
ignored_channels: string[];
|
||||||
|
ignored_roles: string[];
|
||||||
|
messages: MessageConfig;
|
||||||
|
channels: ChannelConfig;
|
||||||
key_roles: string[];
|
key_roles: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -114,14 +117,15 @@ export type ApiError = {
|
||||||
message: string;
|
message: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GuildConfig = GuildChannelConfig & {
|
export type MessageConfig = {
|
||||||
ignored_channels: string[];
|
ignored_channels: string[];
|
||||||
ignored_users: string[];
|
ignored_users: string[];
|
||||||
|
ignored_roles: string[];
|
||||||
ignored_users_per_channel: Record<string, string[]>;
|
ignored_users_per_channel: Record<string, string[]>;
|
||||||
redirects: Record<string, string>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GuildChannelConfig = {
|
export type ChannelConfig = {
|
||||||
|
redirects: Record<string, string>;
|
||||||
guild_update: string;
|
guild_update: string;
|
||||||
guild_emojis_update: string;
|
guild_emojis_update: string;
|
||||||
guild_role_create: string;
|
guild_role_create: string;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Button, ListGroupItem } from "@sveltestrap/sveltestrap";
|
||||||
|
|
||||||
|
export let buttonText = "Remove";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ListGroupItem class="d-flex justify-content-between align-items-center">
|
||||||
|
<span>
|
||||||
|
<slot />
|
||||||
|
</span>
|
||||||
|
<Button color="link" on:click>{buttonText}</Button>
|
||||||
|
</ListGroupItem>
|
||||||
18
Catalogger.Frontend/src/lib/components/RoleListItem.svelte
Normal file
18
Catalogger.Frontend/src/lib/components/RoleListItem.svelte
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { GuildRole } from "$lib/api";
|
||||||
|
import BaseListItem from "./RemovableListItem.svelte";
|
||||||
|
|
||||||
|
export let buttonText: string | undefined = undefined;
|
||||||
|
export let role: GuildRole | string;
|
||||||
|
|
||||||
|
const colour = (c: string) => (c === "#000000" ? "current-color" : c);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<BaseListItem on:click {buttonText}>
|
||||||
|
{#if typeof role === "string"}
|
||||||
|
(unknown role {role})
|
||||||
|
{:else}
|
||||||
|
<span style="color: {colour(role.colour)};">●</span>
|
||||||
|
{role.name}
|
||||||
|
{/if}
|
||||||
|
</BaseListItem>
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import { Button, Nav, NavItem, NavLink } from "@sveltestrap/sveltestrap";
|
import { Button, Nav, NavItem, NavLink } from "@sveltestrap/sveltestrap";
|
||||||
import type { LayoutData } from "./$types";
|
import type { LayoutData } from "./$types";
|
||||||
import { page } from "$app/stores";
|
import { page } from "$app/stores";
|
||||||
import apiFetch, { type ApiError, type GuildConfig } from "$lib/api";
|
import apiFetch, { type ApiError, type ChannelConfig } from "$lib/api";
|
||||||
import { addToast } from "$lib/toast";
|
import { addToast } from "$lib/toast";
|
||||||
|
|
||||||
export let data: LayoutData;
|
export let data: LayoutData;
|
||||||
|
|
@ -10,16 +10,16 @@
|
||||||
// This only saves log channels. All other pages are lists and are saved immediately upon adding/removing an entry.
|
// This only saves log channels. All other pages are lists and are saved immediately upon adding/removing an entry.
|
||||||
const save = async () => {
|
const save = async () => {
|
||||||
try {
|
try {
|
||||||
const resp = await apiFetch<GuildConfig>(
|
const resp = await apiFetch<ChannelConfig>(
|
||||||
"PATCH",
|
"PATCH",
|
||||||
`/api/guilds/${data.guild.id}`,
|
`/api/guilds/${data.guild.id}`,
|
||||||
data.guild.config,
|
data.guild.channels,
|
||||||
);
|
);
|
||||||
data.guild.config = resp;
|
data.guild.channels = resp;
|
||||||
|
|
||||||
addToast({
|
addToast({
|
||||||
header: "Saved log channels.",
|
header: "Saved log channels.",
|
||||||
body: "Successfully edited log channels and ignored channels.",
|
body: "Successfully edited log channels.",
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
addToast({
|
addToast({
|
||||||
|
|
@ -51,16 +51,10 @@
|
||||||
Redirects
|
Redirects
|
||||||
</NavLink>
|
</NavLink>
|
||||||
<NavLink
|
<NavLink
|
||||||
href="/dash/{data.guild.id}/ignored-channels"
|
href="/dash/{data.guild.id}/ignored-messages"
|
||||||
active={$page.url.pathname === `/dash/${data.guild.id}/ignored-channels`}
|
active={$page.url.pathname === `/dash/${data.guild.id}/ignored-messages`}
|
||||||
>
|
>
|
||||||
Ignored channels
|
Ignored messages
|
||||||
</NavLink>
|
|
||||||
<NavLink
|
|
||||||
href="/dash/{data.guild.id}/ignored-users"
|
|
||||||
active={$page.url.pathname === `/dash/${data.guild.id}/ignored-users`}
|
|
||||||
>
|
|
||||||
Ignored users
|
|
||||||
</NavLink>
|
</NavLink>
|
||||||
<NavLink
|
<NavLink
|
||||||
href="/dash/{data.guild.id}/key-roles"
|
href="/dash/{data.guild.id}/key-roles"
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Label } from "@sveltestrap/sveltestrap";
|
import { Label } from "@sveltestrap/sveltestrap";
|
||||||
import type { GuildChannelConfig } from "$lib/api";
|
|
||||||
import type { PageData } from "./$types";
|
import type { PageData } from "./$types";
|
||||||
import ChannelSelect from "./ChannelSelect.svelte";
|
import ChannelSelect from "./ChannelSelect.svelte";
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
|
|
||||||
$: channels = data.guild.config as GuildChannelConfig;
|
$: channels = data.guild.channels;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<h3>Log channels</h3>
|
<h3>Log channels</h3>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import Svelecte from "svelecte";
|
||||||
|
|
||||||
|
type Option = { label: string; value: string };
|
||||||
|
|
||||||
|
export let options: Array<Option>;
|
||||||
|
export let placeholder: string = "Select a role";
|
||||||
|
export let value: string | string[] | null;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Svelecte
|
||||||
|
bind:value
|
||||||
|
{options}
|
||||||
|
{placeholder}
|
||||||
|
labelField="label"
|
||||||
|
valueField="value"
|
||||||
|
searchable={true}
|
||||||
|
/>
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
import {
|
|
||||||
Label,
|
|
||||||
ListGroup,
|
|
||||||
ListGroupItem,
|
|
||||||
Button,
|
|
||||||
} from "@sveltestrap/sveltestrap";
|
|
||||||
import type { PageData } from "./$types";
|
|
||||||
import { makeFullOptions } from "$lib/util";
|
|
||||||
import ChannelSelect from "../ChannelSelect.svelte";
|
|
||||||
import { fastFetch, type ApiError } from "$lib/api";
|
|
||||||
import { addToast } from "$lib/toast";
|
|
||||||
|
|
||||||
export let data: PageData;
|
|
||||||
|
|
||||||
$: ignored = data.guild.config.ignored_channels;
|
|
||||||
$: options = makeFullOptions(data.guild, ignored);
|
|
||||||
|
|
||||||
$: allChannels = [
|
|
||||||
...data.guild.channels_without_category.map((c) => ({
|
|
||||||
id: c.id,
|
|
||||||
name: `#${c.name}`,
|
|
||||||
})),
|
|
||||||
...data.guild.categories.map((cat) => ({ id: cat.id, name: cat.name })),
|
|
||||||
...data.guild.categories.flatMap((cat) =>
|
|
||||||
cat.channels.map((c) => ({ id: c.id, name: `#${c.name}` })),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
const channelName = (id: string) =>
|
|
||||||
allChannels.find((c) => c.id === id)?.name || `(unknown channel ${id})`;
|
|
||||||
|
|
||||||
let toIgnore: string | null = null;
|
|
||||||
|
|
||||||
const addIgnore = async () => {
|
|
||||||
if (!toIgnore) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await fastFetch(
|
|
||||||
"PUT",
|
|
||||||
`/api/guilds/${data.guild.id}/ignored-channels/${toIgnore}`,
|
|
||||||
);
|
|
||||||
ignored.push(toIgnore);
|
|
||||||
ignored = ignored;
|
|
||||||
addToast({
|
|
||||||
header: "Ignored channel",
|
|
||||||
body: `Added ${channelName(toIgnore)} to the list of ignored channels.`,
|
|
||||||
});
|
|
||||||
toIgnore = null;
|
|
||||||
} catch (e) {
|
|
||||||
addToast({
|
|
||||||
header: "Error ignoring channel",
|
|
||||||
body:
|
|
||||||
(e as ApiError).message || "Unknown error. Please try again later.",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeIgnore = async (id: string) => {
|
|
||||||
try {
|
|
||||||
await fastFetch(
|
|
||||||
"DELETE",
|
|
||||||
`/api/guilds/${data.guild.id}/ignored-channels/${id}`,
|
|
||||||
);
|
|
||||||
const idx = ignored.indexOf(id);
|
|
||||||
if (idx > -1) ignored.splice(idx, 1);
|
|
||||||
ignored = ignored;
|
|
||||||
addToast({
|
|
||||||
header: "Stopped ignoring channel",
|
|
||||||
body: `Removed ${channelName(id)} from the list of ignored channels.`,
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
addToast({
|
|
||||||
header: "Error removing channel",
|
|
||||||
body:
|
|
||||||
(e as ApiError).message || "Unknown error. Please try again later.",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<h3>Ignored channels</h3>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Messages from ignored channels will not be logged. Changes to ignored channels
|
|
||||||
will also not be logged, but note that ignored channels being <em>deleted</em>
|
|
||||||
(or new channels being created in an ignored category) will still be logged.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Label><strong>Ignore a new channel</strong></Label>
|
|
||||||
<ChannelSelect bind:value={toIgnore} {options} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="my-2 d-grid d-md-block">
|
|
||||||
<Button color="primary" on:click={() => addIgnore()} disabled={!toIgnore}>
|
|
||||||
Ignore channel
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h4>Currently ignored channels</h4>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ListGroup>
|
|
||||||
{#each ignored as id}
|
|
||||||
<ListGroupItem class="d-flex justify-content-between align-items-center">
|
|
||||||
{channelName(id)}
|
|
||||||
<Button color="link" on:click={() => removeIgnore(id)}>
|
|
||||||
Stop ignoring
|
|
||||||
</Button>
|
|
||||||
</ListGroupItem>
|
|
||||||
{/each}
|
|
||||||
</ListGroup>
|
|
||||||
|
|
@ -0,0 +1,320 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { ListGroup, Button } from "@sveltestrap/sveltestrap";
|
||||||
|
import type { PageData } from "./$types";
|
||||||
|
import { makeFullOptions } from "$lib/util";
|
||||||
|
import ChannelSelect from "../ChannelSelect.svelte";
|
||||||
|
import apiFetch, {
|
||||||
|
fastFetch,
|
||||||
|
TOKEN_KEY,
|
||||||
|
type ApiError,
|
||||||
|
type GuildRole,
|
||||||
|
} from "$lib/api";
|
||||||
|
import { addToast } from "$lib/toast";
|
||||||
|
import RoleSelect from "../RoleSelect.svelte";
|
||||||
|
import RoleListItem from "$lib/components/RoleListItem.svelte";
|
||||||
|
import RemovableListItem from "$lib/components/RemovableListItem.svelte";
|
||||||
|
import Svelecte from "svelecte";
|
||||||
|
|
||||||
|
export let data: PageData;
|
||||||
|
|
||||||
|
$: ignoredChannels = data.guild.messages.ignored_channels;
|
||||||
|
$: channelOptions = makeFullOptions(data.guild, ignoredChannels);
|
||||||
|
|
||||||
|
$: ignoredRoles = data.guild.messages.ignored_roles;
|
||||||
|
$: roleOptions = data.guild.roles
|
||||||
|
.filter((r) => ignoredRoles.indexOf(r.id) === -1)
|
||||||
|
.map((r) => ({ label: r.name, value: r.id }));
|
||||||
|
|
||||||
|
$: allChannels = [
|
||||||
|
...data.guild.channels_without_category.map((c) => ({
|
||||||
|
id: c.id,
|
||||||
|
name: `#${c.name}`,
|
||||||
|
})),
|
||||||
|
...data.guild.categories.map((cat) => ({ id: cat.id, name: cat.name })),
|
||||||
|
...data.guild.categories.flatMap((cat) =>
|
||||||
|
cat.channels.map((c) => ({ id: c.id, name: `#${c.name}` })),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
const channelName = (id: string) =>
|
||||||
|
allChannels.find((c) => c.id === id)?.name || `(unknown channel ${id})`;
|
||||||
|
|
||||||
|
const roleName = (id: string) =>
|
||||||
|
data.guild.roles.find((r) => r.id === id)?.name || `(unknown role ${id})`;
|
||||||
|
|
||||||
|
const roleObject = (id: string): GuildRole | string =>
|
||||||
|
data.guild.roles.find((r) => r.id === id) || id;
|
||||||
|
|
||||||
|
let channelToIgnore: string | null = null;
|
||||||
|
let roleToIgnore: string | null = null;
|
||||||
|
let userToIgnore: string | null = null;
|
||||||
|
const userIdRegex = /^\d{15,}$/;
|
||||||
|
|
||||||
|
const addChannelIgnore = async () => {
|
||||||
|
if (!channelToIgnore) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fastFetch(
|
||||||
|
"PUT",
|
||||||
|
`/api/guilds/${data.guild.id}/ignored-messages/channels/${channelToIgnore}`,
|
||||||
|
);
|
||||||
|
ignoredChannels.push(channelToIgnore);
|
||||||
|
ignoredChannels = ignoredChannels;
|
||||||
|
addToast({
|
||||||
|
header: "Ignored channel",
|
||||||
|
body: `Added ${channelName(channelToIgnore)} to the list of ignored channels.`,
|
||||||
|
});
|
||||||
|
channelToIgnore = null;
|
||||||
|
} catch (e) {
|
||||||
|
addToast({
|
||||||
|
header: "Error ignoring channel",
|
||||||
|
body:
|
||||||
|
(e as ApiError).message || "Unknown error. Please try again later.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeChannelIgnore = async (id: string) => {
|
||||||
|
try {
|
||||||
|
await fastFetch(
|
||||||
|
"DELETE",
|
||||||
|
`/api/guilds/${data.guild.id}/ignored-messages/channels/${id}`,
|
||||||
|
);
|
||||||
|
const idx = ignoredChannels.indexOf(id);
|
||||||
|
if (idx > -1) ignoredChannels.splice(idx, 1);
|
||||||
|
ignoredChannels = ignoredChannels;
|
||||||
|
addToast({
|
||||||
|
header: "Stopped ignoring channel",
|
||||||
|
body: `Removed ${channelName(id)} from the list of ignored channels.`,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
addToast({
|
||||||
|
header: "Error removing channel",
|
||||||
|
body:
|
||||||
|
(e as ApiError).message || "Unknown error. Please try again later.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const addRoleIgnore = async () => {
|
||||||
|
if (!roleToIgnore) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fastFetch(
|
||||||
|
"PUT",
|
||||||
|
`/api/guilds/${data.guild.id}/ignored-messages/roles/${roleToIgnore}`,
|
||||||
|
);
|
||||||
|
ignoredRoles.push(roleToIgnore);
|
||||||
|
ignoredRoles = ignoredRoles;
|
||||||
|
addToast({
|
||||||
|
header: "Ignored role",
|
||||||
|
body: `Added ${roleName(roleToIgnore)} to the list of ignored roles.`,
|
||||||
|
});
|
||||||
|
roleToIgnore = null;
|
||||||
|
} catch (e) {
|
||||||
|
addToast({
|
||||||
|
header: "Error ignoring role",
|
||||||
|
body:
|
||||||
|
(e as ApiError).message || "Unknown error. Please try again later.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeRoleIgnore = async (id: string) => {
|
||||||
|
try {
|
||||||
|
await fastFetch(
|
||||||
|
"DELETE",
|
||||||
|
`/api/guilds/${data.guild.id}/ignored-messages/roles/${id}`,
|
||||||
|
);
|
||||||
|
const idx = ignoredRoles.indexOf(id);
|
||||||
|
if (idx > -1) ignoredRoles.splice(idx, 1);
|
||||||
|
ignoredRoles = ignoredRoles;
|
||||||
|
addToast({
|
||||||
|
header: "Stopped ignoring role",
|
||||||
|
body: `Removed ${roleName(id)} from the list of ignored roles.`,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
addToast({
|
||||||
|
header: "Error removing role",
|
||||||
|
body:
|
||||||
|
(e as ApiError).message || "Unknown error. Please try again later.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const addUserIgnore = async () => {
|
||||||
|
if (!userToIgnore) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const user = await apiFetch<{ id: string; tag: string }>(
|
||||||
|
"PUT",
|
||||||
|
`/api/guilds/${data.guild.id}/ignored-users/${userToIgnore}`,
|
||||||
|
);
|
||||||
|
data.users.push(user);
|
||||||
|
data.users = data.users;
|
||||||
|
addToast({
|
||||||
|
header: "Ignored user",
|
||||||
|
body: `Added ${user.tag} to the list of ignored users.`,
|
||||||
|
});
|
||||||
|
userToIgnore = null;
|
||||||
|
} catch (e) {
|
||||||
|
addToast({
|
||||||
|
header: "Error ignoring user",
|
||||||
|
body:
|
||||||
|
(e as ApiError).message || "Unknown error. Please try again later.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeUserIgnore = async (id: string) => {
|
||||||
|
try {
|
||||||
|
await fastFetch(
|
||||||
|
"DELETE",
|
||||||
|
`/api/guilds/${data.guild.id}/ignored-users/${id}`,
|
||||||
|
);
|
||||||
|
const user = data.users.find((u) => u.id === id);
|
||||||
|
const idx = data.users.findIndex((u) => u.id === id);
|
||||||
|
if (idx > -1) data.users.splice(idx, 1);
|
||||||
|
data.users = data.users;
|
||||||
|
addToast({
|
||||||
|
header: "Stopped ignoring user",
|
||||||
|
body: `Removed ${user?.tag || "unknown user " + id} from the list of ignored users.`,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
addToast({
|
||||||
|
header: "Error removing user",
|
||||||
|
body:
|
||||||
|
(e as ApiError).message || "Unknown error. Please try again later.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchProps: RequestInit = {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
Authorization: localStorage.getItem(TOKEN_KEY) || "",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h3>Ignored messages</h3>
|
||||||
|
|
||||||
|
<p>Here you can select which channels and roles to ignore messages from.</p>
|
||||||
|
|
||||||
|
<h4>How it works</h4>
|
||||||
|
|
||||||
|
<p>Messages will be ignored if:</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>They are posted in a channel that is listed here</li>
|
||||||
|
<li>
|
||||||
|
They are posted in a channel in a <em>category</em> that is listed here
|
||||||
|
</li>
|
||||||
|
<li>They are posted in a thread that's in a channel that is listed here</li>
|
||||||
|
<li>
|
||||||
|
They are posted by a person with <em>at least one role</em> that is listed here
|
||||||
|
</li>
|
||||||
|
<li>They are posted by a user that is listed here</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Additionally, messages from <a href="/dash/{data.guild.id}/ignored-users"
|
||||||
|
>ignored users</a
|
||||||
|
> are also ignored.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h4 class="mt-4">Channels</h4>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<ChannelSelect bind:value={channelToIgnore} options={channelOptions} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-2 mb-3 d-grid d-md-block">
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
on:click={() => addChannelIgnore()}
|
||||||
|
disabled={!channelToIgnore}
|
||||||
|
>
|
||||||
|
Ignore messages from this channel
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ListGroup>
|
||||||
|
{#each ignoredChannels as id}
|
||||||
|
<RemovableListItem
|
||||||
|
on:click={() => removeChannelIgnore(id)}
|
||||||
|
buttonText="Stop ignoring"
|
||||||
|
>
|
||||||
|
{channelName(id)}
|
||||||
|
</RemovableListItem>
|
||||||
|
{/each}
|
||||||
|
</ListGroup>
|
||||||
|
|
||||||
|
<h4 class="mt-4">Roles</h4>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<RoleSelect bind:value={roleToIgnore} options={roleOptions} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-2 mb-3 d-grid d-md-block">
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
on:click={() => addRoleIgnore()}
|
||||||
|
disabled={!roleToIgnore}
|
||||||
|
>
|
||||||
|
Ignore messages from this role
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ListGroup>
|
||||||
|
{#each ignoredRoles as id}
|
||||||
|
<RoleListItem
|
||||||
|
role={roleObject(id)}
|
||||||
|
on:click={() => removeRoleIgnore(id)}
|
||||||
|
buttonText="Stop ignoring"
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
</ListGroup>
|
||||||
|
|
||||||
|
<h4 class="mt-4">Users</h4>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Svelecte
|
||||||
|
bind:value={userToIgnore}
|
||||||
|
fetch="/api/guilds/{data.guild.id}/users?query=[query]"
|
||||||
|
{fetchProps}
|
||||||
|
multiple={false}
|
||||||
|
searchable={true}
|
||||||
|
creatable={true}
|
||||||
|
labelField="name"
|
||||||
|
valueField="id"
|
||||||
|
creatablePrefix="user with ID "
|
||||||
|
/>
|
||||||
|
{#if userToIgnore && !userIdRegex.test(userToIgnore)}
|
||||||
|
<p class="text-danger mt-2">
|
||||||
|
If you're not ignoring a member of your server, you need to give a
|
||||||
|
<strong>user ID</strong>, not their username.
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="my-2 d-grid d-md-block">
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
on:click={() => addUserIgnore()}
|
||||||
|
disabled={!userToIgnore || !userIdRegex.test(userToIgnore)}
|
||||||
|
>
|
||||||
|
Ignore user
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ListGroup>
|
||||||
|
{#each data.users as user (user.id)}
|
||||||
|
<RemovableListItem
|
||||||
|
on:click={() => removeUserIgnore(user.id)}
|
||||||
|
buttonText="Stop ignoring"
|
||||||
|
>
|
||||||
|
{user.tag} (ID: {user.id})
|
||||||
|
</RemovableListItem>
|
||||||
|
{/each}
|
||||||
|
</ListGroup>
|
||||||
|
|
@ -1,119 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Label,
|
|
||||||
ListGroup,
|
|
||||||
ListGroupItem,
|
|
||||||
} from "@sveltestrap/sveltestrap";
|
|
||||||
import type { PageData } from "./$types";
|
|
||||||
import { addToast } from "$lib/toast";
|
|
||||||
import apiFetch, { fastFetch, TOKEN_KEY, type ApiError } from "$lib/api";
|
|
||||||
import Svelecte from "svelecte";
|
|
||||||
|
|
||||||
export let data: PageData;
|
|
||||||
|
|
||||||
let toIgnore: string | null = null;
|
|
||||||
const idRegex = /^\d{15,}$/;
|
|
||||||
|
|
||||||
const addIgnore = async () => {
|
|
||||||
if (!toIgnore) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const user = await apiFetch<{ id: string; tag: string }>(
|
|
||||||
"PUT",
|
|
||||||
`/api/guilds/${data.guild.id}/ignored-users/${toIgnore}`,
|
|
||||||
);
|
|
||||||
data.users.push(user);
|
|
||||||
data.users = data.users;
|
|
||||||
addToast({
|
|
||||||
header: "Ignored user",
|
|
||||||
body: `Added ${user.tag} to the list of ignored users.`,
|
|
||||||
});
|
|
||||||
toIgnore = null;
|
|
||||||
} catch (e) {
|
|
||||||
addToast({
|
|
||||||
header: "Error ignoring user",
|
|
||||||
body:
|
|
||||||
(e as ApiError).message || "Unknown error. Please try again later.",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeIgnore = async (id: string) => {
|
|
||||||
try {
|
|
||||||
await fastFetch(
|
|
||||||
"DELETE",
|
|
||||||
`/api/guilds/${data.guild.id}/ignored-users/${id}`,
|
|
||||||
);
|
|
||||||
const user = data.users.find((u) => u.id === id);
|
|
||||||
const idx = data.users.findIndex((u) => u.id === id);
|
|
||||||
if (idx > -1) data.users.splice(idx, 1);
|
|
||||||
data.users = data.users;
|
|
||||||
addToast({
|
|
||||||
header: "Stopped ignoring user",
|
|
||||||
body: `Removed ${user?.tag || "unknown user " + id} from the list of ignored users.`,
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
addToast({
|
|
||||||
header: "Error removing user",
|
|
||||||
body:
|
|
||||||
(e as ApiError).message || "Unknown error. Please try again later.",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchProps: RequestInit = {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
Authorization: localStorage.getItem(TOKEN_KEY) || "",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<h3>Ignored users</h3>
|
|
||||||
|
|
||||||
<p>Messages from ignored users will not be logged.</p>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Label><strong>Ignore a new user</strong></Label>
|
|
||||||
<Svelecte
|
|
||||||
bind:value={toIgnore}
|
|
||||||
fetch="/api/guilds/{data.guild.id}/users?query=[query]"
|
|
||||||
{fetchProps}
|
|
||||||
multiple={false}
|
|
||||||
searchable={true}
|
|
||||||
creatable={true}
|
|
||||||
labelField="name"
|
|
||||||
valueField="id"
|
|
||||||
creatablePrefix="user with ID "
|
|
||||||
/>
|
|
||||||
{#if toIgnore && !idRegex.test(toIgnore)}
|
|
||||||
<p class="text-danger mt-2">
|
|
||||||
If you're not ignoring a member of your server, you need to give a
|
|
||||||
<strong>user ID</strong>, not their username.
|
|
||||||
</p>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="my-2 d-grid d-md-block">
|
|
||||||
<Button
|
|
||||||
color="primary"
|
|
||||||
on:click={() => addIgnore()}
|
|
||||||
disabled={!toIgnore || !idRegex.test(toIgnore)}
|
|
||||||
>
|
|
||||||
Ignore user
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h4>Currently ignored users</h4>
|
|
||||||
|
|
||||||
<ListGroup>
|
|
||||||
{#each data.users as user (user.id)}
|
|
||||||
<ListGroupItem class="d-flex justify-content-between align-items-center">
|
|
||||||
<span>{user.tag} (ID: {user.id})</span>
|
|
||||||
<Button color="link" on:click={() => removeIgnore(user.id)}>
|
|
||||||
Stop ignoring
|
|
||||||
</Button>
|
|
||||||
</ListGroupItem>
|
|
||||||
{/each}
|
|
||||||
</ListGroup>
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Button, ButtonGroup, Input } from "@sveltestrap/sveltestrap";
|
import { Alert, Button, ButtonGroup, Input } from "@sveltestrap/sveltestrap";
|
||||||
import type { PageData } from "./$types";
|
import type { PageData } from "./$types";
|
||||||
import apiFetch, { fastFetch, type ApiError } from "$lib/api";
|
import apiFetch, { fastFetch, type ApiError } from "$lib/api";
|
||||||
import saveAs from "file-saver";
|
import saveAs from "file-saver";
|
||||||
|
|
@ -64,6 +64,12 @@
|
||||||
too.
|
too.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<Alert color="danger">
|
||||||
|
<h4 class="alert-heading">This will overwrite existing settings</h4>
|
||||||
|
If you have already made some changes to this server's settings, they will be lost
|
||||||
|
when importing a backup.
|
||||||
|
</Alert>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<Input
|
<Input
|
||||||
type="file"
|
type="file"
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@
|
||||||
Button,
|
Button,
|
||||||
Label,
|
Label,
|
||||||
ListGroup,
|
ListGroup,
|
||||||
ListGroupItem,
|
|
||||||
} from "@sveltestrap/sveltestrap";
|
} from "@sveltestrap/sveltestrap";
|
||||||
import type { PageData } from "./$types";
|
import type { PageData } from "./$types";
|
||||||
import Svelecte from "svelecte";
|
import Svelecte from "svelecte";
|
||||||
import { addToast } from "$lib/toast";
|
import { addToast } from "$lib/toast";
|
||||||
import { fastFetch, type ApiError } from "$lib/api";
|
import { fastFetch, type ApiError } from "$lib/api";
|
||||||
|
import RoleListItem from "$lib/components/RoleListItem.svelte";
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
|
|
||||||
|
|
@ -63,8 +63,6 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const colour = (c: string) => (c === "#000000" ? "current-color" : c);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<h3>Key roles</h3>
|
<h3>Key roles</h3>
|
||||||
|
|
@ -95,14 +93,8 @@
|
||||||
<h4>Current key roles</h4>
|
<h4>Current key roles</h4>
|
||||||
|
|
||||||
<ListGroup>
|
<ListGroup>
|
||||||
{#each keyRoles as r (r.id)}
|
{#each keyRoles as role (role.id)}
|
||||||
<ListGroupItem class="d-flex justify-content-between align-items-center">
|
<RoleListItem {role} on:click={() => removeRole(role.id)} />
|
||||||
<span>
|
|
||||||
<span style="color: {colour(r.colour)};">●</span>
|
|
||||||
{r.name}
|
|
||||||
</span>
|
|
||||||
<Button color="link" on:click={() => removeRole(r.id)}>Remove</Button>
|
|
||||||
</ListGroupItem>
|
|
||||||
{/each}
|
{/each}
|
||||||
</ListGroup>
|
</ListGroup>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,11 @@
|
||||||
import { fastFetch } from "$lib/api";
|
import { fastFetch } from "$lib/api";
|
||||||
import { addToast } from "$lib/toast";
|
import { addToast } from "$lib/toast";
|
||||||
import { makeFullOptions } from "$lib/util";
|
import { makeFullOptions } from "$lib/util";
|
||||||
|
import RemovableListItem from "$lib/components/RemovableListItem.svelte";
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
|
|
||||||
$: redirects = data.guild.config.redirects;
|
$: redirects = data.guild.channels.redirects;
|
||||||
|
|
||||||
$: allChannels = [
|
$: allChannels = [
|
||||||
...data.guild.channels_without_category.map((c) => ({
|
...data.guild.channels_without_category.map((c) => ({
|
||||||
|
|
@ -51,7 +52,7 @@
|
||||||
target,
|
target,
|
||||||
});
|
});
|
||||||
|
|
||||||
data.guild.config.redirects[source] = target;
|
data.guild.channels.redirects[source] = target;
|
||||||
|
|
||||||
addToast({ body: "Successfully added redirect." });
|
addToast({ body: "Successfully added redirect." });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -67,8 +68,8 @@
|
||||||
try {
|
try {
|
||||||
await fastFetch("DELETE", `/api/guilds/${data.guild.id}/redirects/${id}`);
|
await fastFetch("DELETE", `/api/guilds/${data.guild.id}/redirects/${id}`);
|
||||||
|
|
||||||
delete data.guild.config.redirects[id];
|
delete data.guild.channels.redirects[id];
|
||||||
data.guild.config.redirects = data.guild.config.redirects;
|
data.guild.channels.redirects = data.guild.channels.redirects;
|
||||||
|
|
||||||
addToast({ body: "Successfully removed redirect." });
|
addToast({ body: "Successfully removed redirect." });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -108,16 +109,12 @@
|
||||||
|
|
||||||
<ListGroup>
|
<ListGroup>
|
||||||
{#each Object.keys(redirects) as redirectSource}
|
{#each Object.keys(redirects) as redirectSource}
|
||||||
<ListGroupItem class="d-flex justify-content-between align-items-center">
|
<RemovableListItem
|
||||||
<span
|
on:click={() => removeRedirect(redirectSource)}
|
||||||
>{channelName(redirectSource)} ➜ {channelName(
|
buttonText="Remove redirect"
|
||||||
redirects[redirectSource],
|
>
|
||||||
)}</span
|
{channelName(redirectSource)} ➜ {channelName(redirects[redirectSource])}
|
||||||
>
|
</RemovableListItem>
|
||||||
<Button color="link" on:click={() => removeRedirect(redirectSource)}>
|
|
||||||
Remove redirect
|
|
||||||
</Button>
|
|
||||||
</ListGroupItem>
|
|
||||||
{/each}
|
{/each}
|
||||||
</ListGroup>
|
</ListGroup>
|
||||||
{:else}
|
{:else}
|
||||||
|
|
|
||||||
|
|
@ -696,8 +696,8 @@ __metadata:
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@sveltejs/kit@npm:^2.0.0":
|
"@sveltejs/kit@npm:^2.0.0":
|
||||||
version: 2.8.0
|
version: 2.8.1
|
||||||
resolution: "@sveltejs/kit@npm:2.8.0"
|
resolution: "@sveltejs/kit@npm:2.8.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/cookie": "npm:^0.6.0"
|
"@types/cookie": "npm:^0.6.0"
|
||||||
cookie: "npm:^0.6.0"
|
cookie: "npm:^0.6.0"
|
||||||
|
|
@ -717,7 +717,7 @@ __metadata:
|
||||||
vite: ^5.0.3
|
vite: ^5.0.3
|
||||||
bin:
|
bin:
|
||||||
svelte-kit: svelte-kit.js
|
svelte-kit: svelte-kit.js
|
||||||
checksum: 10c0/f4fdad81bf5f8f645eed21ffd7015b14c76481b4df054f7dc16ab6bb6a32dc0c9fd28790a7da3e3e3a0b9860e6405cbd082df8cc689694f09bef30659c47cc23
|
checksum: 10c0/531cf8fdff0e039f51ad3e9aa288acea5fd9eb03b3189fda68dc45e7ac12a288f6a1792aace9c9fec3979475b5e487eb3af4b6160ad23c5bea1f503b89da5cd2
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
|
@ -734,7 +734,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@sveltejs/vite-plugin-svelte@npm:3":
|
"@sveltejs/vite-plugin-svelte@npm:^3.0.0":
|
||||||
version: 3.1.2
|
version: 3.1.2
|
||||||
resolution: "@sveltejs/vite-plugin-svelte@npm:3.1.2"
|
resolution: "@sveltejs/vite-plugin-svelte@npm:3.1.2"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -1117,7 +1117,7 @@ __metadata:
|
||||||
dependencies:
|
dependencies:
|
||||||
"@sveltejs/adapter-static": "npm:^3.0.5"
|
"@sveltejs/adapter-static": "npm:^3.0.5"
|
||||||
"@sveltejs/kit": "npm:^2.0.0"
|
"@sveltejs/kit": "npm:^2.0.0"
|
||||||
"@sveltejs/vite-plugin-svelte": "npm:3"
|
"@sveltejs/vite-plugin-svelte": "npm:^3.0.0"
|
||||||
"@sveltestrap/sveltestrap": "npm:^6.2.7"
|
"@sveltestrap/sveltestrap": "npm:^6.2.7"
|
||||||
"@types/eslint": "npm:^9.6.0"
|
"@types/eslint": "npm:^9.6.0"
|
||||||
"@types/file-saver": "npm:^2.0.7"
|
"@types/file-saver": "npm:^2.0.7"
|
||||||
|
|
@ -1135,7 +1135,7 @@ __metadata:
|
||||||
sass: "npm:^1.80.1"
|
sass: "npm:^1.80.1"
|
||||||
snarkdown: "npm:^2.0.0"
|
snarkdown: "npm:^2.0.0"
|
||||||
svelecte: "npm:^4.3.1"
|
svelecte: "npm:^4.3.1"
|
||||||
svelte: "npm:4"
|
svelte: "npm:^4.2.7"
|
||||||
svelte-check: "npm:^4.0.0"
|
svelte-check: "npm:^4.0.0"
|
||||||
typescript: "npm:^5.0.0"
|
typescript: "npm:^5.0.0"
|
||||||
typescript-eslint: "npm:^8.0.0"
|
typescript-eslint: "npm:^8.0.0"
|
||||||
|
|
@ -3124,7 +3124,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"svelte@npm:4":
|
"svelte@npm:^4.2.7":
|
||||||
version: 4.2.19
|
version: 4.2.19
|
||||||
resolution: "svelte@npm:4.2.19"
|
resolution: "svelte@npm:4.2.19"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue