= {};
+
+ if (body.admin != null && typeof body.admin === 'boolean') newBody.admin = body.admin;
+ if (body.banned != null && typeof body.banned === 'boolean') newBody.banned = body.banned;
+
+ let res;
+ try {
+ res = await db
+ .updateTable('jams')
+ .set(newBody)
+ .where('id', '=', updatingUser.id)
+ .executeTakeFirstOrThrow();
+ } catch (err) {
+ return new Response('', {
+ status: 500
+ });
+ }
+
+ return new Response(null, {
+ status: 204
+ })
+}
+
+export async function DELETE(request: NextRequest, {params}: {params: {user:string}}) {
+ // verify user
+ const cookieStore = cookies();
+ const token = cookieStore.get('token')?.value;
+
+ if (token == null) return new Response('', {
+ status: 401
+ });
+
+ let existingToken = await db
+ .selectFrom('tokens')
+ .where('tokens.id', '=', token)
+ .select('owner')
+ .executeTakeFirst();
+
+ if (existingToken == null) return new Response('', {
+ status: 401
+ });
+
+ let existingUser = await db
+ .selectFrom('users')
+ .where('users.id', '=', existingToken.owner)
+ .select(['admin', 'id'])
+ .executeTakeFirst();
+
+ if (existingUser == null) return new Response('', {
+ status: 401
+ });
+
+ const name = params.user;
+
+ if (name == null) return new Response('', {
+ status: 400
+ });
+
+ let deletingUser = await db
+ .selectFrom('users')
+ .where('users.id', '=', name)
+ .select('id')
+ .executeTakeFirst();
+
+ console.log(deletingUser, existingUser)
+
+ if (deletingUser == null) return new Response('', {
+ status: 400
+ });
+
+ if (!existingUser?.admin && deletingUser.id != existingUser.id) return new Response('you are NOT that guy.', {
+ status: 401
+ });
+
+ try {
+ await db
+ .deleteFrom('users')
+ .where('users.id', '=', deletingUser.id)
+ .executeTakeFirstOrThrow();
+ } catch (err) {
+ return new Response('', {
+ status: 500
+ });
+ }
+
+ return new Response(null, {
+ status: 204
+ });
+}
\ No newline at end of file
diff --git a/src/app/jams/api/users/route.ts b/src/app/jams/api/users/route.ts
new file mode 100644
index 0000000..e719c47
--- /dev/null
+++ b/src/app/jams/api/users/route.ts
@@ -0,0 +1,18 @@
+import { db } from "@/lib/mastoauth/kysely";
+import { NextRequest } from "next/server";
+
+export async function GET(request: NextRequest) {
+ const until = parseInt(request.nextUrl.searchParams.get("until") || Date.now().toString());
+ let users = await db
+ .selectFrom('users')
+ .where('users.joined', '<', until)
+ .limit(20)
+ .selectAll()
+ .execute();
+
+ if (users == null) return new Response('', {
+ status: 500
+ });
+
+ return Response.json(users);
+}
\ No newline at end of file
diff --git a/src/app/jams/content/[content]/page.tsx b/src/app/jams/content/[content]/page.tsx
new file mode 100644
index 0000000..db77a57
--- /dev/null
+++ b/src/app/jams/content/[content]/page.tsx
@@ -0,0 +1,130 @@
+import { MainLayout } from "@/layout/MainLayout/MainLayout";
+import { cookies } from "next/headers";
+import { db, JamTable, UserTable } from "@/lib/mastoauth/kysely";
+import { ConditionalNull } from "@/components/utility/Conditional";
+import Link from "next/link";
+import Markdown from "react-markdown";
+import rehypeRaw from "rehype-raw";
+import { JSONContentTable, JSONJamTable, JSONJudgementTable, JSONUserTable } from "@/lib/mastoauth/realtypes";
+import { notFound } from "next/navigation";
+
+export default async function Home({
+ params,
+ searchParams
+}: {
+ params: {
+ content: string
+ },
+ searchParams: {
+ until: string
+ }
+}) {
+ const curDate = Date.now();
+ const curPage = parseInt(searchParams?.until) || curDate;
+
+ const cookieStore = cookies();
+ const token = cookieStore.get('token')?.value;
+ let existingUser:JSONUserTable;
+ if (token != null) {
+ let existingToken = await db
+ .selectFrom('tokens')
+ .where('tokens.id', '=', token)
+ .select('owner')
+ .executeTakeFirst();
+
+ if (existingToken != null) {
+ existingUser = await db
+ .selectFrom('users')
+ .where('users.id', '=', existingToken.owner)
+ .selectAll()
+ .executeTakeFirst() as unknown as JSONUserTable;
+ }
+ }
+
+ const curContent = params.content;
+
+ if (curContent == null) return notFound();
+
+ // It's a JSONJamTable. I don't know why TS hates `number` => `string` conversion.
+ let content = await db
+ .selectFrom('content')
+ .where('content.id', '=', curContent)
+ .selectAll()
+ .executeTakeFirst() as unknown as JSONContentTable;
+
+ console.log(curContent, content)
+
+ if (content == null) return notFound();
+
+ // It's a JSONJamTable. I don't know why TS hates `number` => `string` conversion.
+ let contentOwner = await db
+ .selectFrom('users')
+ .where('users.id', '=', content.author_id)
+ .selectAll()
+ .executeTakeFirst() as unknown as JSONUserTable;
+
+ // It's a JSONJamTable. I don't know why TS hates `number` => `string` conversion.
+ let submittedJam = await db
+ .selectFrom('jams')
+ .where('jams.id', '=', content.jam_id)
+ .selectAll()
+ .executeTakeFirst() as unknown as JSONJamTable;
+
+ let judgements = await db
+ .selectFrom('judgements')
+ .where('judgements.content_id', '=', content.id)
+ .where('judgements.published', '<=', curPage)
+ .limit(20)
+ .selectAll()
+ .execute() as unknown[] as JSONJudgementTable[];
+
+ console.log(content, contentOwner)
+
+ return (
+
+ {content.name}
+ {content.url}
+ Submitted by {`${contentOwner.username}@${contentOwner.instance}`} to {submittedJam.name} - {new Date(parseInt(content.submitted)).toDateString()}
+ {content.description}
+
+
+
+ Judgements
+ {judgements.map(async (judgement:JSONJudgementTable) => {
+ let judgementOwner = await db
+ .selectFrom('users')
+ .where('users.id', '=', judgement.author_id)
+ .selectAll()
+ .executeTakeFirst();
+
+ if (judgementOwner == null) return An error occured.
;
+
+ return (
+
+
+
+
+
Published {new Date(parseInt(judgement.published)).toDateString()}
+
{judgement.content}
+
);
+ })}
+
+
+ Later
+
+ {/* */}
+ Earlier
+ {/* */}
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/app/jams/jam/[jam]/page.tsx b/src/app/jams/jam/[jam]/page.tsx
new file mode 100644
index 0000000..983abc8
--- /dev/null
+++ b/src/app/jams/jam/[jam]/page.tsx
@@ -0,0 +1,119 @@
+import { MainLayout } from "@/layout/MainLayout/MainLayout";
+import { cookies } from "next/headers";
+import { db, JamTable, UserTable } from "@/lib/mastoauth/kysely";
+import { ConditionalNull } from "@/components/utility/Conditional";
+import Link from "next/link";
+import Markdown from "react-markdown";
+import rehypeRaw from "rehype-raw";
+import { JSONContentTable, JSONJamTable, JSONUserTable } from "@/lib/mastoauth/realtypes";
+import { notFound } from "next/navigation";
+
+export default async function Home({
+ params,
+ searchParams
+}: {
+ params: {
+ jam: string
+ },
+ searchParams: {
+ until: string
+ }
+}) {
+ const curDate = Date.now();
+ const curPage = parseInt(searchParams?.until) || curDate;
+
+ const cookieStore = cookies();
+ const token = cookieStore.get('token')?.value;
+ let existingUser;
+ if (token != null) {
+ let existingToken = await db
+ .selectFrom('tokens')
+ .where('tokens.id', '=', token)
+ .select('owner')
+ .executeTakeFirst();
+
+ if (existingToken != null) {
+ existingUser = await db
+ .selectFrom('users')
+ .where('users.id', '=', existingToken.owner)
+ .selectAll()
+ .executeTakeFirst();
+ }
+ }
+
+ const curJam = params.jam;
+
+ if (curJam == null) return notFound();
+
+ // It's a JSONJamTable. I don't know why TS hates `number` => `string` conversion.
+ let jam = await db
+ .selectFrom('jams')
+ .where('jams.id', '=', curJam)
+ .selectAll()
+ .executeTakeFirst() as unknown as JSONJamTable;
+
+ const started = Date.now() >= parseInt(jam.date_start);
+ const ended = Date.now() >= parseInt(jam.date_end);
+
+ // It's a JSONJamTable. I don't know why TS hates `number` => `string` conversion.
+ let jamOwner = await db
+ .selectFrom('users')
+ .where('users.id', '=', jam.author_id)
+ .selectAll()
+ .executeTakeFirst() as unknown as JSONUserTable;
+
+ if (jam == null) return notFound();
+
+ let content = await db
+ .selectFrom('content')
+ .where('content.jam_id', '=', jam.id)
+ .where('content.submitted', '<=', curPage)
+ .limit(20)
+ .selectAll()
+ .execute() as unknown[] as JSONContentTable[];
+
+ console.log(jam, jamOwner)
+
+ return (
+
+ {jam.name}
+ {started ? "Started" : "Starts"} {new Date(parseInt(jam.date_start)).toDateString()} - {ended ? "Ended" : "Ends"} {new Date(parseInt(jam.date_end)).toDateString()}
+ Hosted by {`${jamOwner.username}@${jamOwner.instance}`}
+ {jam.description}
+
+
+
+ Submissions
+ {content.map(async (content:JSONContentTable) => {
+ let contentOwner = await db
+ .selectFrom('users')
+ .where('users.id', '=', content.author_id)
+ .selectAll()
+ .executeTakeFirst();
+
+ if (contentOwner == null) return An error occured.
;
+
+ return ();
+ })}
+
+
+ Later
+
+ {/* */}
+ Earlier
+ {/* */}
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/app/jams/oauth/code/route.ts b/src/app/jams/oauth/code/route.ts
new file mode 100644
index 0000000..8a73fe4
--- /dev/null
+++ b/src/app/jams/oauth/code/route.ts
@@ -0,0 +1,120 @@
+import { AppTable, db, UserTable } from "@/lib/mastoauth/kysely";
+import { MastoAuth } from "@/lib/mastoauth/mastoauth";
+import { nanoid } from "nanoid";
+import { cookies } from "next/headers";
+import { NextRequest, NextResponse } from "next/server";
+import { stringify } from "node:querystring";
+
+export async function GET(request: NextRequest) {
+ const cookieStore = cookies();
+ const instance = cookieStore.get('instance')?.value;
+ const code = request.nextUrl.searchParams.get("code");
+ if (code == null || instance == null) return new Response('', {
+ status: 400
+ });
+ const mauth = new MastoAuth(instance);
+
+ // Get instance app
+ let existingInstanceApp = await db
+ .selectFrom('apps')
+ .where('apps.instance_domain', '=', instance)
+ .selectAll()
+ .executeTakeFirst();
+
+ if (existingInstanceApp == null) {
+ return new Response('', {
+ status: 500
+ });
+ }
+
+ // Get temporary user token
+ let tUserToken = await mauth.getApplicationToken(existingInstanceApp, code);
+
+ console.log(tUserToken);
+
+ if (tUserToken == null) return new Response('', {
+ status: 500
+ });
+
+ // Test for user existence
+ let tUserExists = await mauth.verifyUser(tUserToken.token_type + " " + tUserToken.access_token);
+
+ console.log(tUserExists,);
+
+ if (tUserExists == null) return new Response('', {
+ status: 400
+ });
+
+ let currentUser = {
+ id: nanoid(21),
+ instance,
+ username: tUserExists.acct,
+ admin: false,
+ url: tUserExists.url,
+ banned: false,
+ joined: Date.now()
+ } as UserTable;
+
+ // Get if user already is registered
+ let existingUser = await db
+ .selectFrom('users')
+ .where('users.url', '=', currentUser.url)
+ .selectAll()
+ .executeTakeFirst();
+
+ if (existingUser == null) {
+ try {
+ await db.insertInto('users')
+ .values(currentUser)
+ .returningAll()
+ .executeTakeFirstOrThrow();
+ } catch (err) {
+ return new Response('', {
+ status: 500
+ });
+ }
+ } else currentUser = existingUser;
+
+ // "I've seen enough, give token"
+
+ // Clear tokens older than a week
+
+ try {
+ await db.deleteFrom('tokens')
+ .where('tokens.expires', '<=', Date.now())
+ .executeTakeFirstOrThrow();
+ } catch (err) {
+ // whatever, who cares?
+ }
+
+ let currentToken = {
+ id: nanoid(21),
+ owner: currentUser.id,
+ created: Date.now(),
+ expires: Date.now() + 604800000 // 1 wk
+ };
+
+ // If not expired, give existing token?
+ let existingToken = await db
+ .selectFrom('tokens')
+ .where('tokens.owner', '=', currentUser.id)
+ .selectAll()
+ .executeTakeFirst();
+
+ if (existingToken != null) currentToken = existingToken;
+ else {
+ try {
+ await db.insertInto('tokens')
+ .values(currentToken)
+ .returningAll()
+ .executeTakeFirstOrThrow();
+ } catch (err) {
+ return new Response('', {
+ status: 500
+ });
+ }
+ }
+
+ cookieStore.set("token", currentToken.id);
+ return Response.redirect(new URL('/jams', request.url));
+}
\ No newline at end of file
diff --git a/src/app/jams/oauth/login/route.ts b/src/app/jams/oauth/login/route.ts
new file mode 100644
index 0000000..01c7081
--- /dev/null
+++ b/src/app/jams/oauth/login/route.ts
@@ -0,0 +1,52 @@
+import { AppTable, db } from "@/lib/mastoauth/kysely";
+import { MastoAuth } from "@/lib/mastoauth/mastoauth";
+import { cookies } from "next/headers";
+import { NextRequest } from "next/server";
+
+export async function GET(request: NextRequest) {
+ const cookieStore = cookies();
+ const instance = request.nextUrl.searchParams.get("instance");
+ if (instance == null) return new Response('', {
+ status: 400
+ });
+ const mauth = new MastoAuth(instance);
+
+ // Check if instance app exists
+ let existingInstanceApp = await db
+ .selectFrom('apps')
+ .where('apps.instance_domain', '=', instance)
+ .selectAll()
+ .executeTakeFirst();
+
+ console.log(instance);
+
+ if (existingInstanceApp == null) {
+ // Create new app
+ const temp_iapp = await mauth.newApplication();
+
+ if (temp_iapp == null) return new Response('', {
+ status: 400
+ });
+
+ existingInstanceApp = {
+ instance_domain: instance,
+ client_id: temp_iapp.client_id,
+ client_secret: temp_iapp.client_secret,
+ redirect_uri: temp_iapp.redirect_uri
+ } as AppTable;
+
+ try {
+ await db.insertInto('apps')
+ .values(existingInstanceApp)
+ .returningAll()
+ .executeTakeFirstOrThrow();
+ } catch (err) {
+ return new Response('', {
+ status: 500
+ });
+ }
+ }
+
+ cookieStore.set("instance", instance);
+ return Response.redirect(`https://${instance}/oauth/authorize?response_type=code&client_id=${existingInstanceApp.client_id}&redirect_uri=${existingInstanceApp.redirect_uri}&scope=read`);
+}
\ No newline at end of file
diff --git a/src/app/jams/oauth/logout/route.ts b/src/app/jams/oauth/logout/route.ts
new file mode 100644
index 0000000..63248c5
--- /dev/null
+++ b/src/app/jams/oauth/logout/route.ts
@@ -0,0 +1,27 @@
+import { db } from "@/lib/mastoauth/kysely";
+import { cookies } from "next/headers";
+import { NextRequest } from "next/server";
+
+export async function GET(request: NextRequest) {
+ const cookieStore = cookies();
+ const instance = cookieStore.get('instance')?.value;
+ if (instance == null) return new Response('', {
+ status: 400
+ });
+
+ // Delete token
+
+ try {
+ await db.deleteFrom('tokens')
+ .where('tokens.id', '=', instance)
+ .executeTakeFirstOrThrow();
+ } catch (err) {
+ return new Response('Something failed!', {
+ status: 500
+ });
+ }
+
+ cookieStore.delete("token");
+ cookieStore.delete("instance");
+ return Response.redirect(new URL('/jams', request.url));
+}
\ No newline at end of file
diff --git a/src/app/jams/page.tsx b/src/app/jams/page.tsx
new file mode 100644
index 0000000..b5300f5
--- /dev/null
+++ b/src/app/jams/page.tsx
@@ -0,0 +1,96 @@
+import { MainLayout } from "@/layout/MainLayout/MainLayout";
+import { cookies } from "next/headers";
+import { db, JamTable, UserTable } from "@/lib/mastoauth/kysely";
+import { ConditionalNull } from "@/components/utility/Conditional";
+import Link from "next/link";
+import Markdown from "react-markdown";
+import rehypeRaw from "rehype-raw";
+import { JSONJamTable } from "@/lib/mastoauth/realtypes";
+
+export default async function Home({
+ searchParams
+}: {
+ searchParams: {
+ until: string
+ }
+}) {
+ const curDate = Date.now();
+ const curPage = parseInt(searchParams?.until) || curDate;
+
+ const cookieStore = cookies();
+ const token = cookieStore.get('token')?.value;
+ let existingUser;
+ if (token != null) {
+ let existingToken = await db
+ .selectFrom('tokens')
+ .where('tokens.id', '=', token)
+ .select('owner')
+ .executeTakeFirst();
+
+ if (existingToken != null) {
+ existingUser = await db
+ .selectFrom('users')
+ .where('users.id', '=', existingToken.owner)
+ .selectAll()
+ .executeTakeFirst();
+ }
+ }
+
+ // It's a JSONJamTable. I don't know why TS hates `number` => `string` conversion.
+ let jams = await db
+ .selectFrom('jams')
+ .where('jams.created', '<=', curPage)
+ .limit(20)
+ .selectAll()
+ .execute() as unknown[] as JSONJamTable[];
+
+ return (
+
+
+ Some jams I host that you can participate in!
+ These serve many purposes. To create a community bond, to help creatives find their way, and most importantly, to make me popular. X3
+ Enjoy!
+
+ Log in with the Fediverse
+
+ Tested on Mastodon, GoToSocial, Pleroma, and Misskey
+
+
+ Logged in as {existingUser?.username}@{existingUser?.instance} (Logout)
+ Create Jam
+
+ Jams
+ {jams.map(async (jam:JSONJamTable) => {
+ const started = Date.now() >= parseInt(jam.date_start);
+ const ended = Date.now() >= parseInt(jam.date_end);
+
+ let jamOwner = await db
+ .selectFrom('users')
+ .where('users.id', '=', jam.author_id)
+ .selectAll()
+ .executeTakeFirst();
+
+ if (jamOwner == null) return An error occured.
;
+
+ return (
+
{jam.name}
+
Hosted by {`${jamOwner.username}@${jamOwner.instance}`} - {started ? "Started" : "Starts"} {new Date(parseInt(jam.date_start)).toDateString()} - {ended ? "Ended" : "Ends"} {new Date(parseInt(jam.date_end)).toDateString()}
+
{jam.description}
+
);
+ })}
+
+
+ Later
+
+ {/* */}
+ Earlier
+ {/* */}
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/app/jams/user/[user]/page.tsx b/src/app/jams/user/[user]/page.tsx
new file mode 100644
index 0000000..04e745a
--- /dev/null
+++ b/src/app/jams/user/[user]/page.tsx
@@ -0,0 +1,147 @@
+import { MainLayout } from "@/layout/MainLayout/MainLayout";
+import { cookies } from "next/headers";
+import { db, JamTable, UserTable } from "@/lib/mastoauth/kysely";
+import { ConditionalNull } from "@/components/utility/Conditional";
+import Link from "next/link";
+import Markdown from "react-markdown";
+import rehypeRaw from "rehype-raw";
+import { JSONContentTable, JSONJamTable, JSONJudgementTable, JSONUserTable } from "@/lib/mastoauth/realtypes";
+import { notFound } from "next/navigation";
+
+export default async function Home({
+ params,
+ searchParams
+}: {
+ params: {
+ user: string
+ },
+ searchParams: {
+ until: string
+ }
+}) {
+ const curDate = Date.now();
+ const curPage = parseInt(searchParams?.until) || curDate;
+ const curUser = params.user;
+
+ if (curUser == null) return notFound();
+
+ // It's a JSONJamTable. I don't know why TS hates `number` => `string` conversion.
+ let user = await db
+ .selectFrom('users')
+ .where('users.id', '=', curUser)
+ .selectAll()
+ .executeTakeFirst() as unknown as JSONUserTable;
+
+ if (user == null) return notFound();
+
+ let content = await db
+ .selectFrom('content')
+ .where('content.author_id', '=', user.id)
+ .where('content.submitted', '<=', curPage)
+ .limit(20)
+ .selectAll()
+ .execute() as unknown[] as JSONContentTable[];
+
+ let jams = await db
+ .selectFrom('jams')
+ .where('jams.author_id', '=', user.id)
+ .where('jams.created', '<=', curPage)
+ .limit(20)
+ .selectAll()
+ .execute() as unknown[] as JSONJamTable[];
+
+ let judgements = await db
+ .selectFrom('judgements')
+ .where('judgements.author_id', '=', user.id)
+ .where('judgements.published', '<=', curPage)
+ .limit(20)
+ .selectAll()
+ .execute() as unknown[] as JSONJudgementTable[];
+
+ return (
+
+ {`${user.username}@${user.instance}`}
+ {user.banned ? <>BANNED - > : <>>}{user.admin ? <>ADMIN - > : <>>}Joined {new Date(parseInt(user.joined)).toDateString()}
+ {user.url}
+ Jams
+ {jams.map(async (jam:JSONJamTable) => {
+ const started = Date.now() >= parseInt(jam.date_start);
+ const ended = Date.now() >= parseInt(jam.date_end);
+
+ let jamOwner = await db
+ .selectFrom('users')
+ .where('users.id', '=', jam.author_id)
+ .selectAll()
+ .executeTakeFirst();
+
+ if (jamOwner == null) return An error occured.
;
+
+ return (
+
{jam.name}
+
{started ? "Started" : "Starts"} {new Date(parseInt(jam.date_start)).toDateString()} - {ended ? "Ended" : "Ends"} {new Date(parseInt(jam.date_end)).toDateString()}
+
{jam.description}
+
);
+ })}
+ Submissions
+ {content.map(async (content:JSONContentTable) => {
+ let contentOwner = await db
+ .selectFrom('users')
+ .where('users.id', '=', content.author_id)
+ .selectAll()
+ .executeTakeFirst();
+
+ if (contentOwner == null) return An error occured.
;
+
+ // It's a JSONJamTable. I don't know why TS hates `number` => `string` conversion.
+ let submittedJam = await db
+ .selectFrom('jams')
+ .where('jams.id', '=', content.jam_id)
+ .selectAll()
+ .executeTakeFirst() as unknown as JSONJamTable;
+
+ if (submittedJam == null) return An error occured.
;
+
+ return (
+
{content.name}
+
Submitted {new Date(parseInt(content.submitted)).toDateString()} to {submittedJam.name}
+
{content.description}
+
);
+ })}
+ Judgements
+ {judgements.map(async (judgement:JSONJudgementTable) => {
+ let judgementOwner = await db
+ .selectFrom('users')
+ .where('users.id', '=', judgement.author_id)
+ .selectAll()
+ .executeTakeFirst();
+
+ // It's a JSONJamTable. I don't know why TS hates `number` => `string` conversion.
+ let submittedContent = await db
+ .selectFrom('content')
+ .where('content.id', '=', judgement.content_id)
+ .selectAll()
+ .executeTakeFirst() as unknown as JSONContentTable;
+
+ if (judgementOwner == null) return An error occured.
;
+
+ return (
+
+
Published {new Date(parseInt(judgement.published)).toDateString()} on {submittedContent.name}
+
{judgement.content}
+
);
+ })}
+ {/*
+
+ Later
+
+ Earlier
+
*/}
+
+ )
+}
\ No newline at end of file
diff --git a/src/layout/MainLayout/Sidebar/Main/Main.module.css b/src/layout/MainLayout/Sidebar/Main/Main.module.css
index b778100..c2d199c 100644
--- a/src/layout/MainLayout/Sidebar/Main/Main.module.css
+++ b/src/layout/MainLayout/Sidebar/Main/Main.module.css
@@ -21,15 +21,21 @@
background-color: var(--bg-2);
}
-/* .Main_List_Button {
- transition: scale 0.0625s;
+.Main_List_Button_Badge {
+ background-color: rgb(255, 64, 64);
+ color: white;
+ padding: 0 4px;
+ margin: 4px;
+ float: right;
+ border-radius: 4px;
+ pointer-events: none;
}
-.Main_List_Button:active {
- scale: 0.95;
+.Main_List_CurrentLink .Main_List_Button_Badge {
+ display: none;
}
-*/ .Main_List_Button_Slim {
+.Main_List_Button_Slim {
padding: 2px 8px !important;
background-color: var(--bg-3);
color: var(--fg-2);
diff --git a/src/layout/MainLayout/Sidebar/Main/Main.tsx b/src/layout/MainLayout/Sidebar/Main/Main.tsx
index a149b91..fbd9220 100644
--- a/src/layout/MainLayout/Sidebar/Main/Main.tsx
+++ b/src/layout/MainLayout/Sidebar/Main/Main.tsx
@@ -25,6 +25,19 @@ export function SidebarMain({currentPage}:{currentPage:string}) {
Root
+ currentPage === "/" ? impossiblePlay() : openPlay()}
+ // onMouseEnter={hoverPlay}
+ >
+
+ format_paint
+ Jams
+ NEW
+
+
({
+
+});
\ No newline at end of file
diff --git a/src/lib/mastoauth/mastoauth.ts b/src/lib/mastoauth/mastoauth.ts
new file mode 100644
index 0000000..1209c88
--- /dev/null
+++ b/src/lib/mastoauth/mastoauth.ts
@@ -0,0 +1,118 @@
+type MastoClient = {
+ id: string,
+ name: string,
+ website?: string,
+ redirect_uri: string,
+ client_id: string,
+ client_secret: string,
+ vapid_key: string,
+}
+
+type MastoToken = {
+ access_token: string,
+ token_type: string,
+ scope: string,
+ created_at: number
+}
+
+export class MastoAuth {
+ instance: string;
+
+ constructor(instance:string) {
+ this.instance = instance;
+ }
+
+ async newApplication() {
+ let formData = new FormData();
+ formData.append('client_name', 'abtmtr.link Jams');
+ // formData.append('redirect_uris', 'https://abtmtr.link/jams/oauth/code');
+ formData.append('redirect_uris', 'http://localhost:3000/jams/oauth/code');
+ formData.append('scopes', 'read');
+ formData.append('website', 'https://abtmtr.link/jams');
+
+ let appRequest;
+ try {
+ appRequest = await fetch(`https://${this.instance}/api/v1/apps`, {
+ body: formData,
+ method: "post"
+ });
+ } catch (err) {
+ console.log(err);
+ return null;
+ }
+
+ if (appRequest.ok) {
+ const reqEntities:MastoClient = await appRequest.json();
+ return reqEntities;
+ } else return null;
+ }
+
+ // async getApplicationUserCode(client: MastoClient) {
+ // let formData = new FormData();
+ // formData.append('response_type', 'code');
+ // formData.append('client_id', client.client_id);
+ // formData.append('redirect_uri', client.redirect_uri);
+ // formData.append('scope', 'read');
+ // formData.append('force_login', 'true');
+
+ // const appRequest = await fetch(`https://${this.instance}/oauth/authorize`, {
+ // body: formData
+ // });
+
+ // if (appRequest.ok) {
+ // const reqEntities:MastoToken = await appRequest.json();
+ // return reqEntities;
+ // } else return null;
+ // }
+
+ async getApplicationToken(client: {
+ client_id: string, client_secret: string,
+ redirect_uri: string
+ }, code:string) {
+ let formData = new FormData();
+ formData.append('client_id', client.client_id);
+ formData.append('client_secret', client.client_secret);
+ formData.append('redirect_uri', client.redirect_uri);
+ formData.append('grant_type', 'authorization_code');
+ formData.append('code', code);
+ formData.append('scope', 'read');
+
+ let appRequest;
+ try {
+ appRequest = await fetch(`https://${this.instance}/oauth/token`, {
+ body: formData,
+ method: "post"
+ });
+ } catch (err) {
+ return null;
+ }
+
+ if (appRequest.ok) {
+ const reqEntities:{
+ access_token: string,
+ token_type: string,
+ scope: string,
+ created_at: number
+ } = await appRequest.json();
+ return reqEntities;
+ } else return null;
+ }
+
+ async verifyUser(auth: string) {
+ let appRequest;
+ try {
+ appRequest = await fetch(`https://${this.instance}/api/v1/accounts/verify_credentials`, {
+ headers: {
+ "Authorization": auth
+ }
+ });
+ } catch (err) {
+ return null;
+ }
+
+ if (appRequest.ok) {
+ const reqEntities = await appRequest.json();
+ return reqEntities;
+ } else return null;
+ }
+}
\ No newline at end of file
diff --git a/src/lib/mastoauth/realtypes.ts b/src/lib/mastoauth/realtypes.ts
new file mode 100644
index 0000000..ae917ce
--- /dev/null
+++ b/src/lib/mastoauth/realtypes.ts
@@ -0,0 +1,46 @@
+
+
+export interface JSONUserTable {
+ id: string;
+ instance: string;
+ username: string;
+ admin: boolean;
+ url: string;
+ banned: boolean;
+ joined: string;
+}
+
+export interface JSONTokenTable {
+ id: string;
+ owner: string;
+ created: string;
+ expires: string;
+}
+
+export interface JSONJamTable {
+ id: string;
+ author_id: string;
+ name: string;
+ description: string;
+ date_start: string;
+ date_end: string;
+ created: string;
+}
+
+export interface JSONContentTable {
+ id: string;
+ author_id: string;
+ jam_id: string;
+ name: string;
+ description: string;
+ url: string;
+ submitted: string;
+}
+
+export interface JSONJudgementTable {
+ id: string;
+ author_id: string;
+ content_id: string;
+ content: string;
+ published: string;
+}
\ No newline at end of file