Skip to content

Commit

Permalink
Crews!
Browse files Browse the repository at this point in the history
  • Loading branch information
NotNite committed Nov 27, 2023
1 parent 4215ec4 commit 7a11798
Show file tree
Hide file tree
Showing 23 changed files with 984 additions and 45 deletions.
21 changes: 18 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"react-dom": "^18.2.0",
"react-feather": "^2.0.10",
"react-router-dom": "^6.18.0",
"react-router-typesafe": "^1.4.4",
"zustand": "^4.4.6"
},
"devDependencies": {
Expand Down
4 changes: 4 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ export default function Root() {
</ul>

<ul>
<li>
<Link to="/crews">Crews</Link>
</li>

<CurrentAccount />
</ul>
</nav>
Expand Down
124 changes: 124 additions & 0 deletions src/api/crew.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { useAuthStore } from "../stores";
import { CrewResponse, Result, SimpleCrewResponse } from "./types";
import { tryFetch } from "./util";

export async function getCrews() {
const token = useAuthStore.getState().key;
if (token == null) return null;

const req = await fetch(
`${import.meta.env.VITE_SLOP_CREW_SERVER}api/crew/crews`,
{
headers: {
Authorization: token
}
}
);

if (!req.ok) return null;
const res: SimpleCrewResponse[] = await req.json();
return res;
}

export async function getCrew(id: string) {
const token = useAuthStore.getState().key;
if (token == null) return null;

const req = await fetch(
`${import.meta.env.VITE_SLOP_CREW_SERVER}api/crew/${id}`,
{
headers: {
Authorization: token
}
}
);

if (!req.ok) return null;
const res: CrewResponse = await req.json();
return res;
}

export async function create(
name: string,
tag: string
): Promise<Result<SimpleCrewResponse, string>> {
const token = useAuthStore.getState().key;
if (token == null) {
return {
ok: false,
error: "You must be logged in to create a crew"
};
}

const req = await tryFetch(
`${import.meta.env.VITE_SLOP_CREW_SERVER}api/crew/create`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: token
},
body: JSON.stringify({
name,
tag
})
}
);

if (!req.ok) return req;
const res: SimpleCrewResponse = await req.value.json();
return { ok: true, value: res };
}

export async function promote(
crew: string,
user: string
): Promise<Result<null, string>> {
const token = useAuthStore.getState().key;
if (token == null) {
return {
ok: false,
error: "You must be logged in to promote a user"
};
}

const req = await tryFetch(
`${
import.meta.env.VITE_SLOP_CREW_SERVER
}api/crew/${crew}/promote?id=${user}`,
{
method: "POST",
headers: {
Authorization: token
}
}
);
if (!req.ok) return req;
return { ok: true, value: null };
}

export async function join(
code: string
): Promise<Result<SimpleCrewResponse, string>> {
const token = useAuthStore.getState().key;
if (token == null) {
return {
ok: false,
error: "You must be logged in to join a crew"
};
}

const req = await tryFetch(
`${import.meta.env.VITE_SLOP_CREW_SERVER}api/crew/join?code=${code}`,
{
method: "POST",
headers: {
Authorization: token
}
}
);

if (!req.ok) return req;
const res: SimpleCrewResponse = await req.value.json();
return { ok: true, value: res };
}
27 changes: 27 additions & 0 deletions src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,30 @@ export type MeResponse = {
export type AuthResponse = MeResponse & {
key: string;
};

export type SimpleCrewResponse = {
id: string;
name: string;
tag: string;
};

export type CrewResponse = SimpleCrewResponse & {
members: CrewMember[];
};

export type CrewMember = {
id: string;
username: string;
owner: boolean;
avatar: string | null;
};

export type Result<T, E> =
| {
ok: true;
value: T;
}
| {
ok: false;
error: E;
};
36 changes: 36 additions & 0 deletions src/api/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Result } from "./types";

export async function tryFetch(
input: string,
info: RequestInit
): Promise<Result<Response, string>> {
let req;
try {
req = await fetch(input, info);
} catch (e) {
console.error(e);

if (e instanceof Error) {
return { ok: false, error: e.message };
} else {
return { ok: false, error: "Unknown error" };
}
}

if (!req.ok) {
const text = await req.text();
if (text == "") {
return {
ok: false,
error: `Unknown error: ${req.status} ${req.statusText}`
};
} else {
return { ok: false, error: text };
}
}

return {
ok: true,
value: req
};
}
17 changes: 17 additions & 0 deletions src/components/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
type HasAvatar = {
id: string;
avatar: string | null;
username: string;
};

export default function Avatar({ person }: { person: HasAvatar }) {
if (person.avatar == null) return <></>;

return (
<img
className="avatar"
src={`https://cdn.discordapp.com/avatars/${person.id}/${person.avatar}.png`}
alt={person.username}
/>
);
}
9 changes: 2 additions & 7 deletions src/components/CurrentAccount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from "react";
import { useAuthStore } from "../stores";
import { getMe } from "../api/auth";
import { Link } from "react-router-dom";
import Avatar from "./Avatar";

export default function CurrentAccount() {
const token = useAuthStore((state) => state.key);
Expand All @@ -22,13 +23,7 @@ export default function CurrentAccount() {
<li>
<details role="list">
<summary aria-haspopup="listbox" role="link" className="currentAccount">
{cachedMe.avatar != null && (
<img
src={`https://cdn.discordapp.com/avatars/${cachedMe.id}/${cachedMe.avatar}.png`}
alt={cachedMe.username}
/>
)}

<Avatar person={cachedMe} />
{cachedMe.username}
</summary>

Expand Down
Loading

0 comments on commit 7a11798

Please sign in to comment.