liljudd-website/src/routes/config/[guildId].tsx
2024-02-02 19:31:53 +01:00

280 lines
8.4 KiB
TypeScript

import { getSession } from "@auth/solid-start";
import { faToggleOff, faToggleOn } from "@fortawesome/pro-regular-svg-icons";
import { useNavigate, useParams } from "@solidjs/router";
import { eq } from "drizzle-orm";
import moment from "moment-timezone";
import createClient from "openapi-fetch";
import { Index, createEffect, createResource, createSignal } from "solid-js";
import { createStore } from "solid-js/store";
import { getRequestEvent } from "solid-js/web";
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon";
import Layout from "~/components/Layout";
import db from "~/drizzle";
import { accounts } from "~/drizzle/schema";
import { authOptions } from "~/server/auth";
import { paths } from "~/types/discord";
import "../../styles/pages/config.scss";
const guessTZ = () => Intl.DateTimeFormat().resolvedOptions().timeZone;
const initialValue = (params: ReturnType<typeof useParams>) => ({
success: null as boolean | null,
guild: {
id: params.guildId,
name: undefined as string | undefined,
icon: undefined as string | null | undefined,
},
tzNames: [guessTZ()],
});
const getPayload = async (
id: string,
): Promise<
| { success: false; message: string }
| (ReturnType<typeof initialValue> & { success: true })
> => {
"use server";
const event = getRequestEvent();
if (!event) return { success: false, message: "No request event!" };
const session = await getSession(event.request, authOptions);
if (!session?.user?.id)
return { success: false, message: "No user with id!" };
const { DISCORD_ACCESS_TOKEN } = (
await db
.selectDistinct({ DISCORD_ACCESS_TOKEN: accounts.access_token })
.from(accounts)
.where(eq(accounts.userId, session.user?.id))
.limit(1)
.execute()
)[0];
if (!DISCORD_ACCESS_TOKEN)
return { success: false, message: "No discord access token!" };
// const guilds = await fetch("https://discord.com/api/users/@me/guilds", {
// headers: { Authorization: `Bearer ${DISCORD_ACCESS_TOKEN}` },
// }).then((res) => res.json());
const { GET } = createClient<paths>({
baseUrl: "https://discord.com/api/v10",
});
const { data: guilds, error } = await GET("/users/@me/guilds", {
headers: { Authorization: `Bearer ${DISCORD_ACCESS_TOKEN}` },
});
if (error) {
console.log(error);
return { success: false, message: "Error on discord api request!" };
}
const guild = guilds?.find((e) => e.id === id);
if (!guild)
return {
success: false,
message: "User is in no such guild with requested id!",
};
if (!(parseInt(guild.permissions) & (1 << 5)))
return {
success: false,
message:
"User is no MANAGE_GUILD permissions on this guild with requested id!",
};
return {
success: true,
guild: {
id: guild.id,
name: guild.name,
icon: guild.icon,
},
// guild: guilds
// .filter((e: any) => e.permissions & (1 << 5))
// .map((e: any) => e.name),
tzNames: moment.tz.names(),
};
};
function config() {
const params = useParams();
const navigator = useNavigate();
let [timezoneRef, setTimezoneRef] = createSignal<HTMLInputElement>();
let [timePlanningRef, setTimePlanningRef] = createSignal<HTMLInputElement>();
let [pingableRolesRef, setPingableRolesRef] =
createSignal<HTMLInputElement>();
const [timezone, setTimezone] = createSignal(guessTZ());
const [payload] = createResource(params.guildId, async (id) => {
const payload = await getPayload(id);
if (!payload.success) {
console.log(payload.message, "No success");
navigator("/config", { replace: false });
return initialValue(params);
}
return payload;
});
const guild = () => payload()?.guild ?? initialValue(params).guild;
const tzNames = () => payload()?.tzNames ?? [];
const [config, setConfig] = createStore({
features: {
timePlanning: {
enabled: false,
pingableRoles: false,
},
},
});
createEffect(() => console.log(payload()));
createEffect(() => console.log("timezone", timezone()));
createEffect(() =>
console.log("timePlanning.enabled", config.features.timePlanning.enabled),
);
createEffect(() =>
console.log(
"timePlanning.pingableRoles",
config.features.timePlanning.pingableRoles,
),
);
createEffect(() => {
const ref = timezoneRef();
if (!ref) return;
ref.value = timezone();
});
createEffect(() => {
const ref = timePlanningRef();
if (!ref) return;
ref.checked = config.features.timePlanning.enabled;
});
createEffect(() => {
const ref = pingableRolesRef();
if (!ref) return;
ref.checked = config.features.timePlanning.pingableRoles;
});
return (
<Layout site="config">
<h3 class="text-center">Configure li&apos;l Judd in</h3>
<div>
<div>
<div class="flex-row centered">
<img
class="guildpfp"
src={
guild()?.icon
? `https://cdn.discordapp.com/icons/${guild()?.id}/${
guild()?.icon
}.webp?size=240`
: "https://cdn.discordapp.com/icons/1040502664506646548/bb5a51c4659cf47bdd942bb11e974da7.webp?size=240"
}
alt="Server pfp"
/>
<h1>{guild()?.name ?? "li'l Judds home base"}</h1>
</div>
</div>
<section>
<h2>Guild</h2>
<p>General settings for this guild.</p>
<div class="flex-row">
<label for="timezone">Timezone for your server:</label>
<input
type="text"
list="timezones"
id="timezone"
ref={(e) => setTimezoneRef(e)}
// disabled={!tzNames().find((e) => e === timezone())}
onInput={(e) => setTimezone(e.target.value)}
/>
<datalist id="timezones">
<Index each={tzNames()}>
{(zone) => <option value={zone()} />}
</Index>
</datalist>
<button
disabled={guessTZ() === timezone()}
title={"Detected: " + guessTZ()}
onClick={() => setTimezone(guessTZ())}
>
Auto-detect
</button>
</div>
</section>
<section>
<h2>Features</h2>
<p>Configure the features of the bot</p>
<label for="timePlanning" class="flex-row">
<p>Time Planning </p>
<FontAwesomeIcon
icon={
config.features.timePlanning.enabled ? faToggleOn : faToggleOff
}
size="xl"
/>
</label>
<input
hidden
type="checkbox"
id="timePlanning"
ref={(e) => setTimePlanningRef(e)}
onInput={(e) =>
setConfig("features", "timePlanning", "enabled", e.target.checked)
}
/>
<div
class="sub"
classList={{ disabled: !config.features.timePlanning.enabled }}
>
<div class="flex-row">
<label>Target channel:</label>
<select value={timezone()}>
<optgroup label="--Select a Channel--">
<Index each={tzNames()}>
{(channel) => <option>{channel()}</option>}
</Index>
</optgroup>
</select>
</div>
<div class="flex-row">
<label for="pingableRoles" class="flex-row">
<p>Enable pingable Roles:</p>
<FontAwesomeIcon
icon={
config.features.timePlanning.pingableRoles
? faToggleOn
: faToggleOff
}
size="xl"
/>
</label>
<input
hidden
type="checkbox"
id="pingableRoles"
ref={(e) => setPingableRolesRef(e)}
onInput={(e) =>
setConfig(
"features",
"timePlanning",
"pingableRoles",
e.target.checked,
)
}
/>
</div>
</div>
</section>
<section>
<button>Apply</button>
</section>
</div>
</Layout>
);
}
export default config;