style: login and config working
This commit is contained in:
parent
a657906f4f
commit
55b81fac91
28 changed files with 2322 additions and 3469 deletions
|
@ -13,10 +13,18 @@
|
|||
"@auth/core": "^0.19.0",
|
||||
"@auth/drizzle-adapter": "^0.3.12",
|
||||
"@auth/solid-start": "0.1.2",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.5.1",
|
||||
"@fortawesome/pro-duotone-svg-icons": "^6.5.1",
|
||||
"@fortawesome/pro-light-svg-icons": "^6.5.1",
|
||||
"@fortawesome/pro-regular-svg-icons": "^6.5.1",
|
||||
"@fortawesome/pro-solid-svg-icons": "^6.5.1",
|
||||
"@fortawesome/pro-thin-svg-icons": "^6.5.1",
|
||||
"@fortawesome/sharp-solid-svg-icons": "^6.5.1",
|
||||
"@solidjs/meta": "^0.29.2",
|
||||
"@solidjs/router": "^0.10.9",
|
||||
"@solidjs/start": "^0.4.9",
|
||||
"drizzle-orm": "^0.29.2",
|
||||
"moment-timezone": "^0.5.44",
|
||||
"postgres": "^3.4.3",
|
||||
"solid-js": "^1.8.11",
|
||||
"vinxi": "^0.1.2"
|
||||
|
|
3451
pnpm-lock.yaml
3451
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -1,12 +1,10 @@
|
|||
// @refresh reload
|
||||
import "@fortawesome/fontawesome-svg-core/styles.css";
|
||||
import { Meta, MetaProvider, Title } from "@solidjs/meta";
|
||||
import { Router } from "@solidjs/router";
|
||||
import { FileRoutes } from "@solidjs/start";
|
||||
import { Suspense } from "solid-js";
|
||||
import Footer from "./components/Footer";
|
||||
import NavBar from "./components/NavBar";
|
||||
import "./styles/GlobalLayout.css";
|
||||
import "./styles/Layout.scss";
|
||||
import "./styles/global.scss";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
|
@ -18,9 +16,8 @@ export default function App() {
|
|||
content="The Splatoon Discord bot with unique features."
|
||||
/>
|
||||
<Title>li'l Judd - Your competitive Splatoon assistant</Title>
|
||||
<NavBar />
|
||||
|
||||
<Suspense>{props.children}</Suspense>
|
||||
<Footer />
|
||||
</MetaProvider>
|
||||
)}
|
||||
>
|
||||
|
|
151
src/components/FontAwesomeIcon.tsx
Normal file
151
src/components/FontAwesomeIcon.tsx
Normal file
|
@ -0,0 +1,151 @@
|
|||
import {
|
||||
FaSymbol,
|
||||
FlipProp,
|
||||
IconDefinition,
|
||||
IconProp,
|
||||
PullProp,
|
||||
RotateProp,
|
||||
SizeProp,
|
||||
Transform,
|
||||
} from "@fortawesome/fontawesome-svg-core";
|
||||
import { type JSX } from "solid-js";
|
||||
|
||||
export interface FontAwesomeIconProps
|
||||
extends Omit<
|
||||
JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||
"children" | "mask" | "transform"
|
||||
> {
|
||||
icon: IconDefinition;
|
||||
mask?: IconProp;
|
||||
maskId?: string;
|
||||
color?: string;
|
||||
spin?: boolean;
|
||||
spinPulse?: boolean;
|
||||
spinReverse?: boolean;
|
||||
pulse?: boolean;
|
||||
beat?: boolean;
|
||||
fade?: boolean;
|
||||
beatFade?: boolean;
|
||||
bounce?: boolean;
|
||||
shake?: boolean;
|
||||
flash?: boolean;
|
||||
border?: boolean;
|
||||
fixedWidth?: boolean;
|
||||
inverse?: boolean;
|
||||
listItem?: boolean;
|
||||
flip?: FlipProp;
|
||||
size?: SizeProp;
|
||||
pull?: PullProp;
|
||||
rotation?: RotateProp;
|
||||
transform?: string | Transform;
|
||||
symbol?: FaSymbol;
|
||||
style?: JSX.CSSProperties;
|
||||
tabIndex?: number;
|
||||
title?: string;
|
||||
titleId?: string;
|
||||
swapOpacity?: boolean;
|
||||
}
|
||||
|
||||
const idPool = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
function nextUniqueId() {
|
||||
let size = 12;
|
||||
let id = "";
|
||||
|
||||
while (size-- > 0) {
|
||||
id += idPool[(Math.random() * 62) | 0];
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
function Path(props: { d: string | string[] }) {
|
||||
return (
|
||||
<>
|
||||
{typeof props.d === "string" ? (
|
||||
<path fill="currentColor" d={props.d} />
|
||||
) : (
|
||||
<>
|
||||
<path class="fa-secondary" fill="currentColor" d={props.d[0]} />
|
||||
<path class="fa-primary" fill="currentColor" d={props.d[1]} />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function FontAwesomeIcon(props: FontAwesomeIconProps) {
|
||||
const titleId = () =>
|
||||
props.title
|
||||
? "svg-inline--fa-title-".concat(props.titleId || nextUniqueId())
|
||||
: undefined;
|
||||
// Get CSS class list from the props object
|
||||
function attributes() {
|
||||
const defaultClasses = {
|
||||
"svg-inline--fa": true,
|
||||
[`fa-${props.icon.iconName}`]: true,
|
||||
[props.class ?? ""]:
|
||||
typeof props.class !== "undefined" && props.class !== null,
|
||||
...props.classList,
|
||||
};
|
||||
|
||||
// map of CSS class names to properties
|
||||
const faClasses = {
|
||||
"fa-beat": props.beat,
|
||||
"fa-fade": props.fade,
|
||||
"fa-beat-fade": props.beatFade,
|
||||
"fa-bounce": props.bounce,
|
||||
"fa-shake": props.shake,
|
||||
"fa-flash": props.flash,
|
||||
"fa-spin": props.spin,
|
||||
"fa-spin-reverse": props.spinReverse,
|
||||
"fa-spin-pulse": props.spinPulse,
|
||||
"fa-pulse": props.pulse,
|
||||
"fa-fw": props.fixedWidth,
|
||||
"fa-inverse": props.inverse,
|
||||
"fa-border": props.border,
|
||||
"fa-li": props.listItem,
|
||||
"fa-flip": typeof props.flip !== "undefined" && props.flip !== null,
|
||||
"fa-flip-horizontal":
|
||||
props.flip === "horizontal" || props.flip === "both",
|
||||
"fa-flip-vertical": props.flip === "vertical" || props.flip === "both",
|
||||
[`fa-${props.size}`]:
|
||||
typeof props.size !== "undefined" && props.size !== null,
|
||||
[`fa-rotate-${props.rotation}`]:
|
||||
typeof props.rotation !== "undefined" && props.size !== null,
|
||||
[`fa-pull-${props.pull}`]:
|
||||
typeof props.pull !== "undefined" && props.pull !== null,
|
||||
"fa-swap-opacity": props.swapOpacity,
|
||||
};
|
||||
|
||||
const attributes = {
|
||||
focusable: !!props.title,
|
||||
"aria-hidden": !props.title,
|
||||
role: "img",
|
||||
xmlns: "http://www.w3.org/2000/svg",
|
||||
"aria-labelledby": titleId(),
|
||||
"data-prefix": props.icon.prefix,
|
||||
"data-icon": props.icon.iconName,
|
||||
"data-fa-transform": props.transform,
|
||||
"data-fa-mask": props.mask,
|
||||
"data-fa-mask-id": props.maskId,
|
||||
"data-fa-symbol": props.symbol,
|
||||
tabIndex: props.tabIndex,
|
||||
classList: { ...defaultClasses, ...faClasses },
|
||||
color: props.color,
|
||||
style: props.style,
|
||||
viewBox: `0 0 ${props.icon.icon[0]} ${props.icon.icon[1]}`,
|
||||
} as const;
|
||||
|
||||
// return the complete class list
|
||||
return attributes;
|
||||
}
|
||||
|
||||
return (
|
||||
<svg {...attributes()}>
|
||||
{/* <Show when={props.title}>
|
||||
<title id={titleId()}>{props.title}</title>
|
||||
</Show> */}
|
||||
<Path d={props.icon.icon[4]} />
|
||||
</svg>
|
||||
);
|
||||
}
|
18
src/components/Layout.tsx
Normal file
18
src/components/Layout.tsx
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { JSX, Suspense } from "solid-js";
|
||||
import "../styles/Layout.scss";
|
||||
import Footer from "./Footer";
|
||||
import NavBar from "./NavBar";
|
||||
|
||||
function Layout(props: { children: JSX.Element; site: string }) {
|
||||
return (
|
||||
<>
|
||||
<NavBar />
|
||||
<div class={props.site}>
|
||||
<Suspense>{props.children}</Suspense>
|
||||
</div>
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Layout;
|
|
@ -1,35 +1,53 @@
|
|||
import { faCirclePlus } from "@fortawesome/pro-regular-svg-icons";
|
||||
import { JSX, Show, Suspense } from "solid-js";
|
||||
import "../styles/components/NavBar.scss";
|
||||
import { FontAwesomeIcon } from "./FontAwesomeIcon";
|
||||
import NavUser from "./NavUser";
|
||||
|
||||
export function Li(props: {
|
||||
href: string;
|
||||
action?: () => void;
|
||||
name?: string;
|
||||
children?: JSX.Element;
|
||||
}) {
|
||||
return (
|
||||
<li class="navElem flex-row thick">
|
||||
<a
|
||||
class="flex-row"
|
||||
href={props.href}
|
||||
onClick={() => props.action && props.action()}
|
||||
>
|
||||
{props.children ?? <></>}
|
||||
<Show when={props.name}>
|
||||
<span>{props.name}</span>
|
||||
</Show>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
function NavBar() {
|
||||
return (
|
||||
<nav>
|
||||
<ul>
|
||||
<li class="navElem">
|
||||
<a class="textBx" href="/">
|
||||
<img id="logo" src="/assets/logox256.png" alt="The Bots Logo" />
|
||||
li'l Judd
|
||||
</a>
|
||||
</li>
|
||||
<li class="navElem">
|
||||
<a href="/features">Features</a>
|
||||
</li>
|
||||
<li class="navElem">
|
||||
<a href="/how-do-i">How do I...?</a>
|
||||
</li>
|
||||
<li class="navElem">
|
||||
<a href="/stack">The Stack</a>
|
||||
</li>
|
||||
<li class="navElem">
|
||||
<a href="/about">About</a>
|
||||
</li>
|
||||
<li class="navElem">
|
||||
<a
|
||||
<nav class="flex-row responsive">
|
||||
<ul class="flex-row responsive thick">
|
||||
<Li href="/" name="li'l Judd">
|
||||
<img src="/assets/logox256.png" alt="The Bots Logo" />
|
||||
</Li>
|
||||
<Li href="/features" name="Features" />
|
||||
<Li href="/how-do-i" name="How do I...?" />
|
||||
<Li href="/stack" name="The Stack" />
|
||||
<Li href="/about" name="About" />
|
||||
</ul>
|
||||
<ul class="flex-row responsive thick">
|
||||
<Li
|
||||
href="https://discord.com/api/oauth2/authorize?client_id=1024410658973941862&permissions=18977581952080&scope=bot"
|
||||
target="_blank"
|
||||
name="Invite to your server"
|
||||
>
|
||||
Invite to your server
|
||||
</a>
|
||||
</li>
|
||||
<FontAwesomeIcon class="lower" icon={faCirclePlus} size="xl" />
|
||||
</Li>
|
||||
<Suspense>
|
||||
<NavUser />
|
||||
</Suspense>
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
|
|
87
src/components/NavUser.tsx
Normal file
87
src/components/NavUser.tsx
Normal file
|
@ -0,0 +1,87 @@
|
|||
import { getSession } from "@auth/solid-start";
|
||||
import { signIn, signOut } from "@auth/solid-start/client";
|
||||
import {
|
||||
faArrowRightFromBracket,
|
||||
faArrowRightToBracket,
|
||||
faGear,
|
||||
} from "@fortawesome/pro-regular-svg-icons";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { Show, createResource } from "solid-js";
|
||||
import { getRequestEvent } from "solid-js/web";
|
||||
import db from "~/drizzle";
|
||||
import { users } from "~/drizzle/schema";
|
||||
import { authOptions } from "~/server/auth";
|
||||
import { FontAwesomeIcon } from "./FontAwesomeIcon";
|
||||
import { Li } from "./NavBar";
|
||||
|
||||
const initialUser = {
|
||||
id: "",
|
||||
name: null as string | null,
|
||||
email: "",
|
||||
emailVerified: null as Date | null,
|
||||
image: null as string | null,
|
||||
};
|
||||
|
||||
async function getUser() {
|
||||
"use server";
|
||||
|
||||
const event = getRequestEvent();
|
||||
if (!event)
|
||||
return { success: false, message: "No request event!", ...initialUser };
|
||||
|
||||
const session = await getSession(event.request, authOptions);
|
||||
if (!session?.user?.id)
|
||||
return { success: false, message: "No user with id!", ...initialUser };
|
||||
|
||||
const user = (
|
||||
await db
|
||||
.selectDistinct()
|
||||
.from(users)
|
||||
.where(eq(users.id, session.user?.id))
|
||||
.limit(1)
|
||||
.execute()
|
||||
)[0];
|
||||
|
||||
return { success: true, message: "", ...user };
|
||||
}
|
||||
|
||||
function NavUser() {
|
||||
const [user] = createResource(() =>
|
||||
getUser().then((e) => {
|
||||
if (!e.success) console.log(1, e.message);
|
||||
console.log(2, e);
|
||||
return e;
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<Show
|
||||
when={user()?.id}
|
||||
fallback={
|
||||
<Li
|
||||
href="#"
|
||||
name="Login"
|
||||
action={() => signIn("discord", { callbackUrl: "/config" })}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
class="secondary"
|
||||
icon={faArrowRightToBracket}
|
||||
size="xl"
|
||||
/>
|
||||
</Li>
|
||||
}
|
||||
>
|
||||
<Li href="/config">
|
||||
<div class="swap lower">
|
||||
<img class="primary" src={user()?.image ?? ""} alt="User pfp" />
|
||||
<FontAwesomeIcon class="secondary" icon={faGear} size="xl" />
|
||||
</div>
|
||||
</Li>
|
||||
<Li href="#" action={() => signOut({ callbackUrl: "/" })}>
|
||||
<FontAwesomeIcon icon={faArrowRightFromBracket} size="xl" />
|
||||
</Li>
|
||||
</Show>
|
||||
);
|
||||
}
|
||||
|
||||
export default NavUser;
|
|
@ -1,10 +1,11 @@
|
|||
import { Title } from "@solidjs/meta";
|
||||
import { HttpStatusCode } from "@solidjs/start";
|
||||
import Layout from "~/components/Layout";
|
||||
import "../styles/pages/index.scss";
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<>
|
||||
<Layout site="index">
|
||||
<Title>Not Found</Title>
|
||||
<HttpStatusCode code={404} />
|
||||
<section class="index">
|
||||
|
@ -17,6 +18,6 @@ export default function NotFound() {
|
|||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import Layout from "~/components/Layout";
|
||||
import "../styles/pages/about.scss";
|
||||
|
||||
function about() {
|
||||
return (
|
||||
<>
|
||||
<div class="aboutdiv">
|
||||
<Layout site="about">
|
||||
<h1>About</h1>
|
||||
<section>
|
||||
<h2>Why does this bot exist?</h2>
|
||||
|
@ -12,19 +12,19 @@ function about() {
|
|||
<a href="/assets/screenshots/oldplanningmsg.png" target="_blank">
|
||||
these planning messages
|
||||
</a>{" "}
|
||||
and I thought that this should be automated. Some time later the
|
||||
first version of li'l Judd was born. Today the bot has more features
|
||||
and keeps getting more of them! It is designed to actually improve
|
||||
the Splatoon experience and not be the 10000th moderation and
|
||||
general utility bot with the same features as all bots.
|
||||
and I thought that this should be automated. Some time later the first
|
||||
version of li'l Judd was born. Today the bot has more features and
|
||||
keeps getting more of them! It is designed to actually improve the
|
||||
Splatoon experience and not be the 10000th moderation and general
|
||||
utility bot with the same features as all bots.
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Who is behind this?</h2>
|
||||
<p>
|
||||
The bot is currently being developed by{" "}
|
||||
<a href="/contact">moonleay</a> (hey that's me!) with
|
||||
occasional help from his friends!
|
||||
<a href="/contact">moonleay</a> (hey that's me!) with occasional
|
||||
help from his friends!
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
|
@ -36,17 +36,17 @@ function about() {
|
|||
<a href="https://git.moonleay.net/DiscordBots/lilJudd">
|
||||
read the code
|
||||
</a>
|
||||
and if you still don't trust me, you can always host the bot
|
||||
yourself! A guide on how to do that can be found in the README of
|
||||
the git project.
|
||||
and if you still don't trust me, you can always host the bot yourself!
|
||||
A guide on how to do that can be found in the README of the git
|
||||
project.
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Where is my data stored?</h2>
|
||||
<p>
|
||||
Your data is stored on a VPS from Contabo in Germany. The bot used
|
||||
to be hosted on a server in my basement, but I moved it to a VPS,
|
||||
because my internet connection was not stable enough.
|
||||
Your data is stored on a VPS from Contabo in Germany. The bot used to
|
||||
be hosted on a server in my basement, but I moved it to a VPS, because
|
||||
my internet connection was not stable enough.
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
|
@ -66,13 +66,12 @@ function about() {
|
|||
<section>
|
||||
<h2>Hey, there is this really cool idea I have! Can you add it?</h2>
|
||||
<p>
|
||||
Just message me! I can't promise anything, but I am always open to
|
||||
new ideas and improvements! You can find ways to contact me{" "}
|
||||
Just message me! I can't promise anything, but I am always open to new
|
||||
ideas and improvements! You can find ways to contact me{" "}
|
||||
<a href="/contact">here</a>.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import Layout from "~/components/Layout";
|
||||
import "../styles/pages/acknowledgements.scss";
|
||||
|
||||
function acknowledgements() {
|
||||
return (
|
||||
<>
|
||||
<div class="acknowledgements">
|
||||
<Layout site="acknowledgements">
|
||||
<h1>Acknowledgements</h1>
|
||||
<section>
|
||||
<table>
|
||||
|
@ -142,10 +142,7 @@ function acknowledgements() {
|
|||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a
|
||||
href="https://github.com/JetBrains/Exposed"
|
||||
target="_blank"
|
||||
>
|
||||
<a href="https://github.com/JetBrains/Exposed" target="_blank">
|
||||
repo
|
||||
</a>
|
||||
</td>
|
||||
|
@ -192,8 +189,7 @@ function acknowledgements() {
|
|||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
</div>
|
||||
</>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,60 +1,270 @@
|
|||
import "../styles/pages/config.scss";
|
||||
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: HTMLInputElement;
|
||||
let timePlanningRef: HTMLInputElement;
|
||||
let pingableRolesRef: 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(() => (timezoneRef.value = timezone()));
|
||||
createEffect(
|
||||
() => (timePlanningRef.checked = config.features.timePlanning.enabled),
|
||||
);
|
||||
createEffect(
|
||||
() =>
|
||||
(pingableRolesRef.checked = config.features.timePlanning.pingableRoles),
|
||||
);
|
||||
|
||||
return (
|
||||
<Layout site="config">
|
||||
<h3 class="text-center">Configure li'l Judd in</h3>
|
||||
<div>
|
||||
<h3 class={"centered"}>Configure li'l Judd in</h3>
|
||||
<div class={"config"}>
|
||||
<div>
|
||||
<div class={"horizontal centered"}>
|
||||
<div class="flex-row centered">
|
||||
<img
|
||||
class={"guildpfp"}
|
||||
src="https://cdn.discordapp.com/icons/1040502664506646548/bb5a51c4659cf47bdd942bb11e974da7.webp?size=240"
|
||||
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>li'l Judds home base</h1>
|
||||
<h1>{guild()?.name ?? "li'l Judds home base"}</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<article>
|
||||
<h2>Timezone</h2>
|
||||
<p>Set the timezone for your server.</p>
|
||||
<label>
|
||||
<select>
|
||||
<option value="-1">UTC-1:00</option>
|
||||
<option value="0">UTC+0:00</option>
|
||||
<option value="1">UTC+1:00</option>
|
||||
</select>
|
||||
</label>
|
||||
</article>
|
||||
<section>
|
||||
<div class={"centered"}>
|
||||
<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={timezoneRef!}
|
||||
// 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>
|
||||
</div>
|
||||
<article>
|
||||
<div class={"horizontal"}>
|
||||
<h3>Time Planning</h3>
|
||||
<input type="checkbox" id="time planning" />
|
||||
</div>
|
||||
<label class={"horizontal"}>
|
||||
<p class={"marg_right_5px"}>Target channel:</p>
|
||||
<select>
|
||||
<option value="id">Channel 1</option>
|
||||
<option value="id">Channel 2</option>
|
||||
<option value="id">Channel 3</option>
|
||||
<option value="id">Channel 4</option>
|
||||
</select>
|
||||
<label for="timePlanning" class="flex-row">
|
||||
<p>Time Planning </p>
|
||||
<FontAwesomeIcon
|
||||
icon={
|
||||
config.features.timePlanning.enabled ? faToggleOn : faToggleOff
|
||||
}
|
||||
size="xl"
|
||||
/>
|
||||
</label>
|
||||
<div class={"horizontal"}>
|
||||
<h4>Enable pingable Roles</h4>
|
||||
<input type="checkbox" id="pingableroles" />
|
||||
<input
|
||||
hidden
|
||||
type="checkbox"
|
||||
id="timePlanning"
|
||||
ref={timePlanningRef!}
|
||||
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={pingableRolesRef!}
|
||||
onInput={(e) =>
|
||||
setConfig(
|
||||
"features",
|
||||
"timePlanning",
|
||||
"pingableRoles",
|
||||
e.target.checked,
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<button>Apply</button>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
124
src/routes/config/index.tsx
Normal file
124
src/routes/config/index.tsx
Normal file
|
@ -0,0 +1,124 @@
|
|||
import { getSession } from "@auth/solid-start";
|
||||
import {
|
||||
faBadgeCheck,
|
||||
faCircleExclamation,
|
||||
faPlus,
|
||||
} from "@fortawesome/pro-regular-svg-icons";
|
||||
import { useNavigate } from "@solidjs/router";
|
||||
import { eq } from "drizzle-orm";
|
||||
import createClient from "openapi-fetch";
|
||||
import { For, createResource } from "solid-js";
|
||||
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 initialValue = () => ({
|
||||
success: null as boolean | null,
|
||||
guilds: [] as {
|
||||
id: string;
|
||||
name: string;
|
||||
icon: string | null | undefined;
|
||||
}[],
|
||||
});
|
||||
|
||||
const getPayload = async (): 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 { GET } = createClient<paths>({
|
||||
baseUrl: "https://discord.com/api/v10",
|
||||
});
|
||||
const { data: guilds, error } = await GET("/users/@me/guilds", {
|
||||
headers: { Authorization: `Bearer ${DISCORD_ACCESS_TOKEN}` },
|
||||
});
|
||||
|
||||
console.log("guilds", guilds);
|
||||
|
||||
if (error) {
|
||||
console.log(error);
|
||||
return { success: false, message: "Error on discord api request!" };
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
guilds:
|
||||
guilds
|
||||
?.filter((e) => parseInt(e.permissions) & (1 << 5))
|
||||
.map(({ id, name, icon }) => ({ id, name, icon })) ?? [],
|
||||
};
|
||||
};
|
||||
|
||||
function index() {
|
||||
const navigator = useNavigate();
|
||||
|
||||
const [payload] = createResource(async () => {
|
||||
const payload = await getPayload();
|
||||
|
||||
if (!payload.success) {
|
||||
console.log(payload.message, "No success");
|
||||
navigator("/", { replace: false });
|
||||
return initialValue();
|
||||
}
|
||||
console.log("success");
|
||||
return payload;
|
||||
});
|
||||
|
||||
const icons = [faPlus, faCircleExclamation, faBadgeCheck];
|
||||
const colors = [undefined, "orange", "green"];
|
||||
|
||||
return (
|
||||
<Layout site="config">
|
||||
<h3 class="text-center">Configure li'l Judd in</h3>
|
||||
<div>
|
||||
<For each={payload()?.guilds ?? []}>
|
||||
{(guild, i) => (
|
||||
<a href={`/config/${guild.id}`} 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}</h1>
|
||||
<FontAwesomeIcon
|
||||
// beat={i() % 3 === 1}
|
||||
color={colors[i() % 3]}
|
||||
icon={icons[i() % 3]}
|
||||
size="xl"
|
||||
/>
|
||||
</a>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
export default index;
|
|
@ -1,19 +1,16 @@
|
|||
import Layout from "~/components/Layout";
|
||||
import "../styles/pages/contact.scss";
|
||||
|
||||
function contact() {
|
||||
return (
|
||||
<>
|
||||
<div class="contact">
|
||||
<Layout site="contact">
|
||||
<h1>Contact</h1>
|
||||
<section class="contact">
|
||||
<section>
|
||||
<a href="mailto:contact@moonleay.net" target="_blank">
|
||||
<img src="/assets/icons/email.svg" alt="Email" />
|
||||
contact@moonleay.net
|
||||
</a>
|
||||
<a
|
||||
href="https://discord.com/users/372703841151614976"
|
||||
target="_blank"
|
||||
>
|
||||
<a href="https://discord.com/users/372703841151614976" target="_blank">
|
||||
<img src="/assets/icons/discord.svg" alt="Discord" />
|
||||
@moonleay
|
||||
</a>
|
||||
|
@ -22,8 +19,7 @@ function contact() {
|
|||
li'l Judd's home base
|
||||
</a>
|
||||
</section>
|
||||
</div>
|
||||
</>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import ImageSection from "~/components/ImageSection";
|
||||
import Layout from "~/components/Layout";
|
||||
import "../styles/pages/features.scss";
|
||||
|
||||
function features() {
|
||||
return (
|
||||
<>
|
||||
<div class="features">
|
||||
<h1 class="title">Features</h1>
|
||||
<Layout site="features">
|
||||
<h1>Features</h1>
|
||||
<div class="gridlayout">
|
||||
<ImageSection
|
||||
imgUrl="/assets/screenshots/timeplanner.png"
|
||||
|
@ -44,8 +44,7 @@ function features() {
|
|||
note="If you have a specific feature request, you can contact me on Discord: @moonleay or email: contact at moonleay dot net"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import Layout from "~/components/Layout";
|
||||
import "../styles/pages/how-do-i.scss";
|
||||
|
||||
function howDoI() {
|
||||
return (
|
||||
<>
|
||||
<h1 class="hdi-title">How do I...?</h1>
|
||||
<section class="hdi-section">
|
||||
<Layout site="how-do-i">
|
||||
<h1>How do I...?</h1>
|
||||
<section>
|
||||
<h2>.. enable / disable certain features?</h2>
|
||||
<p>
|
||||
Features can be enabled and disables using the <code>/feature</code>
|
||||
|
@ -26,7 +27,7 @@ function howDoI() {
|
|||
{/* <p><code>/feature feature:Time Planning Feature set:Enable channel:#ich-kann-heute</code></p> */}
|
||||
</div>
|
||||
</section>
|
||||
<section class="hdi-section">
|
||||
<section>
|
||||
<h2>.. create a match?</h2>
|
||||
<p>
|
||||
You can create a match time using the <code>/match</code> command.
|
||||
|
@ -47,14 +48,14 @@ function howDoI() {
|
|||
{/* <p><code>/match match:Ladder Match timestamp:24.12.2069 04:20 opponent:Forbidden</code></p> */}
|
||||
</div>
|
||||
</section>
|
||||
<section class="hdi-footernotesection">
|
||||
<section class="note">
|
||||
<p>
|
||||
Is something missing here?
|
||||
<br />
|
||||
Please <a href="/contact">contact me</a>!
|
||||
</p>
|
||||
</section>
|
||||
</>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import Layout from "~/components/Layout";
|
||||
import "../styles/pages/imprint.scss";
|
||||
|
||||
function imprint() {
|
||||
return (
|
||||
<>
|
||||
<section class="imprint">
|
||||
<Layout site="imprint">
|
||||
<section>
|
||||
<h1>Imprint</h1>
|
||||
<section>
|
||||
<a href="/contact">
|
||||
|
@ -54,7 +55,7 @@ function imprint() {
|
|||
</h5>
|
||||
</section>
|
||||
</section>
|
||||
</>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import Layout from "~/components/Layout";
|
||||
import "../styles/pages/index.scss";
|
||||
|
||||
function index() {
|
||||
return (
|
||||
<>
|
||||
<section class="index">
|
||||
<Layout site="index">
|
||||
<section>
|
||||
<h1>li'l Judd</h1>
|
||||
<h5>The competetive Splatoon Bot</h5>
|
||||
<div>
|
||||
|
@ -13,7 +14,7 @@ function index() {
|
|||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import Layout from "~/components/Layout";
|
||||
import "../styles/pages/privacy-policy.scss";
|
||||
|
||||
function privacyPolicy() {
|
||||
return (
|
||||
<>
|
||||
<div class="privacyPolicy">
|
||||
<Layout site="privacyPolicy">
|
||||
<div>
|
||||
<h1>Privacy Policy for li'l Judd</h1>
|
||||
<h4>Last updated: 2023-12-05</h4>
|
||||
|
@ -57,8 +57,8 @@ function privacyPolicy() {
|
|||
</ul>
|
||||
<h3>3.2 Usage Data</h3>
|
||||
<p>
|
||||
We may collect information on how you interact with our bot,
|
||||
including but not limited to:
|
||||
We may collect information on how you interact with our bot, including
|
||||
but not limited to:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
|
@ -96,8 +96,8 @@ function privacyPolicy() {
|
|||
<ul>
|
||||
<li>
|
||||
<p>
|
||||
- Consent: You have given your consent for the processing of
|
||||
your personal data for one or more specific purposes.
|
||||
- Consent: You have given your consent for the processing of your
|
||||
personal data for one or more specific purposes.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -111,9 +111,8 @@ function privacyPolicy() {
|
|||
<section>
|
||||
<h2>6. Data Sharing</h2>
|
||||
<p>
|
||||
We do not sell, trade, or otherwise transfer your personal
|
||||
information to third parties. However, we may share your information
|
||||
with:
|
||||
We do not sell, trade, or otherwise transfer your personal information
|
||||
to third parties. However, we may share your information with:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
|
@ -151,8 +150,8 @@ function privacyPolicy() {
|
|||
</li>
|
||||
<li>
|
||||
<p>
|
||||
- Right to erasure: You can request the deletion of your
|
||||
personal data.
|
||||
- Right to erasure: You can request the deletion of your personal
|
||||
data.
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -160,8 +159,8 @@ function privacyPolicy() {
|
|||
<section>
|
||||
<h2>9. Changes to this Privacy Policy</h2>
|
||||
<p>
|
||||
We may update this Privacy Policy to reflect changes in our
|
||||
practices. The updated version will be posted on
|
||||
We may update this Privacy Policy to reflect changes in our practices.
|
||||
The updated version will be posted on
|
||||
https://liljudd.ink/privacy-policy.
|
||||
</p>
|
||||
</section>
|
||||
|
@ -172,8 +171,7 @@ function privacyPolicy() {
|
|||
please contact us at contact@moonleay.net.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import Layout from "~/components/Layout";
|
||||
import "../styles/pages/stack.scss";
|
||||
|
||||
function stack() {
|
||||
return (
|
||||
<>
|
||||
<h1 class="stack-title">The Stack</h1>
|
||||
<section class="stack-section">
|
||||
<Layout site="stack">
|
||||
<h1>The Stack</h1>
|
||||
<section>
|
||||
<img src="/assets/logos/kotlin.svg" alt="Kotlin 'K' logo" />
|
||||
<div class="stackgrid_3 stackitm">
|
||||
<h1>The Kotlin programming language</h1>
|
||||
|
@ -14,14 +15,14 @@ function stack() {
|
|||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="stack-section">
|
||||
<section>
|
||||
<img src="/assets/logos/kord.png" alt="The Kord logo" />
|
||||
<div class="stackgrid_3 stackitm">
|
||||
<h1>The Kord library</h1>
|
||||
<p>A Kotlin library for creating Discord bots. Pretty bare bones.</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="stack-section">
|
||||
<section>
|
||||
<img
|
||||
src="/assets/logos/kordextensions.png"
|
||||
alt="The Kord-Extensions logo"
|
||||
|
@ -31,7 +32,7 @@ function stack() {
|
|||
<p>A Kotlin library to improve the Kord experience.</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="stack-section">
|
||||
<section>
|
||||
<img src="/assets/logos/pgelephant.png" alt="The PostgreSQL elephant" />
|
||||
<div class="stackgrid_3 stackitm">
|
||||
<h1>The PostgreSQL database</h1>
|
||||
|
@ -47,7 +48,7 @@ function stack() {
|
|||
<a href="/acknowledgements">Acknowledgements</a>.
|
||||
</p>
|
||||
</section>
|
||||
</>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
import Layout from "~/components/Layout";
|
||||
import "../styles/pages/terms-of-service.scss";
|
||||
|
||||
function termsOfService() {
|
||||
return (
|
||||
<>
|
||||
<div class="termsOfService">
|
||||
<Layout site="termsOfService">
|
||||
<h1>Terms of Service</h1>
|
||||
<div>
|
||||
<h2>Usage Agreement</h2>
|
||||
<p>
|
||||
By inviting the bot and using its features (commands, planning
|
||||
system) are you agreeing to the below mentioned Terms and Privacy
|
||||
Policy (Policy) of the bot.
|
||||
By inviting the bot and using its features (commands, planning system)
|
||||
are you agreeing to the below mentioned Terms and Privacy Policy
|
||||
(Policy) of the bot.
|
||||
<br />
|
||||
You acknowledge that you have the privilege to use the bot freely on
|
||||
any Discord Server (Server) you share with it, that you can invite
|
||||
it to any Server that you have "Manage Server" rights for and that
|
||||
this privilege might get revoked for you, if you're subject of
|
||||
breaking the terms and/or policy of this bot, or the{" "}
|
||||
any Discord Server (Server) you share with it, that you can invite it
|
||||
to any Server that you have "Manage Server" rights for and that this
|
||||
privilege might get revoked for you, if you're subject of breaking the
|
||||
terms and/or policy of this bot, or the{" "}
|
||||
<a href="https://discord.com/terms" target="_blank">
|
||||
Terms of Service
|
||||
</a>
|
||||
|
@ -33,11 +33,11 @@ function termsOfService() {
|
|||
Discord Inc
|
||||
</a>
|
||||
.<br />
|
||||
Through Inviting the bot may it collect specific data as described
|
||||
in its Policy.
|
||||
Through Inviting the bot may it collect specific data as described in
|
||||
its Policy.
|
||||
<br />
|
||||
The intended usage of this data is for core functionalities of the
|
||||
bot such as command handling, guild-specific settings and the
|
||||
The intended usage of this data is for core functionalities of the bot
|
||||
such as command handling, guild-specific settings and the
|
||||
time-planning system.
|
||||
<br />
|
||||
</p>
|
||||
|
@ -45,11 +45,11 @@ function termsOfService() {
|
|||
<div>
|
||||
<h2>Intended Age</h2>
|
||||
<p>
|
||||
The bot may not be used by individuals under the minimal age
|
||||
described in Discord's Terms of Service.
|
||||
The bot may not be used by individuals under the minimal age described
|
||||
in Discord's Terms of Service.
|
||||
<br />
|
||||
Doing so will be seen as a violation of these terms and will result
|
||||
in a removal of the bot from any servers you own.
|
||||
Doing so will be seen as a violation of these terms and will result in
|
||||
a removal of the bot from any servers you own.
|
||||
<br />
|
||||
</p>
|
||||
</div>
|
||||
|
@ -60,8 +60,7 @@ function termsOfService() {
|
|||
<br />
|
||||
Any direct connection to Discord or any of its Trademark objects is
|
||||
purely coincidental. We do not claim to have the copyright ownership
|
||||
of any of Discord's assets, trademarks or other intellectual
|
||||
property.
|
||||
of any of Discord's assets, trademarks or other intellectual property.
|
||||
<br />
|
||||
</p>
|
||||
</div>
|
||||
|
@ -71,26 +70,25 @@ function termsOfService() {
|
|||
The owner of the bot may not be made liable for individuals breaking
|
||||
these Terms at any given time.
|
||||
<br />
|
||||
He has faith in the end users being truthfull about their
|
||||
information and not misusing this bot or the services of Discord Inc
|
||||
in a malicious way.
|
||||
He has faith in the end users being truthfull about their information
|
||||
and not misusing this bot or the services of Discord Inc in a
|
||||
malicious way.
|
||||
<br />
|
||||
We reserve the right to update these terms at our own discretion,
|
||||
giving you a 1-Week (7 days) period to opt out of these terms if
|
||||
you're not agreeing with the new changes. You may opt out by
|
||||
Removing the bot from any Server you have the rights for.
|
||||
you're not agreeing with the new changes. You may opt out by Removing
|
||||
the bot from any Server you have the rights for.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Contact</h2>
|
||||
<p>
|
||||
People may get in contact through e-mail at contact@moonleay.net, or
|
||||
through the official Support Discord of the Bot. Other ways of
|
||||
support may be provided but aren't guaranteed.
|
||||
through the official Support Discord of the Bot. Other ways of support
|
||||
may be provided but aren't guaranteed.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,46 +5,73 @@ nav {
|
|||
backdrop-filter: blur(5px);
|
||||
position: sticky;
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
|
||||
.navElem {
|
||||
margin: 0.5rem 1rem;
|
||||
transition: 0.5s;
|
||||
ul .navElem a {
|
||||
color: white;
|
||||
padding: 0 1rem;
|
||||
vertical-align: middle;
|
||||
|
||||
&:hover {
|
||||
color: rgb(96 59 255) !important;
|
||||
|
||||
.swap {
|
||||
svg path {
|
||||
transition: color 0.5s 0.5s;
|
||||
}
|
||||
|
||||
.textBx {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.primary {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#logo {
|
||||
.secondary {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
img,
|
||||
.swap,
|
||||
.swap svg {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 100%;
|
||||
max-width: initial;
|
||||
max-height: initial;
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
.swap {
|
||||
position: relative;
|
||||
|
||||
> * {
|
||||
transition: opacity 0.5s;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
img {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.secondary {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
span,
|
||||
> svg path {
|
||||
transition: color 0.5s;
|
||||
}
|
||||
|
||||
.lower {
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
ul {
|
||||
flex-direction: row;
|
||||
padding: 0 2rem;
|
||||
justify-content: space-between;
|
||||
|
||||
.navElem:last-child {
|
||||
margin-left: auto;
|
||||
}
|
||||
ul {
|
||||
height: 72px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,3 +53,24 @@ a {
|
|||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.flex-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
|
||||
&.centered {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&.thick {
|
||||
align-items: initial;
|
||||
}
|
||||
|
||||
&.responsive {
|
||||
@media (max-width: 768px) {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
.aboutdiv {
|
||||
.about {
|
||||
max-width: 1100px;
|
||||
margin: 1rem auto;
|
||||
width: 100%;
|
||||
|
|
|
@ -1,38 +1,34 @@
|
|||
.guildpfp {
|
||||
.config {
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.guildpfp {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
.horizontal {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
h1 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
p, h3, h4 {
|
||||
|
||||
label {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.marg_right_5px {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.centered {
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.config {
|
||||
article {
|
||||
section,
|
||||
a {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
border-radius: 4px;
|
||||
padding: 1rem;
|
||||
max-width: 1100px;
|
||||
margin: 1rem auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sub {
|
||||
margin-left: 10px;
|
||||
|
||||
&.disabled {
|
||||
pointer-events: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
}
|
||||
text-align: center;
|
||||
|
||||
.contact {
|
||||
section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.title {
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
text-align: center;
|
||||
margin-bottom: 1.2rem;
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
.hdi-title {
|
||||
.how-do-i {
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
text-align: center;
|
||||
margin-bottom: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.hdi-section {
|
||||
section {
|
||||
max-width: 1100px;
|
||||
margin: 1rem auto;
|
||||
width: 100%;
|
||||
|
@ -39,9 +40,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hdi-footernotesection {
|
||||
.note {
|
||||
max-width: 1100px;
|
||||
margin: 1rem auto;
|
||||
width: 100%;
|
||||
|
@ -61,4 +62,5 @@
|
|||
color: rgb(96 59 255) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
.stack-title {
|
||||
.stack {
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
text-align: center;
|
||||
margin-bottom: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.stack-section {
|
||||
section {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
border-radius: 4px;
|
||||
margin: 1rem;
|
||||
|
@ -33,9 +34,9 @@
|
|||
align-self: center;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.stack-note {
|
||||
.note {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
border-radius: 4px;
|
||||
margin: 1rem;
|
||||
|
@ -58,4 +59,5 @@
|
|||
color: rgb(96 59 255) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue